ReferenceCustomGBIxn.cpp 20 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

/* 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>

#include "../SimTKUtilities/SimTKOpenMMCommon.h"
#include "../SimTKUtilities/SimTKOpenMMLog.h"
#include "../SimTKUtilities/SimTKOpenMMUtilities.h"
#include "ReferenceForce.h"
#include "ReferenceCustomGBIxn.h"

using std::map;
using std::set;
using std::string;
using std::stringstream;
using std::vector;

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

   ReferenceCustomGBIxn constructor

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

ReferenceCustomGBIxn::ReferenceCustomGBIxn(const vector<Lepton::ExpressionProgram>& valueExpressions,
47
                     const vector<vector<Lepton::ExpressionProgram> > valueDerivExpressions,
48
                     const vector<vector<Lepton::ExpressionProgram> > valueGradientExpressions,
49
50
51
52
                     const vector<string>& valueNames,
                     const vector<OpenMM::CustomGBForce::ComputationType>& valueTypes,
                     const vector<Lepton::ExpressionProgram>& energyExpressions,
                     const vector<vector<Lepton::ExpressionProgram> > energyDerivExpressions,
53
                     const vector<vector<Lepton::ExpressionProgram> > energyGradientExpressions,
54
55
                     const vector<OpenMM::CustomGBForce::ComputationType>& energyTypes,
                     const vector<string>& parameterNames) :
56
57
            cutoff(false), periodic(false), valueExpressions(valueExpressions), valueDerivExpressions(valueDerivExpressions), valueGradientExpressions(valueGradientExpressions),
            valueNames(valueNames), valueTypes(valueTypes), energyExpressions(energyExpressions), energyDerivExpressions(energyDerivExpressions), energyGradientExpressions(energyGradientExpressions),
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
            energyTypes(energyTypes), paramNames(parameterNames) {

   // ---------------------------------------------------------------------------------------

   // static const char* methodName = "\nReferenceCustomGBIxn::ReferenceCustomGBIxn";

   // ---------------------------------------------------------------------------------------

    for (int i = 0; i < (int) paramNames.size(); i++) {
        for (int j = 1; j < 3; j++) {
            stringstream name;
            name << paramNames[i] << j;
            particleParamNames.push_back(name.str());
        }
    }
    for (int i = 0; i < (int) valueNames.size(); i++) {
        for (int j = 1; j < 3; j++) {
            stringstream name;
            name << valueNames[i] << j;
            particleValueNames.push_back(name.str());
        }
    }
}

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

   ReferenceCustomGBIxn destructor

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

ReferenceCustomGBIxn::~ReferenceCustomGBIxn( ){

   // ---------------------------------------------------------------------------------------

   // static const char* methodName = "\nReferenceCustomGBIxn::~ReferenceCustomGBIxn";

   // ---------------------------------------------------------------------------------------

}

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

     Set the force to use a cutoff.

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

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

107
  void ReferenceCustomGBIxn::setUseCutoff( RealOpenMM distance, const OpenMM::NeighborList& neighbors ) {
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

    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.

     @param boxSize             the X, Y, and Z widths of the periodic box

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

124
  void ReferenceCustomGBIxn::setPeriodic( RealOpenMM* boxSize ) {
125
126
127
128
129
130
131
132
133
134

    assert(boxSize[0] >= 2.0*cutoffDistance);
    assert(boxSize[1] >= 2.0*cutoffDistance);
    assert(boxSize[2] >= 2.0*cutoffDistance);
    periodic = true;
    periodicBoxSize[0] = boxSize[0];
    periodicBoxSize[1] = boxSize[1];
    periodicBoxSize[2] = boxSize[2];
  }

135
void ReferenceCustomGBIxn::calculateIxn(int numberOfAtoms, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
136
                                           const vector<set<int> >& exclusions, map<string, double>& globalParameters, RealOpenMM** forces,
137
138
139
                                           RealOpenMM* totalEnergy) const {
    // First calculate the computed values.

140
    int numValues = valueTypes.size();
141
    vector<vector<RealOpenMM> > values(numValues);
142
143
    for (int valueIndex = 0; valueIndex < numValues; valueIndex++) {
        if (valueTypes[valueIndex] == OpenMM::CustomGBForce::SingleParticle)
144
            calculateSingleParticleValue(valueIndex, numberOfAtoms, atomCoordinates, values, globalParameters, atomParameters);
145
146
147
148
149
        else if (valueTypes[valueIndex] == OpenMM::CustomGBForce::ParticlePair)
            calculateParticlePairValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters, values, globalParameters, exclusions, true);
        else
            calculateParticlePairValue(valueIndex, numberOfAtoms, atomCoordinates, atomParameters, values, globalParameters, exclusions, false);
    }
150
151
152
153

    // Now calculate the energy and its derivates.

    vector<vector<RealOpenMM> > dEdV(numValues, vector<RealOpenMM>(numberOfAtoms, (RealOpenMM) 0));
154
155
    for (int termIndex = 0; termIndex < (int) energyExpressions.size(); termIndex++) {
        if (energyTypes[termIndex] == OpenMM::CustomGBForce::SingleParticle)
156
            calculateSingleParticleEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, values, globalParameters, atomParameters, forces, totalEnergy, dEdV);
157
        else if (energyTypes[termIndex] == OpenMM::CustomGBForce::ParticlePair)
158
            calculateParticlePairEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, values, globalParameters, exclusions, true, forces, totalEnergy, dEdV);
159
        else
160
            calculateParticlePairEnergyTerm(termIndex, numberOfAtoms, atomCoordinates, atomParameters, values, globalParameters, exclusions, false, forces, totalEnergy, dEdV);
161
    }
162
163
164
165

    // Apply the chain rule to evaluate forces.

    calculateChainRuleForces(numberOfAtoms, atomCoordinates, atomParameters, values, globalParameters, exclusions, forces, dEdV);
166
167
}

168
void ReferenceCustomGBIxn::calculateSingleParticleValue(int index, int numAtoms, RealOpenMM** atomCoordinates, vector<vector<RealOpenMM> >& values,
169
170
171
172
        const map<string, double>& globalParameters, RealOpenMM** atomParameters) const {
    values[index].resize(numAtoms);
    map<string, double> variables = globalParameters;
    for (int i = 0; i < numAtoms; i++) {
173
174
175
        variables["x"] = atomCoordinates[i][0];
        variables["y"] = atomCoordinates[i][1];
        variables["z"] = atomCoordinates[i][2];
176
177
178
        for (int j = 0; j < (int) paramNames.size(); j++)
            variables[paramNames[j]] = atomParameters[i][j];
        for (int j = 0; j < index; j++)
179
180
            variables[valueNames[j]] = values[j][i];
        values[index][i] = (RealOpenMM) valueExpressions[index].evaluate(variables);
181
182
183
184
    }
}

void ReferenceCustomGBIxn::calculateParticlePairValue(int index, int numAtoms, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
185
        vector<vector<RealOpenMM> >& values, const map<string, double>& globalParameters, const vector<set<int> >& exclusions, bool useExclusions) const {
186
187
    values[index].resize(numAtoms);
    for (int i = 0; i < numAtoms; i++)
188
        values[index][i] = (RealOpenMM) 0.0;
189
    if (cutoff) {
190
191
        // Loop over all pairs in the neighbor list.

192
193
194
195
196
197
198
199
200
        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;
            calculateOnePairValue(index, pair.first, pair.second, atomCoordinates, atomParameters, globalParameters, values);
            calculateOnePairValue(index, pair.second, pair.first, atomCoordinates, atomParameters, globalParameters, values);
        }
    }
    else {
201
202
        // Perform an O(N^2) loop over all atom pairs.

203
204
205
206
207
208
209
210
211
212
213
214
        for (int i = 0; i < numAtoms; i++){
            for (int j = i+1; j < numAtoms; j++ ){
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
                calculateOnePairValue(index, i, j, atomCoordinates, atomParameters, globalParameters, values);
                calculateOnePairValue(index, j, i, atomCoordinates, atomParameters, globalParameters, values);
           }
        }
    }
}

void ReferenceCustomGBIxn::calculateOnePairValue(int index, int atom1, int atom2, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
215
        const map<string, double>& globalParameters, vector<vector<RealOpenMM> >& values) const {
216
217
218
219
220
221
222
223
224
    RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
    if (periodic)
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxSize, deltaR);
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
    RealOpenMM r = deltaR[ReferenceForce::RIndex];
    if (cutoff && r >= cutoffDistance)
        return;
    map<string, double> variables = globalParameters;
225
    for (int i = 0; i < (int) paramNames.size(); i++) {
226
227
228
229
230
        variables[particleParamNames[i*2]] = atomParameters[atom1][i];
        variables[particleParamNames[i*2+1]] = atomParameters[atom2][i];
    }
    variables["r"] = r;
    for (int i = 0; i < index; i++) {
231
232
        variables[particleValueNames[i*2]] = values[i][atom1];
        variables[particleValueNames[i*2+1]] = values[i][atom2];
233
    }
234
    values[index][atom1] += (RealOpenMM) valueExpressions[index].evaluate(variables);
235
236
}

237
238
void ReferenceCustomGBIxn::calculateSingleParticleEnergyTerm(int index, int numAtoms, RealOpenMM** atomCoordinates, const vector<vector<RealOpenMM> >& values,
        const map<string, double>& globalParameters, RealOpenMM** atomParameters, RealOpenMM** forces, RealOpenMM* totalEnergy,
239
        vector<vector<RealOpenMM> >& dEdV) const {
240
241
    map<string, double> variables = globalParameters;
    for (int i = 0; i < numAtoms; i++) {
242
243
244
        variables["x"] = atomCoordinates[i][0];
        variables["y"] = atomCoordinates[i][1];
        variables["z"] = atomCoordinates[i][2];
245
246
247
        for (int j = 0; j < (int) paramNames.size(); j++)
            variables[paramNames[j]] = atomParameters[i][j];
        for (int j = 0; j < (int) valueNames.size(); j++)
248
            variables[valueNames[j]] = values[j][i];
249
250
        if (totalEnergy != NULL)
            *totalEnergy += (RealOpenMM) energyExpressions[index].evaluate(variables);
251
252
        for (int j = 0; j < (int) valueNames.size(); j++)
            dEdV[j][i] += (RealOpenMM) energyDerivExpressions[index][j].evaluate(variables);
253
254
255
        forces[i][0] -= (RealOpenMM) energyGradientExpressions[index][0].evaluate(variables);
        forces[i][1] -= (RealOpenMM) energyGradientExpressions[index][1].evaluate(variables);
        forces[i][2] -= (RealOpenMM) energyGradientExpressions[index][2].evaluate(variables);
256
257
258
259
    }
}

void ReferenceCustomGBIxn::calculateParticlePairEnergyTerm(int index, int numAtoms, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
260
261
        const vector<vector<RealOpenMM> >& values, const map<string, double>& globalParameters, const vector<set<int> >& exclusions, bool useExclusions,
        RealOpenMM** forces, RealOpenMM* totalEnergy, vector<vector<RealOpenMM> >& dEdV) const {
262
    if (cutoff) {
263
264
        // Loop over all pairs in the neighbor list.

265
266
267
268
        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;
269
            calculateOnePairEnergyTerm(index, pair.first, pair.second, atomCoordinates, atomParameters, globalParameters, values, forces, totalEnergy, dEdV);
270
271
272
        }
    }
    else {
273
274
        // Perform an O(N^2) loop over all atom pairs.

275
276
277
278
        for (int i = 0; i < numAtoms; i++){
            for (int j = i+1; j < numAtoms; j++ ){
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
279
                calculateOnePairEnergyTerm(index, i, j, atomCoordinates, atomParameters, globalParameters, values, forces, totalEnergy, dEdV);
280
281
282
283
284
285
           }
        }
    }
}

void ReferenceCustomGBIxn::calculateOnePairEnergyTerm(int index, int atom1, int atom2, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
286
287
288
289
        const map<string, double>& globalParameters, const vector<vector<RealOpenMM> >& values, RealOpenMM** forces, RealOpenMM* totalEnergy,
        vector<vector<RealOpenMM> >& dEdV) const {
    // Compute the displacement.

290
291
292
293
294
295
296
297
    RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
    if (periodic)
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxSize, deltaR);
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
    RealOpenMM r = deltaR[ReferenceForce::RIndex];
    if (cutoff && r >= cutoffDistance)
        return;
298
299
300

    // Record variables for evaluating expressions.

301
    map<string, double> variables = globalParameters;
302
    for (int i = 0; i < (int) paramNames.size(); i++) {
303
304
305
306
307
        variables[particleParamNames[i*2]] = atomParameters[atom1][i];
        variables[particleParamNames[i*2+1]] = atomParameters[atom2][i];
    }
    variables["r"] = r;
    for (int i = 0; i < (int) valueNames.size(); i++) {
308
309
        variables[particleValueNames[i*2]] = values[i][atom1];
        variables[particleValueNames[i*2+1]] = values[i][atom2];
310
    }
311
312
313

    // Evaluate the energy and its derivatives.

314
315
316
317
318
319
320
321
322
    if (totalEnergy != NULL)
        *totalEnergy += (RealOpenMM) energyExpressions[index].evaluate(variables);
    RealOpenMM dEdR = (RealOpenMM) energyDerivExpressions[index][0].evaluate(variables);
    dEdR *= 1/r;
    for (int i = 0; i < 3; i++) {
       forces[atom1][i] -= dEdR*deltaR[i];
       forces[atom2][i] += dEdR*deltaR[i];
    }
    for (int i = 0; i < (int) valueNames.size(); i++) {
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
        dEdV[i][atom1] += (RealOpenMM) energyDerivExpressions[index][2*i+1].evaluate(variables);
        dEdV[i][atom2] += (RealOpenMM) energyDerivExpressions[index][2*i+2].evaluate(variables);
    }
}

void ReferenceCustomGBIxn::calculateChainRuleForces(int numAtoms, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
        const vector<vector<RealOpenMM> >& values, const map<string, double>& globalParameters,
        const vector<set<int> >& exclusions, RealOpenMM** forces, vector<vector<RealOpenMM> >& dEdV) const {
    bool useExclusions = (energyTypes[0] == OpenMM::CustomGBForce::ParticlePair);
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

        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;
            calculateOnePairChainRule(pair.first, pair.second, atomCoordinates, atomParameters, globalParameters, values, forces, dEdV);
            calculateOnePairChainRule(pair.second, pair.first, atomCoordinates, atomParameters, globalParameters, values, forces, dEdV);
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

        for (int i = 0; i < numAtoms; i++){
            for (int j = i+1; j < numAtoms; j++ ){
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
                calculateOnePairChainRule(i, j, atomCoordinates, atomParameters, globalParameters, values, forces, dEdV);
                calculateOnePairChainRule(j, i, atomCoordinates, atomParameters, globalParameters, values, forces, dEdV);
           }
        }
    }
}

void ReferenceCustomGBIxn::calculateOnePairChainRule(int atom1, int atom2, RealOpenMM** atomCoordinates, RealOpenMM** atomParameters,
        const map<string, double>& globalParameters, const vector<vector<RealOpenMM> >& values, RealOpenMM** forces,
        vector<vector<RealOpenMM> >& dEdV) const {
    // Compute the displacement.

    RealOpenMM deltaR[ReferenceForce::LastDeltaRIndex];
    if (periodic)
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom2], atomCoordinates[atom1], periodicBoxSize, deltaR);
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom2], atomCoordinates[atom1], deltaR);
    RealOpenMM r = deltaR[ReferenceForce::RIndex];
    if (cutoff && r >= cutoffDistance)
        return;

    // Record variables for evaluating expressions.

    map<string, double> variables = globalParameters;
374
    for (int i = 0; i < (int) paramNames.size(); i++) {
375
376
377
378
379
380
381
382
383
        variables[particleParamNames[i*2]] = atomParameters[atom1][i];
        variables[particleParamNames[i*2+1]] = atomParameters[atom2][i];
    }
    variables["r"] = r;
    variables[particleValueNames[0]] = values[0][atom1];
    variables[particleValueNames[1]] = values[0][atom2];

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

384
385
386
    vector<vector<RealOpenMM> > gradient1(valueDerivExpressions.size(), vector<RealOpenMM>(3, 0.0));
    vector<vector<RealOpenMM> > gradient2(valueDerivExpressions.size(), vector<RealOpenMM>(3, 0.0));
    RealOpenMM dVdR = (RealOpenMM) valueDerivExpressions[0][0].evaluate(variables);
387
388
    RealOpenMM rinv = 1/r;
    for (int i = 0; i < 3; i++) {
389
390
391
392
        gradient1[0][i] = dVdR*deltaR[i]*rinv;
        gradient2[0][i] = -gradient1[0][i];
        forces[atom1][i] -= dEdV[0][atom1]*gradient1[0][i];
        forces[atom2][i] -= dEdV[0][atom1]*gradient2[0][i];
393
394
    }
    variables = globalParameters;
395
    for (int i = 0; i < (int) paramNames.size(); i++)
396
397
398
399
        variables[paramNames[i]] = atomParameters[atom1][i];
    variables[valueNames[0]] = values[0][atom1];
    for (int i = 1; i < (int) valueNames.size(); i++) {
        variables[valueNames[i]] = values[i][atom1];
400
401
402
403
404
405
406
407
408
409
410
        for (int j = 0; j < i; j++) {
            RealOpenMM dVdV = (RealOpenMM) valueDerivExpressions[i][j].evaluate(variables);
            for (int k = 0; k < 3; k++) {
                gradient1[i][k] += dVdV*gradient1[j][k];
                gradient2[i][k] += dVdV*gradient2[j][k];
            }
        }
        for (int k = 0; k < 3; k++) {
            gradient1[i][k] += (RealOpenMM) valueGradientExpressions[i][k].evaluate(variables);
            forces[atom1][k] -= dEdV[i][atom1]*gradient1[i][k];
            forces[atom2][k] -= dEdV[i][atom1]*gradient2[i][k];
411
        }
412
413
    }
}