ReferenceCustomHbondIxn.cpp 13.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/* Portions copyright (c) 2009-2010 Stanford University and Simbios.
 * Contributors: Peter Eastman
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <string.h>
#include <sstream>
#include <utility>

29
#include "SimTKOpenMMUtilities.h"
30
31
32
33
34
#include "ReferenceForce.h"
#include "ReferenceCustomHbondIxn.h"

using std::map;
using std::pair;
35
using std::set;
36
37
38
using std::string;
using std::stringstream;
using std::vector;
39
using namespace OpenMM;
40
41
42
43
44
45
46

/**---------------------------------------------------------------------------------------

   ReferenceCustomHbondIxn constructor

   --------------------------------------------------------------------------------------- */

47
48
49
50
51
52
53
54
55
56
57
ReferenceCustomHbondIxn::ReferenceCustomHbondIxn(const vector<vector<int> >& donorAtoms, const vector<vector<int> >& acceptorAtoms,
            const Lepton::ParsedExpression& energyExpression, const vector<string>& donorParameterNames, const vector<string>& acceptorParameterNames,
            const map<string, vector<int> >& distances, const map<string, vector<int> >& angles, const map<string, vector<int> >& dihedrals) :
            cutoff(false), periodic(false), donorAtoms(donorAtoms), acceptorAtoms(acceptorAtoms), energyExpression(energyExpression.createProgram()),
            donorParamNames(donorParameterNames), acceptorParamNames(acceptorParameterNames) {
    for (map<string, vector<int> >::const_iterator iter = distances.begin(); iter != distances.end(); ++iter)
        distanceTerms.push_back(ReferenceCustomHbondIxn::DistanceTermInfo(iter->first, iter->second, energyExpression.differentiate(iter->first).optimize().createProgram()));
    for (map<string, vector<int> >::const_iterator iter = angles.begin(); iter != angles.end(); ++iter)
        angleTerms.push_back(ReferenceCustomHbondIxn::AngleTermInfo(iter->first, iter->second, energyExpression.differentiate(iter->first).optimize().createProgram()));
    for (map<string, vector<int> >::const_iterator iter = dihedrals.begin(); iter != dihedrals.end(); ++iter)
        dihedralTerms.push_back(ReferenceCustomHbondIxn::DihedralTermInfo(iter->first, iter->second, energyExpression.differentiate(iter->first).optimize().createProgram()));
58
59
60
61
62
63
64
65
}

/**---------------------------------------------------------------------------------------

   ReferenceCustomHbondIxn destructor

   --------------------------------------------------------------------------------------- */

66
ReferenceCustomHbondIxn::~ReferenceCustomHbondIxn() {
67
68
69
70
71
72
73
74
75
76
}

  /**---------------------------------------------------------------------------------------

     Set the force to use a cutoff.

     @param distance            the cutoff distance

     --------------------------------------------------------------------------------------- */

peastman's avatar
peastman committed
77
void ReferenceCustomHbondIxn::setUseCutoff(double distance) {
78
79
80
81
82
83
84
85
86
87
    cutoff = true;
    cutoffDistance = distance;
}

  /**---------------------------------------------------------------------------------------

     Set the force to use periodic boundary conditions.  This requires that a cutoff has
     also been set, and the smallest side of the periodic box is at least twice the cutoff
     distance.

88
     @param vectors    the vectors defining the periodic box
89
90
91

     --------------------------------------------------------------------------------------- */

peastman's avatar
peastman committed
92
void ReferenceCustomHbondIxn::setPeriodic(Vec3* vectors) {
93
    assert(cutoff);
94
95
96
    assert(vectors[0][0] >= 2.0*cutoffDistance);
    assert(vectors[1][1] >= 2.0*cutoffDistance);
    assert(vectors[2][2] >= 2.0*cutoffDistance);
97
    periodic = true;
98
99
100
    periodicBoxVectors[0] = vectors[0];
    periodicBoxVectors[1] = vectors[1];
    periodicBoxVectors[2] = vectors[2];
101
102
103
104
105
106
107
108
109
110
  }


/**---------------------------------------------------------------------------------------

   Calculate custom hbond interaction

   @param atomCoordinates    atom coordinates
   @param donorParameters    donor parameters values       donorParameters[donorIndex][parameterIndex]
   @param acceptorParameters acceptor parameters values    acceptorParameters[acceptorIndex][parameterIndex]
111
112
   @param exclusions         exclusion indices
                             exclusions[donorIndex] contains the list of excluded acceptors for that donor
113
114
115
116
117
118
   @param globalParameters   the values of global parameters
   @param forces             force array (forces added)
   @param totalEnergy        total energy

   --------------------------------------------------------------------------------------- */

