ReferenceCustomGBIxn.cpp 21.9 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

/* Portions copyright (c) 2009 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>

28
#include "SimTKOpenMMUtilities.h"
29
30
31
32
33
34
35
36
#include "ReferenceForce.h"
#include "ReferenceCustomGBIxn.h"

using std::map;
using std::set;
using std::string;
using std::stringstream;
using std::vector;
37
using namespace OpenMM;
38
39
40
41
42
43
44

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

   ReferenceCustomGBIxn constructor

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

45
46
47
ReferenceCustomGBIxn::ReferenceCustomGBIxn(const vector<Lepton::CompiledExpression>& valueExpressions,
                     const vector<vector<Lepton::CompiledExpression> > valueDerivExpressions,
                     const vector<vector<Lepton::CompiledExpression> > valueGradientExpressions,
48
49
                     const vector<string>& valueNames,
                     const vector<OpenMM::CustomGBForce::ComputationType>& valueTypes,
50
51
52
                     const vector<Lepton::CompiledExpression>& energyExpressions,
                     const vector<vector<Lepton::CompiledExpression> > energyDerivExpressions,
                     const vector<vector<Lepton::CompiledExpression> > energyGradientExpressions,
53
54
                     const vector<OpenMM::CustomGBForce::ComputationType>& energyTypes,
                     const vector<string>& parameterNames) :
55
            cutoff(false), periodic(false), valueExpressions(valueExpressions), valueDerivExpressions(valueDerivExpressions), valueGradientExpressions(valueGradientExpressions),
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
            valueTypes(valueTypes), energyExpressions(energyExpressions), energyDerivExpressions(energyDerivExpressions), energyGradientExpressions(energyGradientExpressions),
            energyTypes(energyTypes) {

    for (int i = 0; i < this->valueExpressions.size(); i++)
        expressionSet.registerExpression(this->valueExpressions[i]);
    for (int i = 0; i < this->valueDerivExpressions.size(); i++)
        for (int j = 0; j < this->valueDerivExpressions[i].size(); j++)
            expressionSet.registerExpression(this->valueDerivExpressions[i][j]);
    for (int i = 0; i < this->valueGradientExpressions.size(); i++)
        for (int j = 0; j < this->valueGradientExpressions[i].size(); j++)
            expressionSet.registerExpression(this->valueGradientExpressions[i][j]);
    for (int i = 0; i < this->energyExpressions.size(); i++)
        expressionSet.registerExpression(this->energyExpressions[i]);
    for (int i = 0; i < this->energyDerivExpressions.size(); i++)
        for (int j = 0; j < this->energyDerivExpressions[i].size(); j++)
            expressionSet.registerExpression(this->energyDerivExpressions[i][j]);
    for (int i = 0; i < this->energyGradientExpressions.size(); i++)
        for (int j = 0; j < this->energyGradientExpressions[i].size(); j++)
            expressionSet.registerExpression(this->energyGradientExpressions[i][j]);
    rIndex = expressionSet.getVariableIndex("r");
    xIndex = expressionSet.getVariableIndex("x");
    yIndex = expressionSet.getVariableIndex("y");
    zIndex = expressionSet.getVariableIndex("z");
    for (int i = 0; i < (int) parameterNames.size(); i++) {
        paramIndex.push_back(expressionSet.getVariableIndex(parameterNames[i]));
81
82
        for (int j = 1; j < 3; j++) {
            stringstream name;
83
84
            name << parameterNames[i] << j;
            particleParamIndex.push_back(expressionSet.getVariableIndex(name.str()));
85
86
87
        }
    }
    for (int i = 0; i < (int) valueNames.size(); i++) {
88
        valueIndex.push_back(expressionSet.getVariableIndex(valueNames[i]));
89
90
91
        for (int j = 1; j < 3; j++) {
            stringstream name;
            name << valueNames[i] << j;
92
            particleValueIndex.push_back(expressionSet.getVariableIndex(name.str()));
93
94
95
96
97
98
99
100
101
102
        }
    }
}

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

   ReferenceCustomGBIxn destructor

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

103
ReferenceCustomGBIxn::~ReferenceCustomGBIxn() {
104
105
106
107
108
109
110
111
112
113
114
}

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

     Set the force to use a cutoff.

     @param distance            the cutoff distance
     @param neighbors           the neighbor list to use

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

115
  void ReferenceCustomGBIxn::setUseCutoff(RealOpenMM distance, const OpenMM::NeighborList& neighbors) {
116
117
118
119
120
121
122
123
124
125
126
127

    cutoff = true;
    cutoffDistance = distance;
    neighborList = &neighbors;
  }

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

     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.

128
     @param vectors    the vectors defining the periodic box
129
130
131

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

132
  void ReferenceCustomGBIxn::setPeriodic(RealVec* vectors) {
133

134
    if (cutoff) {
135
136
137
        assert(vectors[0][0] >= 2.0*cutoffDistance);
        assert(vectors[1][1] >= 2.0*cutoffDistance);
        assert(vectors[2][2] >= 2.0*cutoffDistance);
138
    }
139
    periodic = true;
140
141
142
    periodicBoxVectors[0] = vectors[0];
    periodicBoxVectors[1] = vectors[1];
    periodicBoxVectors[2] = vectors[2];
143
144
  }

145
146
void ReferenceCustomGBIxn::calculateIxn(int numberOfAtoms, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
                                           const vector<set<int> >& exclusions, map<string, double>& globalParameters, vector<RealVec>& forces,
147
148
149
150
                                           RealOpenMM* totalEnergy) {
    for (map<string, double>::const_iterator iter = globalParameters.begin(); iter != globalParameters.end(); ++iter)
        expressionSet.setVariable(expressionSet.getVariableIndex(iter->first), iter->second);

151
152
    // First calculate the computed values.

153
    int numValues = valueTypes.size();
154
    vector<vector<RealOpenMM> > values(numValues);
155
156
    for (int valueIndex = 0; valueIndex < numValues; valueIndex++) {
        if (valueTypes[valueIndex] == OpenMM::CustomGBForce::SingleParticle)
157
            calculateSingleParticleValue(valueIndex, numberOfAtoms, atomCoordinates, values, atomParameters);
158
        else if (valueTypes[valueIndex] == OpenMM::CustomGBForce::ParticlePair)
159
            calculateParticlePairValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters, values, exclusions, true);
160
        else
161
            calculateParticlePairValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters, values, exclusions, false);
162
    }
163
164
165
166

    // Now calculate the energy and its derivates.

    vector<vector<RealOpenMM> > dEdV(numValues, vector<RealOpenMM>(numberOfAtoms, (RealOpenMM) 0));
167
168
    for (int termIndex = 0; termIndex < (int) energyExpressions.size(); termIndex++) {
        if (energyTypes[termIndex] == OpenMM::CustomGBForce::SingleParticle)
169
            calculateSingleParticleEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, values, atomParameters, forces, totalEnergy, dEdV);
170
        else if (energyTypes[termIndex] == OpenMM::CustomGBForce::ParticlePair)
171
            calculateParticlePairEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, values, exclusions, true, forces, totalEnergy, dEdV);
172
        else
173
            calculateParticlePairEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, values, exclusions, false, forces, totalEnergy, dEdV);
174
    }
175
176
177

    // Apply the chain rule to evaluate forces.

178
    calculateChainRuleForces(numberOfAtoms, atomCoordinates, atomParameters, values, exclusions, forces, dEdV);
179
180
}

181
void ReferenceCustomGBIxn::calculateSingleParticleValue(int index, int numAtoms, vector<RealVec>& atomCoordinates, vector<vector<RealOpenMM> >& values,
182
        RealOpenMM** atomParameters) {
183
184
    values[index].resize(numAtoms);
    for (int i = 0; i < numAtoms; i++) {
185
186
187
188
189
        expressionSet.setVariable(xIndex, atomCoordinates[i][0]);
        expressionSet.setVariable(yIndex, atomCoordinates[i][1]);
        expressionSet.setVariable(zIndex, atomCoordinates[i][2]);
        for (int j = 0; j < (int) paramIndex.size(); j++)
            expressionSet.setVariable(paramIndex[j], atomParameters[i][j]);
190
        for (int j = 0; j < index; j++)
191
192
            expressionSet.setVariable(valueIndex[j], values[j][i]);
        values[index][i] = (RealOpenMM) valueExpressions[index].evaluate();
193
194
195
    }
}

196
void ReferenceCustomGBIxn::calculateParticlePairValue(int index, int numAtoms, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
197
        vector<vector<RealOpenMM> >& values, const vector<set<int> >& exclusions, bool useExclusions) {
198
199
    values[index].resize(numAtoms);
    for (int i = 0; i < numAtoms; i++)
200
        values[index][i] = (RealOpenMM) 0.0;
201
    if (cutoff) {
202
203
        // Loop over all pairs in the neighbor list.

204
205
206
207
        for (int i = 0; i < (int) neighborList->size(); i++) {
            OpenMM::AtomPair pair = (*neighborList)[i];
            if (useExclusions && exclusions[pair.first].find(pair.second) != exclusions[pair.first].end())
                continue;
208
209
            calculateOnePairValue(index, pair.first, pair.second, atomCoordinates, atomParameters, values);
            calculateOnePairValue(index, pair.second, pair.first, atomCoordinates, atomParameters, values);
210
211
212
        }
    }
    else {
213
214
        // Perform an O(N^2) loop over all atom pairs.

215
216
        for (int i = 0; i < numAtoms; i++) {
            for (int j = i+1; j < numAtoms; j++) {
217
218
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
219
220
                calculateOnePairValue(index, i, j, atomCoordinates, atomParameters, values);
                calculateOnePairValue(index, j, i, atomCoordinates, atomParameters, values);
221
222
223
224
225
           }
        }
    }
}

226
void ReferenceCustomGBIxn::calculateOnePairValue(int index, int atom1, int atom2, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
227
        vector<vector<RealOpenMM> >& values) {
228
229
    RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
    if (periodic)
230
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxVectors, deltaR);
231
232
233
234
235
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
    RealOpenMM r = deltaR[ReferenceForce::RIndex];
    if (cutoff && r >= cutoffDistance)
        return;
236
237
238
    for (int i = 0; i < (int) paramIndex.size(); i++) {
        expressionSet.setVariable(particleParamIndex[i*2], atomParameters[atom1][i]);
        expressionSet.setVariable(particleParamIndex[i*2+1], atomParameters[atom2][i]);
239
    }
240
    expressionSet.setVariable(rIndex, r);
241
    for (int i = 0; i < index; i++) {
242
243
        expressionSet.setVariable(particleValueIndex[i*2], values[i][atom1]);
        expressionSet.setVariable(particleValueIndex[i*2+1], values[i][atom2]);
244
    }
245
    values[index][atom1] += (RealOpenMM) valueExpressions[index].evaluate();
246
247
}

248
void ReferenceCustomGBIxn::calculateSingleParticleEnergyTerm(int index, int numAtoms, vector<RealVec>& atomCoordinates, const vector<vector<RealOpenMM> >& values,
249
250
        RealOpenMM** atomParameters, vector<RealVec>& forces, RealOpenMM* totalEnergy,
        vector<vector<RealOpenMM> >& dEdV) {
251
    for (int i = 0; i < numAtoms; i++) {
252
253
254
255
256
257
258
        expressionSet.setVariable(xIndex, atomCoordinates[i][0]);
        expressionSet.setVariable(yIndex, atomCoordinates[i][1]);
        expressionSet.setVariable(zIndex, atomCoordinates[i][2]);
        for (int j = 0; j < (int) paramIndex.size(); j++)
            expressionSet.setVariable(paramIndex[j], atomParameters[i][j]);
        for (int j = 0; j < valueIndex.size(); j++)
            expressionSet.setVariable(valueIndex[j], values[j][i]);
259
        if (totalEnergy != NULL)
260
261
262
263
264
265
            *totalEnergy += (RealOpenMM) energyExpressions[index].evaluate();
        for (int j = 0; j < (int) valueIndex.size(); j++)
            dEdV[j][i] += (RealOpenMM) energyDerivExpressions[index][j].evaluate();
        forces[i][0] -= (RealOpenMM) energyGradientExpressions[index][0].evaluate();
        forces[i][1] -= (RealOpenMM) energyGradientExpressions[index][1].evaluate();
        forces[i][2] -= (RealOpenMM) energyGradientExpressions[index][2].evaluate();
266
267
268
    }
}

269
void ReferenceCustomGBIxn::calculateParticlePairEnergyTerm(int index, int numAtoms, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
270
271
        const vector<vector<RealOpenMM> >& values, const vector<set<int> >& exclusions, bool useExclusions,
        vector<RealVec>& forces, RealOpenMM* totalEnergy, vector<vector<RealOpenMM> >& dEdV) {
272
    if (cutoff) {
273
274
        // Loop over all pairs in the neighbor list.

275
276
277
278
        for (int i = 0; i < (int) neighborList->size(); i++) {
            OpenMM::AtomPair pair = (*neighborList)[i];
            if (useExclusions && exclusions[pair.first].find(pair.second) != exclusions[pair.first].end())
                continue;
279
            calculateOnePairEnergyTerm(index, pair.first, pair.second, atomCoordinates, atomParameters, values, forces, totalEnergy, dEdV);
280
281
282
        }
    }
    else {
283
284
        // Perform an O(N^2) loop over all atom pairs.

285
286
        for (int i = 0; i < numAtoms; i++) {
            for (int j = i+1; j < numAtoms; j++) {
287
288
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
289
                calculateOnePairEnergyTerm(index, i, j, atomCoordinates, atomParameters, values, forces, totalEnergy, dEdV);
290
291
292
293
294
           }
        }
    }
}

295
void ReferenceCustomGBIxn::calculateOnePairEnergyTerm(int index, int atom1, int atom2, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
296
297
        const vector<vector<RealOpenMM> >& values, vector<RealVec>& forces, RealOpenMM* totalEnergy,
        vector<vector<RealOpenMM> >& dEdV) {
298
299
    // Compute the displacement.

300
301
    RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
    if (periodic)
302
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxVectors, deltaR);
303
304
305
306
307
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
    RealOpenMM r = deltaR[ReferenceForce::RIndex];
    if (cutoff && r >= cutoffDistance)
        return;
308
309
310

    // Record variables for evaluating expressions.

311
312
313
    for (int i = 0; i < (int) paramIndex.size(); i++) {
        expressionSet.setVariable(particleParamIndex[i*2], atomParameters[atom1][i]);
        expressionSet.setVariable(particleParamIndex[i*2+1], atomParameters[atom2][i]);
314
    }
315
316
317
318
    expressionSet.setVariable(rIndex, r);
    for (int i = 0; i < (int) valueIndex.size(); i++) {
        expressionSet.setVariable(particleValueIndex[i*2], values[i][atom1]);
        expressionSet.setVariable(particleValueIndex[i*2+1], values[i][atom2]);
319
    }
320
321
322

    // Evaluate the energy and its derivatives.

323
    if (totalEnergy != NULL)
324
325
        *totalEnergy += (RealOpenMM) energyExpressions[index].evaluate();
    RealOpenMM dEdR = (RealOpenMM) energyDerivExpressions[index][0].evaluate();
326
327
328
329
330
    dEdR *= 1/r;
    for (int i = 0; i < 3; i++) {
       forces[atom1][i] -= dEdR*deltaR[i];
       forces[atom2][i] += dEdR*deltaR[i];
    }
331
332
333
    for (int i = 0; i < (int) valueIndex.size(); i++) {
        dEdV[i][atom1] += (RealOpenMM) energyDerivExpressions[index][2*i+1].evaluate();
        dEdV[i][atom2] += (RealOpenMM) energyDerivExpressions[index][2*i+2].evaluate();
334
335
336
    }
}

337
void ReferenceCustomGBIxn::calculateChainRuleForces(int numAtoms, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
338
        const vector<vector<RealOpenMM> >& values, const vector<set<int> >& exclusions, vector<RealVec>& forces, vector<vector<RealOpenMM> >& dEdV) {
339
340
341
342
343
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

        for (int i = 0; i < (int) neighborList->size(); i++) {
            OpenMM::AtomPair pair = (*neighborList)[i];
344
            bool isExcluded = (exclusions[pair.first].find(pair.second) != exclusions[pair.first].end());
345
346
            calculateOnePairChainRule(pair.first, pair.second, atomCoordinates, atomParameters, values, forces, dEdV, isExcluded);
            calculateOnePairChainRule(pair.second, pair.first, atomCoordinates, atomParameters, values, forces, dEdV, isExcluded);
347
348
349
350
351
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

352
353
        for (int i = 0; i < numAtoms; i++) {
            for (int j = i+1; j < numAtoms; j++) {
354
                bool isExcluded = (exclusions[i].find(j) != exclusions[i].end());
355
356
                calculateOnePairChainRule(i, j, atomCoordinates, atomParameters, values, forces, dEdV, isExcluded);
                calculateOnePairChainRule(j, i, atomCoordinates, atomParameters, values, forces, dEdV, isExcluded);
357
358
359
           }
        }
    }
Peter Eastman's avatar
Peter Eastman committed
360
361
362
363

    // Compute chain rule terms for computed values that depend explicitly on particle coordinates.

    for (int i = 0; i < numAtoms; i++) {
364
365
366
        expressionSet.setVariable(xIndex, atomCoordinates[i][0]);
        expressionSet.setVariable(yIndex, atomCoordinates[i][1]);
        expressionSet.setVariable(zIndex, atomCoordinates[i][2]);
367
368
369
        vector<RealOpenMM> dVdX(valueDerivExpressions.size(), 0.0);
        vector<RealOpenMM> dVdY(valueDerivExpressions.size(), 0.0);
        vector<RealOpenMM> dVdZ(valueDerivExpressions.size(), 0.0);
370
371
372
373
        for (int j = 0; j < (int) paramIndex.size(); j++)
            expressionSet.setVariable(paramIndex[j], atomParameters[i][j]);
        for (int j = 1; j < (int) valueIndex.size(); j++) {
            expressionSet.setVariable(valueIndex[j-1], values[j-1][i]);
374
            for (int k = 1; k < j; k++) {
375
                RealOpenMM dVdV = (RealOpenMM) valueDerivExpressions[j][k].evaluate();
376
377
378
                dVdX[j] += dVdV*dVdX[k];
                dVdY[j] += dVdV*dVdY[k];
                dVdZ[j] += dVdV*dVdZ[k];
Peter Eastman's avatar
Peter Eastman committed
379
            }
380
381
382
            dVdX[j] += (RealOpenMM) valueGradientExpressions[j][0].evaluate();
            dVdY[j] += (RealOpenMM) valueGradientExpressions[j][1].evaluate();
            dVdZ[j] += (RealOpenMM) valueGradientExpressions[j][2].evaluate();
383
384
385
            forces[i][0] -= dEdV[j][i]*dVdX[j];
            forces[i][1] -= dEdV[j][i]*dVdY[j];
            forces[i][2] -= dEdV[j][i]*dVdZ[j];
Peter Eastman's avatar
Peter Eastman committed
386
387
        }
    }
388
389
}

390
void ReferenceCustomGBIxn::calculateOnePairChainRule(int atom1, int atom2, vector<RealVec>& atomCoordinates, RealOpenMM** atomParameters,
391
392
        const vector<vector<RealOpenMM> >& values, vector<RealVec>& forces,
        vector<vector<RealOpenMM> >& dEdV, bool isExcluded) {
393
394
395
396
    // Compute the displacement.

    RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
    if (periodic)
397
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxVectors, deltaR);
398
399
400
401
402
403
404
405
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
    RealOpenMM r = deltaR[ReferenceForce::RIndex];
    if (cutoff && r >= cutoffDistance)
        return;

    // Record variables for evaluating expressions.

406
407
408
    for (int i = 0; i < (int) paramIndex.size(); i++) {
        expressionSet.setVariable(particleParamIndex[i*2], atomParameters[atom1][i]);
        expressionSet.setVariable(particleParamIndex[i*2+1], atomParameters[atom2][i]);
409
    }
410
411
412
    expressionSet.setVariable(rIndex, r);
    expressionSet.setVariable(particleValueIndex[0], values[0][atom1]);
    expressionSet.setVariable(particleValueIndex[1], values[0][atom2]);
413
414
415

    // Evaluate the derivative of each parameter with respect to position and apply forces.

Peter Eastman's avatar
Peter Eastman committed
416
417
418
419
420
421
    RealOpenMM rinv = 1/r;
    deltaR[0] *= rinv;
    deltaR[1] *= rinv;
    deltaR[2] *= rinv;
    vector<RealOpenMM> dVdR1(valueDerivExpressions.size(), 0.0);
    vector<RealOpenMM> dVdR2(valueDerivExpressions.size(), 0.0);
422
    if (!isExcluded || valueTypes[0] != OpenMM::CustomGBForce::ParticlePair) {
423
        dVdR1[0] = (RealOpenMM) valueDerivExpressions[0][0].evaluate();
Peter Eastman's avatar
Peter Eastman committed
424
        dVdR2[0] = -dVdR1[0];
425
        for (int i = 0; i < 3; i++) {
Peter Eastman's avatar
Peter Eastman committed
426
427
            forces[atom1][i] -= dEdV[0][atom1]*dVdR1[0]*deltaR[i];
            forces[atom2][i] -= dEdV[0][atom1]*dVdR2[0]*deltaR[i];
428
        }
429
    }
430
431
432
433
434
435
436
437
    for (int i = 0; i < (int) paramIndex.size(); i++)
        expressionSet.setVariable(paramIndex[i], atomParameters[atom1][i]);
    expressionSet.setVariable(valueIndex[0], values[0][atom1]);
    for (int i = 1; i < (int) valueIndex.size(); i++) {
        expressionSet.setVariable(valueIndex[i], values[i][atom1]);
        expressionSet.setVariable(xIndex, atomCoordinates[atom1][0]);
        expressionSet.setVariable(yIndex, atomCoordinates[atom1][1]);
        expressionSet.setVariable(zIndex, atomCoordinates[atom1][2]);
438
        for (int j = 0; j < i; j++) {
439
            RealOpenMM dVdV = (RealOpenMM) valueDerivExpressions[i][j].evaluate();
Peter Eastman's avatar
Peter Eastman committed
440
441
            dVdR1[i] += dVdV*dVdR1[j];
            dVdR2[i] += dVdV*dVdR2[j];
442
443
        }
        for (int k = 0; k < 3; k++) {
Peter Eastman's avatar
Peter Eastman committed
444
445
            forces[atom1][k] -= dEdV[i][atom1]*dVdR1[i]*deltaR[k];
            forces[atom2][k] -= dEdV[i][atom1]*dVdR2[i]*deltaR[k];
446
        }
447
448
    }
}