ReferenceCustomGBIxn.cpp 23.1 KB
Newer Older
1

2
/* Portions copyright (c) 2009-2016 Stanford University and Simbios.
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
 * 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
                     const vector<vector<Lepton::CompiledExpression> > valueParamDerivExpressions,
49
50
                     const vector<string>& valueNames,
                     const vector<OpenMM::CustomGBForce::ComputationType>& valueTypes,
51
52
53
                     const vector<Lepton::CompiledExpression>& energyExpressions,
                     const vector<vector<Lepton::CompiledExpression> > energyDerivExpressions,
                     const vector<vector<Lepton::CompiledExpression> > energyGradientExpressions,
54
                     const vector<vector<Lepton::CompiledExpression> > energyParamDerivExpressions,
55
56
                     const vector<OpenMM::CustomGBForce::ComputationType>& energyTypes,
                     const vector<string>& parameterNames) :
57
58
            cutoff(false), periodic(false), valueExpressions(valueExpressions), valueDerivExpressions(valueDerivExpressions), valueGradientExpressions(valueGradientExpressions), valueParamDerivExpressions(valueParamDerivExpressions),
            valueTypes(valueTypes), energyExpressions(energyExpressions), energyDerivExpressions(energyDerivExpressions), energyGradientExpressions(energyGradientExpressions), energyParamDerivExpressions(energyParamDerivExpressions),
59
60
61
62
63
64
65
66
67
68
            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]);
69
70
71
    for (int i = 0; i < this->valueParamDerivExpressions.size(); i++)
        for (int j = 0; j < this->valueParamDerivExpressions[i].size(); j++)
            expressionSet.registerExpression(this->valueParamDerivExpressions[i][j]);
72
73
74
75
76
77
78
79
    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]);
80
81
82
    for (int i = 0; i < this->energyParamDerivExpressions.size(); i++)
        for (int j = 0; j < this->energyParamDerivExpressions[i].size(); j++)
            expressionSet.registerExpression(this->energyParamDerivExpressions[i][j]);
83
84
85
86
    rIndex = expressionSet.getVariableIndex("r");
    xIndex = expressionSet.getVariableIndex("x");
    yIndex = expressionSet.getVariableIndex("y");
    zIndex = expressionSet.getVariableIndex("z");
peastman's avatar
peastman committed
87
88
    for (auto& param : parameterNames) {
        paramIndex.push_back(expressionSet.getVariableIndex(param));
89
90
        for (int j = 1; j < 3; j++) {
            stringstream name;
peastman's avatar
peastman committed
91
            name << param << j;
92
            particleParamIndex.push_back(expressionSet.getVariableIndex(name.str()));
93
94
        }
    }
peastman's avatar
peastman committed
95
96
    for (auto& value : valueNames) {
        valueIndex.push_back(expressionSet.getVariableIndex(value));
97
98
        for (int j = 1; j < 3; j++) {
            stringstream name;
peastman's avatar
peastman committed
99
            name << value << j;
100
            particleValueIndex.push_back(expressionSet.getVariableIndex(name.str()));
101
102
103
104
105
106
107
108
109
110
        }
    }
}

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

   ReferenceCustomGBIxn destructor

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

111
ReferenceCustomGBIxn::~ReferenceCustomGBIxn() {
112
113
114
115
116
117
118
119
120
121
122
}

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

     Set the force to use a cutoff.

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

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

peastman's avatar
peastman committed
123
  void ReferenceCustomGBIxn::setUseCutoff(double distance, const OpenMM::NeighborList& neighbors) {
124
125
126
127
128
129
130
131
132
133
134
135

    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.

136
     @param vectors    the vectors defining the periodic box
137
138
139

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

peastman's avatar
peastman committed
140
  void ReferenceCustomGBIxn::setPeriodic(Vec3* vectors) {
141

142
    if (cutoff) {
143
144
145
        assert(vectors[0][0] >= 2.0*cutoffDistance);
        assert(vectors[1][1] >= 2.0*cutoffDistance);
        assert(vectors[2][2] >= 2.0*cutoffDistance);
146
    }
147
    periodic = true;
148
149
150
    periodicBoxVectors[0] = vectors[0];
    periodicBoxVectors[1] = vectors[1];
    periodicBoxVectors[2] = vectors[2];
151
152
  }

peastman's avatar
peastman committed
153
154
155
void ReferenceCustomGBIxn::calculateIxn(int numberOfAtoms, vector<Vec3>& atomCoordinates, double** atomParameters,
                                           const vector<set<int> >& exclusions, map<string, double>& globalParameters, vector<Vec3>& forces,
                                           double* totalEnergy, double* energyParamDerivs) {
peastman's avatar
peastman committed
156
157
    for (auto& param : globalParameters)
        expressionSet.setVariable(expressionSet.getVariableIndex(param.first), param.second);
158
159
160
161
162
163
    
    // Initialize arrays for storing values.
    
    int numValues = valueTypes.size();
    int numDerivs = valueParamDerivExpressions[0].size();
    values.resize(numValues);
peastman's avatar
peastman committed
164
    dEdV.resize(numValues, vector<double>(numberOfAtoms, 0.0));
165
166
    dValuedParam.resize(numValues);
    for (int i = 0; i < numValues; i++)
peastman's avatar
peastman committed
167
        dValuedParam[i].resize(numDerivs, vector<double>(numberOfAtoms, 0.0));
168

169
170
    // First calculate the computed values.

171
172
    for (int valueIndex = 0; valueIndex < numValues; valueIndex++) {
        if (valueTypes[valueIndex] == OpenMM::CustomGBForce::SingleParticle)
173
            calculateSingleParticleValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters);
174
        else if (valueTypes[valueIndex] == OpenMM::CustomGBForce::ParticlePair)
175
            calculateParticlePairValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters, exclusions, true);
176
        else
177
            calculateParticlePairValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters, exclusions, false);
178
    }
179
180
181

    // Now calculate the energy and its derivates.

182
183
    for (int termIndex = 0; termIndex < (int) energyExpressions.size(); termIndex++) {
        if (energyTypes[termIndex] == OpenMM::CustomGBForce::SingleParticle)
184
            calculateSingleParticleEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, forces, totalEnergy, energyParamDerivs);
185
        else if (energyTypes[termIndex] == OpenMM::CustomGBForce::ParticlePair)
186
            calculateParticlePairEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, exclusions, true, forces, totalEnergy, energyParamDerivs);
187
        else
188
            calculateParticlePairEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, exclusions, false, forces, totalEnergy, energyParamDerivs);
189
    }
190
191
192

    // Apply the chain rule to evaluate forces.

193
    calculateChainRuleForces(numberOfAtoms, atomCoordinates, atomParameters, exclusions, forces, energyParamDerivs);
194
195
}

peastman's avatar
peastman committed
196
void ReferenceCustomGBIxn::calculateSingleParticleValue(int index, int numAtoms, vector<Vec3>& atomCoordinates, double** atomParameters) {
197
198
    values[index].resize(numAtoms);
    for (int i = 0; i < numAtoms; i++) {
199
200
201
202
203
        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]);
204
        for (int j = 0; j < index; j++)
205
            expressionSet.setVariable(valueIndex[j], values[j][i]);
peastman's avatar
peastman committed
206
        values[index][i] = valueExpressions[index].evaluate();
207
208
209

        // Calculate derivatives with respect to parameters.

210
211
        for (int j = 0; j < valueParamDerivExpressions[index].size(); j++)
            dValuedParam[index][j][i] += valueParamDerivExpressions[index][j].evaluate();
212
        for (int j = 0; j < index; j++) {
peastman's avatar
peastman committed
213
            double dVdV = valueDerivExpressions[index][j].evaluate();
214
215
216
            for (int k = 0; k < valueParamDerivExpressions[index].size(); k++)
                dValuedParam[index][k][i] += dVdV*dValuedParam[j][k][i];
        }
217
218
219
    }
}

peastman's avatar
peastman committed
220
void ReferenceCustomGBIxn::calculateParticlePairValue(int index, int numAtoms, vector<Vec3>& atomCoordinates, double** atomParameters,
221
        const vector<set<int> >& exclusions, bool useExclusions) {
222
223
    values[index].resize(numAtoms);
    for (int i = 0; i < numAtoms; i++)
peastman's avatar
peastman committed
224
        values[index][i] = 0.0;
225
    if (cutoff) {
226
227
        // Loop over all pairs in the neighbor list.

peastman's avatar
peastman committed
228
        for (auto& pair : *neighborList) {
229
230
            if (useExclusions && exclusions[pair.first].find(pair.second) != exclusions[pair.first].end())
                continue;
231
232
            calculateOnePairValue(index, pair.first, pair.second, atomCoordinates, atomParameters);
            calculateOnePairValue(index, pair.second, pair.first, atomCoordinates, atomParameters);
233
234
235
        }
    }
    else {
236
237
        // Perform an O(N^2) loop over all atom pairs.

238
239
        for (int i = 0; i < numAtoms; i++) {
            for (int j = i+1; j < numAtoms; j++) {
240
241
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
242
243
                calculateOnePairValue(index, i, j, atomCoordinates, atomParameters);
                calculateOnePairValue(index, j, i, atomCoordinates, atomParameters);
244
245
246
247
248
           }
        }
    }
}

peastman's avatar
peastman committed
249
250
void ReferenceCustomGBIxn::calculateOnePairValue(int index, int atom1, int atom2, vector<Vec3>& atomCoordinates, double** atomParameters) {
    double deltaR[ReferenceForce::LastDeltaRIndex];
251
    if (periodic)
252
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxVectors, deltaR);
253
254
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
peastman's avatar
peastman committed
255
    double r = deltaR[ReferenceForce::RIndex];
256
257
    if (cutoff && r >= cutoffDistance)
        return;
258
259
260
    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]);
261
    }
262
    expressionSet.setVariable(rIndex, r);
263
    for (int i = 0; i < index; i++) {
264
265
        expressionSet.setVariable(particleValueIndex[i*2], values[i][atom1]);
        expressionSet.setVariable(particleValueIndex[i*2+1], values[i][atom2]);
266
    }
peastman's avatar
peastman committed
267
    values[index][atom1] += valueExpressions[index].evaluate();
268
269
270
271
272
    
    // Calculate derivatives with respect to parameters.
    
    for (int i = 0; i < valueParamDerivExpressions[index].size(); i++)
        dValuedParam[index][i][atom1] += valueParamDerivExpressions[index][i].evaluate();
273
274
}

peastman's avatar
peastman committed
275
276
void ReferenceCustomGBIxn::calculateSingleParticleEnergyTerm(int index, int numAtoms, vector<Vec3>& atomCoordinates,
        double** atomParameters, vector<Vec3>& forces, double* totalEnergy, double* energyParamDerivs) {
277
    for (int i = 0; i < numAtoms; i++) {
278
279
280
281
282
283
284
        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]);
285
286
287
        
        // Compute energy and force.
        
288
        if (totalEnergy != NULL)
peastman's avatar
peastman committed
289
            *totalEnergy += energyExpressions[index].evaluate();
290
        for (int j = 0; j < (int) valueIndex.size(); j++)
peastman's avatar
peastman committed
291
292
293
294
            dEdV[j][i] += energyDerivExpressions[index][j].evaluate();
        forces[i][0] -= energyGradientExpressions[index][0].evaluate();
        forces[i][1] -= energyGradientExpressions[index][1].evaluate();
        forces[i][2] -= energyGradientExpressions[index][2].evaluate();
295
296
297
        
        // Compute derivatives with respect to parameters.
        
298
        for (int k = 0; k < energyParamDerivExpressions[index].size(); k++)
299
            energyParamDerivs[k] += energyParamDerivExpressions[index][k].evaluate();
300
301
302
    }
}

peastman's avatar
peastman committed
303
304
void ReferenceCustomGBIxn::calculateParticlePairEnergyTerm(int index, int numAtoms, vector<Vec3>& atomCoordinates, double** atomParameters,
        const vector<set<int> >& exclusions, bool useExclusions, vector<Vec3>& forces, double* totalEnergy, double* energyParamDerivs) {
305
    if (cutoff) {
306
307
        // Loop over all pairs in the neighbor list.

peastman's avatar
peastman committed
308
        for (auto& pair : *neighborList) {
309
310
            if (useExclusions && exclusions[pair.first].find(pair.second) != exclusions[pair.first].end())
                continue;
311
            calculateOnePairEnergyTerm(index, pair.first, pair.second, atomCoordinates, atomParameters, forces, totalEnergy, energyParamDerivs);
312
313
314
        }
    }
    else {
315
316
        // Perform an O(N^2) loop over all atom pairs.

317
318
        for (int i = 0; i < numAtoms; i++) {
            for (int j = i+1; j < numAtoms; j++) {
319
320
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
321
                calculateOnePairEnergyTerm(index, i, j, atomCoordinates, atomParameters, forces, totalEnergy, energyParamDerivs);
322
323
324
325
326
           }
        }
    }
}

peastman's avatar
peastman committed
327
328
void ReferenceCustomGBIxn::calculateOnePairEnergyTerm(int index, int atom1, int atom2, vector<Vec3>& atomCoordinates, double** atomParameters,
        vector<Vec3>& forces, double* totalEnergy, double* energyParamDerivs) {
329
330
    // Compute the displacement.

peastman's avatar
peastman committed
331
    double deltaR[ReferenceForce::LastDeltaRIndex];
332
    if (periodic)
333
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxVectors, deltaR);
334
335
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
peastman's avatar
peastman committed
336
    double r = deltaR[ReferenceForce::RIndex];
337
338
    if (cutoff && r >= cutoffDistance)
        return;
339
340
341

    // Record variables for evaluating expressions.

342
343
344
    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]);
345
    }
346
347
348
349
    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]);
350
    }
351
352
353

    // Evaluate the energy and its derivatives.

354
    if (totalEnergy != NULL)
peastman's avatar
peastman committed
355
356
        *totalEnergy += energyExpressions[index].evaluate();
    double dEdR = energyDerivExpressions[index][0].evaluate();
357
358
359
360
361
    dEdR *= 1/r;
    for (int i = 0; i < 3; i++) {
       forces[atom1][i] -= dEdR*deltaR[i];
       forces[atom2][i] += dEdR*deltaR[i];
    }
362
    for (int i = 0; i < (int) valueIndex.size(); i++) {
peastman's avatar
peastman committed
363
364
        dEdV[i][atom1] += energyDerivExpressions[index][2*i+1].evaluate();
        dEdV[i][atom2] += energyDerivExpressions[index][2*i+2].evaluate();
365
    }
366
367
368
369
370
        
    // Compute derivatives with respect to parameters.

    for (int i = 0; i < energyParamDerivExpressions[index].size(); i++)
        energyParamDerivs[i] += energyParamDerivExpressions[index][i].evaluate();
371
372
}

peastman's avatar
peastman committed
373
374
void ReferenceCustomGBIxn::calculateChainRuleForces(int numAtoms, vector<Vec3>& atomCoordinates, double** atomParameters,
        const vector<set<int> >& exclusions, vector<Vec3>& forces, double* energyParamDerivs) {
375
376
377
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

peastman's avatar
peastman committed
378
        for (auto& pair : *neighborList) {
379
            bool isExcluded = (exclusions[pair.first].find(pair.second) != exclusions[pair.first].end());
380
381
            calculateOnePairChainRule(pair.first, pair.second, atomCoordinates, atomParameters, forces, isExcluded);
            calculateOnePairChainRule(pair.second, pair.first, atomCoordinates, atomParameters, forces, isExcluded);
382
383
384
385
386
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

387
388
        for (int i = 0; i < numAtoms; i++) {
            for (int j = i+1; j < numAtoms; j++) {
389
                bool isExcluded = (exclusions[i].find(j) != exclusions[i].end());
390
391
                calculateOnePairChainRule(i, j, atomCoordinates, atomParameters, forces, isExcluded);
                calculateOnePairChainRule(j, i, atomCoordinates, atomParameters, forces, isExcluded);
392
393
394
           }
        }
    }
Peter Eastman's avatar
Peter Eastman committed
395
396
397
398

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

    for (int i = 0; i < numAtoms; i++) {
399
400
401
        expressionSet.setVariable(xIndex, atomCoordinates[i][0]);
        expressionSet.setVariable(yIndex, atomCoordinates[i][1]);
        expressionSet.setVariable(zIndex, atomCoordinates[i][2]);
peastman's avatar
peastman committed
402
403
404
        vector<double> dVdX(valueDerivExpressions.size(), 0.0);
        vector<double> dVdY(valueDerivExpressions.size(), 0.0);
        vector<double> dVdZ(valueDerivExpressions.size(), 0.0);
405
406
407
408
        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]);
409
            for (int k = 1; k < j; k++) {
peastman's avatar
peastman committed
410
                double dVdV = valueDerivExpressions[j][k].evaluate();
411
412
413
                dVdX[j] += dVdV*dVdX[k];
                dVdY[j] += dVdV*dVdY[k];
                dVdZ[j] += dVdV*dVdZ[k];
Peter Eastman's avatar
Peter Eastman committed
414
            }
peastman's avatar
peastman committed
415
416
417
            dVdX[j] += valueGradientExpressions[j][0].evaluate();
            dVdY[j] += valueGradientExpressions[j][1].evaluate();
            dVdZ[j] += valueGradientExpressions[j][2].evaluate();
418
419
420
            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
421
422
        }
    }
423
424
425
426
427
428
429
        
    // Compute chain rule terms for derivatives with respect to parameters.

    for (int i = 0; i < numAtoms; i++)
        for (int j = 0; j < (int) valueIndex.size(); j++)
            for (int k = 0; k < dValuedParam[j].size(); k++)
                energyParamDerivs[k] += dEdV[j][i]*dValuedParam[j][k][i];
430
431
}

peastman's avatar
peastman committed
432
433
void ReferenceCustomGBIxn::calculateOnePairChainRule(int atom1, int atom2, vector<Vec3>& atomCoordinates, double** atomParameters,
        vector<Vec3>& forces, bool isExcluded) {
434
435
    // Compute the displacement.

peastman's avatar
peastman committed
436
    double deltaR[ReferenceForce::LastDeltaRIndex];
437
    if (periodic)
438
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxVectors, deltaR);
439
440
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
peastman's avatar
peastman committed
441
    double r = deltaR[ReferenceForce::RIndex];
442
443
444
445
446
    if (cutoff && r >= cutoffDistance)
        return;

    // Record variables for evaluating expressions.

447
448
449
    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]);
450
    }
451
452
453
    expressionSet.setVariable(rIndex, r);
    expressionSet.setVariable(particleValueIndex[0], values[0][atom1]);
    expressionSet.setVariable(particleValueIndex[1], values[0][atom2]);
454
455
456

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

peastman's avatar
peastman committed
457
    double rinv = 1/r;
Peter Eastman's avatar
Peter Eastman committed
458
459
460
    deltaR[0] *= rinv;
    deltaR[1] *= rinv;
    deltaR[2] *= rinv;
peastman's avatar
peastman committed
461
462
    vector<double> dVdR1(valueDerivExpressions.size(), 0.0);
    vector<double> dVdR2(valueDerivExpressions.size(), 0.0);
463
    if (!isExcluded || valueTypes[0] != OpenMM::CustomGBForce::ParticlePair) {
peastman's avatar
peastman committed
464
        dVdR1[0] = valueDerivExpressions[0][0].evaluate();
Peter Eastman's avatar
Peter Eastman committed
465
        dVdR2[0] = -dVdR1[0];
466
        for (int i = 0; i < 3; i++) {
Peter Eastman's avatar
Peter Eastman committed
467
468
            forces[atom1][i] -= dEdV[0][atom1]*dVdR1[0]*deltaR[i];
            forces[atom2][i] -= dEdV[0][atom1]*dVdR2[0]*deltaR[i];
469
        }
470
    }
471
472
473
474
475
476
477
478
    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]);
479
        for (int j = 0; j < i; j++) {
peastman's avatar
peastman committed
480
            double dVdV = valueDerivExpressions[i][j].evaluate();
Peter Eastman's avatar
Peter Eastman committed
481
482
            dVdR1[i] += dVdV*dVdR1[j];
            dVdR2[i] += dVdV*dVdR2[j];
483
484
        }
        for (int k = 0; k < 3; k++) {
Peter Eastman's avatar
Peter Eastman committed
485
486
            forces[atom1][k] -= dEdV[i][atom1]*dVdR1[i]*deltaR[k];
            forces[atom2][k] -= dEdV[i][atom1]*dVdR2[i]*deltaR[k];
487
        }
488
489
    }
}