peastman's avatar
peastman committed
119
120
121
void ReferenceCustomHbondIxn::calculatePairIxn(vector<Vec3>& atomCoordinates, double** donorParameters, double** acceptorParameters,
                                             vector<set<int> >& exclusions, const map<string, double>& globalParameters, vector<Vec3>& forces,
                                             double* totalEnergy) const {
122
123
124
125
126
127
128
129

   map<string, double> variables = globalParameters;

   // allocate and initialize exclusion array

   int numDonors = donorAtoms.size();
   int numAcceptors = acceptorAtoms.size();

130
   for (int donor = 0; donor < numDonors; donor++) {
131
132
133
134
135
136
137
      // Initialize per-donor parameters.

      for (int j = 0; j < (int) donorParamNames.size(); j++)
          variables[donorParamNames[j]] = donorParameters[donor][j];

      // loop over atom pairs

138
      for (int acceptor = 0; acceptor < numAcceptors; acceptor++) {
139
         if (exclusions[donor].find(acceptor) == exclusions[donor].end()) {
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
             for (int j = 0; j < (int) acceptorParamNames.size(); j++)
                 variables[acceptorParamNames[j]] = acceptorParameters[acceptor][j];
             calculateOneIxn(donor, acceptor, atomCoordinates, variables, forces, totalEnergy);
         }
      }
   }
}

  /**---------------------------------------------------------------------------------------

     Calculate custom interaction between a donor and an acceptor

     @param donor            the index of the donor
     @param acceptor         the index of the acceptor
     @param atomCoordinates  atom coordinates
     @param variables        the values of variables that may appear in expressions
     @param forces           force array (forces added)
     @param energyByAtom     atom energy
     @param totalEnergy      total energy

     --------------------------------------------------------------------------------------- */

peastman's avatar
peastman committed
162
163
void ReferenceCustomHbondIxn::calculateOneIxn(int donor, int acceptor, vector<Vec3>& atomCoordinates,
                        map<string, double>& variables, vector<Vec3>& forces, double* totalEnergy) const {
164

165
166
167
168
169
170
171
    int atoms[6];
    atoms[0] = acceptorAtoms[acceptor][0];
    atoms[1] = acceptorAtoms[acceptor][1];
    atoms[2] = acceptorAtoms[acceptor][2];
    atoms[3] = donorAtoms[donor][0];
    atoms[4] = donorAtoms[donor][1];
    atoms[5] = donorAtoms[donor][2];
172
173
174

    // Compute the distance between the primary donor and acceptor atoms, and compare to the cutoff.

175
    if (cutoff) {
peastman's avatar
peastman committed
176
        double delta[ReferenceForce::LastDeltaRIndex];
177
178
179
180
        computeDelta(atoms[0], atoms[3], delta, atomCoordinates);
        if (delta[ReferenceForce::RIndex] >= cutoffDistance)
            return;
    }
181
182

    // Compute all of the variables the energy can depend on.
183
184
185
186
187

    for (int i = 0; i < (int) distanceTerms.size(); i++) {
        const DistanceTermInfo& term = distanceTerms[i];
        computeDelta(atoms[term.p1], atoms[term.p2], term.delta, atomCoordinates);
        variables[term.name] = term.delta[ReferenceForce::RIndex];
188
    }
189
190
191
192
193
194
195
196
197
198
199
    for (int i = 0; i < (int) angleTerms.size(); i++) {
        const AngleTermInfo& term = angleTerms[i];
        computeDelta(atoms[term.p1], atoms[term.p2], term.delta1, atomCoordinates);
        computeDelta(atoms[term.p3], atoms[term.p2], term.delta2, atomCoordinates);
        variables[term.name] = computeAngle(term.delta1, term.delta2);
    }
    for (int i = 0; i < (int) dihedralTerms.size(); i++) {
        const DihedralTermInfo& term = dihedralTerms[i];
        computeDelta(atoms[term.p2], atoms[term.p1], term.delta1, atomCoordinates);
        computeDelta(atoms[term.p2], atoms[term.p3], term.delta2, atomCoordinates);
        computeDelta(atoms[term.p4], atoms[term.p3], term.delta3, atomCoordinates);
peastman's avatar
peastman committed
200
201
        double dotDihedral, signOfDihedral;
        double* crossProduct[] = {term.cross1, term.cross2};
202
        variables[term.name] = getDihedralAngleBetweenThreeVectors(term.delta1, term.delta2, term.delta3, crossProduct, &dotDihedral, term.delta1, &signOfDihedral, 1);
203
204
    }

205
    // Apply forces based on distances.
206

207
208
    for (int i = 0; i < (int) distanceTerms.size(); i++) {
        const DistanceTermInfo& term = distanceTerms[i];
peastman's avatar
peastman committed
209
        double dEdR = term.forceExpression.evaluate(variables)/(term.delta[ReferenceForce::RIndex]);
210
        for (int i = 0; i < 3; i++) {
peastman's avatar
peastman committed
211
           double force  = -dEdR*term.delta[i];
212
213
           forces[atoms[term.p1]][i] -= force;
           forces[atoms[term.p2]][i] += force;
214
215
216
        }
    }

217
    // Apply forces based on angles.
218

219
220
    for (int i = 0; i < (int) angleTerms.size(); i++) {
        const AngleTermInfo& term = angleTerms[i];
peastman's avatar
peastman committed
221
222
        double dEdTheta = term.forceExpression.evaluate(variables);
        double thetaCross[ReferenceForce::LastDeltaRIndex];
223
        SimTKOpenMMUtilities::crossProductVector3(term.delta1, term.delta2, thetaCross);
peastman's avatar
peastman committed
224
        double lengthThetaCross = sqrt(DOT3(thetaCross, thetaCross));
225
        if (lengthThetaCross < 1.0e-06)
peastman's avatar
peastman committed
226
227
228
229
            lengthThetaCross = 1.0e-06;
        double termA = dEdTheta/(term.delta1[ReferenceForce::R2Index]*lengthThetaCross);
        double termC = -dEdTheta/(term.delta2[ReferenceForce::R2Index]*lengthThetaCross);
        double deltaCrossP[3][3];
230
231
        SimTKOpenMMUtilities::crossProductVector3(term.delta1, thetaCross, deltaCrossP[0]);
        SimTKOpenMMUtilities::crossProductVector3(term.delta2, thetaCross, deltaCrossP[2]);
232
233
234
235
236
237
        for (int i = 0; i < 3; i++) {
            deltaCrossP[0][i] *= termA;
            deltaCrossP[2][i] *= termC;
            deltaCrossP[1][i] = -(deltaCrossP[0][i]+deltaCrossP[2][i]);
        }
        for (int i = 0; i < 3; i++) {
238
239
240
            forces[atoms[term.p1]][i] += deltaCrossP[0][i];
            forces[atoms[term.p2]][i] += deltaCrossP[1][i];
            forces[atoms[term.p3]][i] += deltaCrossP[2][i];
241
242
243
        }
    }

244
    // Apply forces based on dihedrals.
245

246
247
    for (int i = 0; i < (int) dihedralTerms.size(); i++) {
        const DihedralTermInfo& term = dihedralTerms[i];
peastman's avatar
peastman committed
248
249
250
251
252
        double dEdTheta = term.forceExpression.evaluate(variables);
        double internalF[4][3];
        double forceFactors[4];
        double normCross1 = DOT3(term.cross1, term.cross1);
        double normBC = term.delta2[ReferenceForce::RIndex];
253
        forceFactors[0] = (-dEdTheta*normBC)/normCross1;
peastman's avatar
peastman committed
254
255
256
257
258
259
        double normCross2 = DOT3(term.cross2, term.cross2);
        forceFactors[3] = (dEdTheta*normBC)/normCross2;
        forceFactors[1] = DOT3(term.delta1, term.delta2);
        forceFactors[1] /= term.delta2[ReferenceForce::R2Index];
        forceFactors[2] = DOT3(term.delta3, term.delta2);
        forceFactors[2] /= term.delta2[ReferenceForce::R2Index];
260
        for (int i = 0; i < 3; i++) {
261
262
            internalF[0][i] = forceFactors[0]*term.cross1[i];
            internalF[3][i] = forceFactors[3]*term.cross2[i];
peastman's avatar
peastman committed
263
            double s = forceFactors[1]*internalF[0][i] - forceFactors[2]*internalF[3][i];
264
265
266
267
            internalF[1][i] = internalF[0][i] - s;
            internalF[2][i] = internalF[3][i] + s;
        }
        for (int i = 0; i < 3; i++) {
268
269
270
271
            forces[atoms[term.p1]][i] += internalF[0][i];
            forces[atoms[term.p2]][i] -= internalF[1][i];
            forces[atoms[term.p3]][i] -= internalF[2][i];
            forces[atoms[term.p4]][i] += internalF[3][i];
272
273
274
275
276
277
        }
    }

    // Add the energy

    if (totalEnergy)
peastman's avatar
peastman committed
278
        *totalEnergy += energyExpression.evaluate(variables);
279
280
}

peastman's avatar
peastman committed
281
void ReferenceCustomHbondIxn::computeDelta(int atom1, int atom2, double* delta, vector<Vec3>& atomCoordinates) const {
282
    if (periodic)
283
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom1], atomCoordinates[atom2], periodicBoxVectors, delta);
284
285
286
287
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom1], atomCoordinates[atom2], delta);
}

peastman's avatar
peastman committed
288
289
290
291
double ReferenceCustomHbondIxn::computeAngle(double* vec1, double* vec2) {
    double dot = DOT3(vec1, vec2);
    double cosine = dot/sqrt((vec1[ReferenceForce::R2Index]*vec2[ReferenceForce::R2Index]));
    double angle;
292
293
294
295
296
    if (cosine >= 1)
        angle = 0;
    else if (cosine <= -1)
        angle = PI_M;
    else
peastman's avatar
peastman committed
297
        angle = acos(cosine);
298
299
    return angle;
}