CpuCustomGBForce.cpp 29.4 KB
Newer Older
1

2
/* Portions copyright (c) 2009-2018 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
28
29
30
31
 * 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 "SimTKOpenMMUtilities.h"
#include "ReferenceForce.h"
#include "CpuCustomGBForce.h"

32
33
using namespace OpenMM;
using namespace std;
34

peastman's avatar
peastman committed
35
36
37
38
CpuCustomGBForce::ThreadData::ThreadData(int numAtoms, int numThreads, int threadIndex,
                      const vector<Lepton::CompiledExpression>& valueExpressions,
                      const vector<vector<Lepton::CompiledExpression> >& valueDerivExpressions,
                      const vector<vector<Lepton::CompiledExpression> >& valueGradientExpressions,
39
                      const vector<vector<Lepton::CompiledExpression> >& valueParamDerivExpressions,
peastman's avatar
peastman committed
40
41
42
43
                      const vector<string>& valueNames,
                      const vector<Lepton::CompiledExpression>& energyExpressions,
                      const vector<vector<Lepton::CompiledExpression> >& energyDerivExpressions,
                      const vector<vector<Lepton::CompiledExpression> >& energyGradientExpressions,
44
                      const vector<vector<Lepton::CompiledExpression> >& energyParamDerivExpressions,
peastman's avatar
peastman committed
45
46
                      const vector<string>& parameterNames) :
            valueExpressions(valueExpressions), valueDerivExpressions(valueDerivExpressions), valueGradientExpressions(valueGradientExpressions),
47
48
            valueParamDerivExpressions(valueParamDerivExpressions), energyExpressions(energyExpressions), energyDerivExpressions(energyDerivExpressions),
            energyGradientExpressions(energyGradientExpressions), energyParamDerivExpressions(energyParamDerivExpressions) {
peastman's avatar
peastman committed
49
50
    firstAtom = (threadIndex*(long long) numAtoms)/numThreads;
    lastAtom = ((threadIndex+1)*(long long) numAtoms)/numThreads;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    map<string, double*> variableLocations;
    variableLocations["x"] = &x;
    variableLocations["y"] = &y;
    variableLocations["z"] = &z;
    variableLocations["r"] = &r;
    param.resize(parameterNames.size());
    particleParam.resize(parameterNames.size()*2);
    for (int i = 0; i < (int) parameterNames.size(); i++) {
        variableLocations[parameterNames[i]] = &param[i];
        for (int j = 0; j < 2; j++) {
            stringstream name;
            name << parameterNames[i] << (j+1);
            variableLocations[name.str()] = &particleParam[2*i+j];
        }
    }
    value.resize(valueNames.size());
    particleValue.resize(valueNames.size()*2);
    for (int i = 0; i < (int) valueNames.size(); i++) {
        variableLocations[valueNames[i]] = &value[i];
        for (int j = 0; j < 2; j++) {
            stringstream name;
            name << valueNames[i] << (j+1);
            variableLocations[name.str()] = &particleValue[2*i+j];
        }
    }
peastman's avatar
peastman committed
76
77
78
    for (auto& expression : this->valueExpressions) {
        expression.setVariableLocations(variableLocations);
        expressionSet.registerExpression(expression);
79
    }
peastman's avatar
peastman committed
80
81
82
83
    for (auto& expressions : this->valueDerivExpressions)
        for (auto& expression : expressions) {
            expression.setVariableLocations(variableLocations);
            expressionSet.registerExpression(expression);
84
        }
peastman's avatar
peastman committed
85
86
87
88
    for (auto& expressions : this->valueGradientExpressions)
        for (auto& expression : expressions) {
            expression.setVariableLocations(variableLocations);
            expressionSet.registerExpression(expression);
89
        }
peastman's avatar
peastman committed
90
91
92
93
    for (auto& expressions : this->valueParamDerivExpressions)
        for (auto& expression : expressions) {
            expression.setVariableLocations(variableLocations);
            expressionSet.registerExpression(expression);
94
        }
peastman's avatar
peastman committed
95
96
97
    for (auto& expression : this->energyExpressions) {
        expression.setVariableLocations(variableLocations);
        expressionSet.registerExpression(expression);
98
    }
peastman's avatar
peastman committed
99
100
101
102
    for (auto& expressions : this->energyDerivExpressions)
        for (auto& expression : expressions) {
            expression.setVariableLocations(variableLocations);
            expressionSet.registerExpression(expression);
103
        }
peastman's avatar
peastman committed
104
105
106
107
    for (auto& expressions : this->energyGradientExpressions)
        for (auto& expression : expressions) {
            expression.setVariableLocations(variableLocations);
            expressionSet.registerExpression(expression);
108
        }
peastman's avatar
peastman committed
109
110
111
112
    for (auto& expressions : this->energyParamDerivExpressions)
        for (auto& expression : expressions) {
            expression.setVariableLocations(variableLocations);
            expressionSet.registerExpression(expression);
113
        }
peastman's avatar
peastman committed
114
115
    value0.resize(numAtoms);
    dEdV.resize(valueNames.size());
peastman's avatar
peastman committed
116
117
    for (auto& v : dEdV)
        v.resize(numAtoms);
118
119
120
121
122
    dVdX.resize(valueDerivExpressions.size());
    dVdY.resize(valueDerivExpressions.size());
    dVdZ.resize(valueDerivExpressions.size());
    dVdR1.resize(valueDerivExpressions.size());
    dVdR2.resize(valueDerivExpressions.size());
123
124
    dValue0dParam.resize(valueParamDerivExpressions[0].size(), vector<float>(numAtoms));
    energyParamDerivs.resize(valueParamDerivExpressions[0].size());
125
126
}

peastman's avatar
peastman committed
127
128
129
130
CpuCustomGBForce::CpuCustomGBForce(int numAtoms, const std::vector<std::set<int> >& exclusions,
                     const vector<Lepton::CompiledExpression>& valueExpressions,
                     const vector<vector<Lepton::CompiledExpression> >& valueDerivExpressions,
                     const vector<vector<Lepton::CompiledExpression> >& valueGradientExpressions,
131
                     const vector<vector<Lepton::CompiledExpression> >& valueParamDerivExpressions,
peastman's avatar
peastman committed
132
133
134
135
136
                     const vector<string>& valueNames,
                     const vector<CustomGBForce::ComputationType>& valueTypes,
                     const vector<Lepton::CompiledExpression>& energyExpressions,
                     const vector<vector<Lepton::CompiledExpression> >& energyDerivExpressions,
                     const vector<vector<Lepton::CompiledExpression> >& energyGradientExpressions,
137
                     const vector<vector<Lepton::CompiledExpression> >& energyParamDerivExpressions,
peastman's avatar
peastman committed
138
139
                     const vector<CustomGBForce::ComputationType>& energyTypes,
                     const vector<string>& parameterNames, ThreadPool& threads) :
140
141
            exclusions(exclusions), cutoff(false), periodic(false), valueTypes(valueTypes), energyTypes(energyTypes), numValues(valueNames.size()),
            numParams(parameterNames.size()), threads(threads) {
peastman's avatar
peastman committed
142
    for (int i = 0; i < threads.getNumThreads(); i++)
143
144
145
146
        threadData.push_back(new ThreadData(numAtoms, threads.getNumThreads(), i, valueExpressions, valueDerivExpressions, valueGradientExpressions,
                valueParamDerivExpressions, valueNames, energyExpressions, energyDerivExpressions, energyGradientExpressions, energyParamDerivExpressions, parameterNames));
    values.resize(numValues);
    dEdV.resize(numValues);
peastman's avatar
peastman committed
147
148
149
150
    for (int i = 0; i < (int) values.size(); i++) {
        values[i].resize(numAtoms);
        dEdV[i].resize(numAtoms);
    }
151
152
153
    dValuedParam.resize(numValues);
    for (int i = 0; i < numValues; i++)
        dValuedParam[i].resize(valueParamDerivExpressions[0].size(), vector<float>(numAtoms));
peastman's avatar
peastman committed
154
155
}

156
CpuCustomGBForce::~CpuCustomGBForce() {
peastman's avatar
peastman committed
157
158
    for (auto data : threadData)
        delete data;
159
160
}

161
void CpuCustomGBForce::setUseCutoff(float distance, const CpuNeighborList& neighbors) {
162
163
    cutoff = true;
    cutoffDistance = distance;
164
    cutoffDistance2 = distance*distance;
165
    neighborList = &neighbors;
166
  }
167

peastman's avatar
peastman committed
168
void CpuCustomGBForce::setPeriodic(Vec3& boxSize) {
169
170
171
172
173
174
175
176
177
178
179
    if (cutoff) {
        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];
  }

180
void CpuCustomGBForce::calculateIxn(int numberOfAtoms, float* posq, vector<vector<double> >& atomParameters,
peastman's avatar
peastman committed
181
                                           map<string, double>& globalParameters, vector<AlignedArray<float> >& threadForce,
182
                                           bool includeForce, bool includeEnergy, double& totalEnergy, double* energyParamDerivs) {
peastman's avatar
peastman committed
183
184
185
186
    // Record the parameters for the threads.
    
    this->numberOfAtoms = numberOfAtoms;
    this->posq = posq;
187
    this->atomParameters = &atomParameters[0];
peastman's avatar
peastman committed
188
189
190
191
192
193
194
195
    this->globalParameters = &globalParameters;
    this->threadForce = &threadForce;
    this->includeForce = includeForce;
    this->includeEnergy = includeEnergy;
    threadEnergy.resize(threads.getNumThreads());

    // Calculate the first computed value.

peastman's avatar
peastman committed
196
    auto task = [&] (ThreadPool& threads, int threadIndex) { threadComputeForce(threads, threadIndex); };
peastman's avatar
peastman committed
197
    atomicCounter = 0;
peastman's avatar
peastman committed
198
199
    threads.execute(task);
    threads.waitForThreads();
200
201
202
203
204
205
206
207

    // Sum derivatives of the first computed value with respect to global parameters.

    bool hasParamDerivs = (threadData[0]->dValue0dParam.size() > 0);
    if (hasParamDerivs) {
        threads.resumeThreads();
        threads.waitForThreads();
    }
peastman's avatar
peastman committed
208
209
210
211
212
    
    // Calculate the remaining computed values.
    
    threads.resumeThreads();
    threads.waitForThreads();
213
    
peastman's avatar
peastman committed
214
215
216
    // Calculate the energy terms.

    for (int i = 0; i < (int) threadData[0]->energyExpressions.size(); i++) {
peastman's avatar
peastman committed
217
        atomicCounter = 0;
peastman's avatar
peastman committed
218
219
220
221
222
223
224
225
226
227
228
        threads.execute(task);
        threads.waitForThreads();
    }

    // Sum the energy derivatives.

    threads.resumeThreads();
    threads.waitForThreads();
    
    // Apply the chain rule to evaluate forces.

peastman's avatar
peastman committed
229
    atomicCounter = 0;
peastman's avatar
peastman committed
230
231
232
233
234
235
236
237
238
239
    threads.resumeThreads();
    threads.waitForThreads();

    // Combine the energies from all the threads.
    
    if (includeEnergy) {
        int numThreads = threads.getNumThreads();
        for (int i = 0; i < numThreads; i++)
            totalEnergy += threadEnergy[i];
    }
240
241
    if (hasParamDerivs)
        for (int i = 0; i < threads.getNumThreads(); i++)
Peter Eastman's avatar
Peter Eastman committed
242
            for (int j = 0; j < threadData[i]->energyParamDerivs.size(); j++)
243
                energyParamDerivs[j] += threadData[i]->energyParamDerivs[j];
peastman's avatar
peastman committed
244
245
246
247
248
249
250
251
252
253
}

void CpuCustomGBForce::threadComputeForce(ThreadPool& threads, int threadIndex) {
    // Compute this thread's subset of interactions.

    int numThreads = threads.getNumThreads();
    threadEnergy[threadIndex] = 0;
    double& energy = threadEnergy[threadIndex];
    float* forces = &(*threadForce)[threadIndex][0];
    ThreadData& data = *threadData[threadIndex];
254
255
    fvec4 boxSize(periodicBoxSize[0], periodicBoxSize[1], periodicBoxSize[2], 0);
    fvec4 invBoxSize((1/periodicBoxSize[0]), (1/periodicBoxSize[1]), (1/periodicBoxSize[2]), 0);
peastman's avatar
peastman committed
256
257
    for (auto& param : *globalParameters)
        data.expressionSet.setVariable(data.expressionSet.getVariableIndex(param.first), param.second);
peastman's avatar
peastman committed
258
259
260

    // Calculate the first computed value.

peastman's avatar
peastman committed
261
262
263
264
265
    for (auto& v : data.value0)
        v = 0.0f;
    for (auto& vals : data.dValue0dParam)
        for (auto& v : vals)
            v = 0.0f;
peastman's avatar
peastman committed
266
267
268
269
270
    if (valueTypes[0] == CustomGBForce::ParticlePair)
        calculateParticlePairValue(0, data, numberOfAtoms, posq, atomParameters, true, boxSize, invBoxSize);
    else
        calculateParticlePairValue(0, data, numberOfAtoms, posq, atomParameters, false, boxSize, invBoxSize);
    threads.syncThreads();
271
272
273
274
275
276
277
278
279
280
281
282
283
284
    
    // Sum derivatives of the first computed value with respect to global parameters.
    
    bool hasParamDerivs = (data.dValue0dParam.size() > 0);
    if (hasParamDerivs) {
        for (int j = 0; j < data.dValue0dParam.size(); j++)
            for (int k = data.firstAtom; k < data.lastAtom; k++) {
                float sum = 0.0f;
                for (int m = 0; m < threadData.size(); m++)
                    sum += threadData[m]->dValue0dParam[j][k];
                dValuedParam[0][j][k] = sum;
            }
        threads.syncThreads();
    }
peastman's avatar
peastman committed
285

286
    // Sum the first computed value and calculate the remaining ones.
peastman's avatar
peastman committed
287

288
    int numValues = valueTypes.size();
peastman's avatar
peastman committed
289
290
    for (int atom = data.firstAtom; atom < data.lastAtom; atom++) {
        float sum = 0.0f;
peastman's avatar
peastman committed
291
292
        for (auto& data : threadData)
            sum += data->value0[atom];
peastman's avatar
peastman committed
293
        values[0][atom] = sum;
294
295
296
        data.x = posq[4*atom];
        data.y = posq[4*atom+1];
        data.z = posq[4*atom+2];
297
        for (int j = 0; j < numParams; j++)
298
            data.param[j] = atomParameters[atom][j];
299
        for (int i = 1; i < numValues; i++) {
300
            data.value[i-1] = values[i-1][atom];
301
            values[i][atom] = (float) data.valueExpressions[i].evaluate();
302
303
304
305
306
307
308
309
310
311
312
313

            // Calculate derivatives with respect to parameters.

            if (hasParamDerivs) {
                for (int j = 0; j < data.valueParamDerivExpressions[i].size(); j++)
                    dValuedParam[i][j][atom] = data.valueParamDerivExpressions[i][j].evaluate();
                for (int j = 0; j < i; j++) {
                    float dVdV = data.valueDerivExpressions[i][j].evaluate();
                    for (int k = 0; k < data.valueParamDerivExpressions[i].size(); k++)
                        dValuedParam[i][k][atom] += dVdV*dValuedParam[j][k][atom];
                }
            }
314
        }
peastman's avatar
peastman committed
315
316
    }
    threads.syncThreads();
317
318
319

    // Now calculate the energy and its derivatives.

peastman's avatar
peastman committed
320
321
322
323
324
    for (auto& vals : data.dEdV)
        for (auto& v : vals)
            v = 0.0f;
    for (auto& v : data.energyParamDerivs)
        v = 0.0f;
peastman's avatar
peastman committed
325
    for (int termIndex = 0; termIndex < (int) data.energyExpressions.size(); termIndex++) {
326
        if (energyTypes[termIndex] == CustomGBForce::SingleParticle)
peastman's avatar
peastman committed
327
            calculateSingleParticleEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, forces, energy);
328
        else if (energyTypes[termIndex] == CustomGBForce::ParticlePair)
peastman's avatar
peastman committed
329
            calculateParticlePairEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, true, forces, energy, boxSize, invBoxSize);
330
        else
peastman's avatar
peastman committed
331
332
333
334
335
336
337
338
339
            calculateParticlePairEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, false, forces, energy, boxSize, invBoxSize);
        threads.syncThreads();
    }

    // Sum the energy derivatives.

    for (int atom = data.firstAtom; atom < data.lastAtom; atom++) {
        for (int i = 0; i < (int) dEdV.size(); i++) {
            float sum = 0.0f;
peastman's avatar
peastman committed
340
341
            for (auto& data : threadData)
                sum += data->dEdV[i][atom];
peastman's avatar
peastman committed
342
343
            dEdV[i][atom] = sum;
        }
344
    }
peastman's avatar
peastman committed
345
    threads.syncThreads();
346
347
348

    // Apply the chain rule to evaluate forces.

peastman's avatar
peastman committed
349
    calculateChainRuleForces(data, numberOfAtoms, posq, atomParameters, forces, boxSize, invBoxSize);
350
351
}

352
void CpuCustomGBForce::calculateParticlePairValue(int index, ThreadData& data, int numAtoms, float* posq, vector<double>* atomParameters,
peastman's avatar
peastman committed
353
        bool useExclusions, const fvec4& boxSize, const fvec4& invBoxSize) {
354
    for (int i = 0; i < numAtoms; i++)
355
        values[index][i] = 0.0f;
peastman's avatar
peastman committed
356
    vector<float>& valueArray = (index == 0 ? data.value0 : values[index]);
357
358
359
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

peastman's avatar
peastman committed
360
        while (true) {
peastman's avatar
peastman committed
361
            int blockIndex = atomicCounter++;
peastman's avatar
peastman committed
362
363
            if (blockIndex >= neighborList->getNumBlocks())
                break;
364
365
            const int blockSize = neighborList->getBlockSize();
            const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
366
367
368
369
            const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
            const vector<char>& blockExclusions = neighborList->getBlockExclusions(blockIndex);
            for (int i = 0; i < (int) neighbors.size(); i++) {
                int first = neighbors[i];
370
                for (int k = 0; k < blockSize; k++) {
371
372
373
374
                    if ((blockExclusions[i] & (1<<k)) == 0) {
                        int second = blockAtom[k];
                        if (useExclusions && exclusions[first].find(second) != exclusions[first].end())
                            continue;
peastman's avatar
peastman committed
375
376
                        calculateOnePairValue(index, first, second, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
                        calculateOnePairValue(index, second, first, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
377
378
379
                    }
                }
            }
380
381
382
383
384
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

peastman's avatar
peastman committed
385
        while (true) {
peastman's avatar
peastman committed
386
            int i = atomicCounter++;
peastman's avatar
peastman committed
387
388
            if (i >= numAtoms)
                break;
389
390
391
            for (int j = i+1; j < numAtoms; j++) {
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
peastman's avatar
peastman committed
392
393
                calculateOnePairValue(index, i, j, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
                calculateOnePairValue(index, j, i, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
394
395
396
397
398
           }
        }
    }
}

399
void CpuCustomGBForce::calculateOnePairValue(int index, int atom1, int atom2, ThreadData& data, float* posq, vector<double>* atomParameters,
peastman's avatar
peastman committed
400
        vector<float>& valueArray, const fvec4& boxSize, const fvec4& invBoxSize) {
401
402
403
404
405
    fvec4 deltaR;
    fvec4 pos1(posq+4*atom1);
    fvec4 pos2(posq+4*atom2);
    float r2;
    getDeltaR(pos2, pos1, deltaR, r2, periodic, boxSize, invBoxSize);
406
    if (cutoff && r2 >= cutoffDistance2)
407
        return;
408
    data.r = sqrtf(r2);
409
    for (int i = 0; i < numParams; i++) {
410
411
        data.particleParam[i*2] = atomParameters[atom1][i];
        data.particleParam[i*2+1] = atomParameters[atom2][i];
412
413
    }
    for (int i = 0; i < index; i++) {
414
415
        data.particleValue[i*2] = values[i][atom1];
        data.particleValue[i*2+1] = values[i][atom2];
416
    }
peastman's avatar
peastman committed
417
    valueArray[atom1] += (float) data.valueExpressions[index].evaluate();
418
419
420
421
422
    
    // Calculate derivatives with respect to parameters.
    
    for (int i = 0; i < data.valueParamDerivExpressions[index].size(); i++)
        data.dValue0dParam[i][atom1] += data.valueParamDerivExpressions[index][i].evaluate();
423
424
}

peastman's avatar
peastman committed
425
void CpuCustomGBForce::calculateSingleParticleEnergyTerm(int index, ThreadData& data, int numAtoms, float* posq,
426
        vector<double>* atomParameters, float* forces, double& totalEnergy) {
peastman's avatar
peastman committed
427
    for (int i = data.firstAtom; i < data.lastAtom; i++) {
428
429
430
        data.x = posq[4*i];
        data.y = posq[4*i+1];
        data.z = posq[4*i+2];
431
        for (int j = 0; j < numParams; j++)
432
            data.param[j] = atomParameters[i][j];
433
        for (int j = 0; j < (int) values.size(); j++)
434
            data.value[j] = values[j][i];
peastman's avatar
peastman committed
435
436
        if (includeEnergy)
            totalEnergy += (float) data.energyExpressions[index].evaluate();
437
        for (int j = 0; j < (int) values.size(); j++)
peastman's avatar
peastman committed
438
439
440
441
            data.dEdV[j][i] += (float) data.energyDerivExpressions[index][j].evaluate();
        forces[4*i+0] -= (float) data.energyGradientExpressions[index][0].evaluate();
        forces[4*i+1] -= (float) data.energyGradientExpressions[index][1].evaluate();
        forces[4*i+2] -= (float) data.energyGradientExpressions[index][2].evaluate();
442
443
444
445
446
        
        // Compute derivatives with respect to parameters.
        
        for (int k = 0; k < data.energyParamDerivExpressions[index].size(); k++)
            data.energyParamDerivs[k] += data.energyParamDerivExpressions[index][k].evaluate();
447
448
449
    }
}

450
void CpuCustomGBForce::calculateParticlePairEnergyTerm(int index, ThreadData& data, int numAtoms, float* posq, vector<double>* atomParameters,
peastman's avatar
peastman committed
451
        bool useExclusions, float* forces, double& totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize) {
452
453
454
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

peastman's avatar
peastman committed
455
        while (true) {
peastman's avatar
peastman committed
456
            int blockIndex = atomicCounter++;
peastman's avatar
peastman committed
457
458
            if (blockIndex >= neighborList->getNumBlocks())
                break;
459
460
            const int blockSize = neighborList->getBlockSize();
            const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
461
462
463
464
            const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
            const vector<char>& blockExclusions = neighborList->getBlockExclusions(blockIndex);
            for (int i = 0; i < (int) neighbors.size(); i++) {
                int first = neighbors[i];
465
                for (int k = 0; k < blockSize; k++) {
466
467
468
469
                    if ((blockExclusions[i] & (1<<k)) == 0) {
                        int second = blockAtom[k];
                        if (useExclusions && exclusions[first].find(second) != exclusions[first].end())
                            continue;
peastman's avatar
peastman committed
470
                        calculateOnePairEnergyTerm(index, first, second, data, posq, atomParameters, forces, totalEnergy, boxSize, invBoxSize);
471
472
473
                    }
                }
            }
474
475
476
477
478
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

peastman's avatar
peastman committed
479
        while (true) {
peastman's avatar
peastman committed
480
            int i = atomicCounter++;
peastman's avatar
peastman committed
481
482
            if (i >= numAtoms)
                break;
483
484
485
            for (int j = i+1; j < numAtoms; j++) {
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
peastman's avatar
peastman committed
486
                calculateOnePairEnergyTerm(index, i, j, data, posq, atomParameters, forces, totalEnergy, boxSize, invBoxSize);
487
488
489
490
491
           }
        }
    }
}

492
void CpuCustomGBForce::calculateOnePairEnergyTerm(int index, int atom1, int atom2, ThreadData& data, float* posq, vector<double>* atomParameters,
peastman's avatar
peastman committed
493
        float* forces, double& totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize) {
494
495
    // Compute the displacement.

496
497
498
499
500
    fvec4 deltaR;
    fvec4 pos1(posq+4*atom1);
    fvec4 pos2(posq+4*atom2);
    float r2;
    getDeltaR(pos2, pos1, deltaR, r2, periodic, boxSize, invBoxSize);
501
    if (cutoff && r2 >= cutoffDistance2)
502
        return;
503
504
    float r = sqrtf(r2);
    data.r = r;
505
506
507

    // Record variables for evaluating expressions.

508
    for (int i = 0; i < numParams; i++) {
509
510
        data.particleParam[i*2] = atomParameters[atom1][i];
        data.particleParam[i*2+1] = atomParameters[atom2][i];
511
    }
512
    for (int i = 0; i < (int) values.size(); i++) {
513
514
        data.particleValue[i*2] = values[i][atom1];
        data.particleValue[i*2+1] = values[i][atom2];
515
516
517
518
    }

    // Evaluate the energy and its derivatives.

peastman's avatar
peastman committed
519
520
521
    if (includeEnergy)
        totalEnergy += (float) data.energyExpressions[index].evaluate();
    float dEdR = (float) data.energyDerivExpressions[index][0].evaluate();
522
    dEdR *= 1/r;
523
524
525
    fvec4 result = deltaR*dEdR;
    (fvec4(forces+4*atom1)-result).store(forces+4*atom1);
    (fvec4(forces+4*atom2)+result).store(forces+4*atom2);
526
    for (int i = 0; i < (int) values.size(); i++) {
peastman's avatar
peastman committed
527
528
        data.dEdV[i][atom1] += (float) data.energyDerivExpressions[index][2*i+1].evaluate();
        data.dEdV[i][atom2] += (float) data.energyDerivExpressions[index][2*i+2].evaluate();
529
    }
530
531
532
533
534
        
    // Compute derivatives with respect to parameters.

    for (int i = 0; i < data.energyParamDerivExpressions[index].size(); i++)
        data.energyParamDerivs[i] += data.energyParamDerivExpressions[index][i].evaluate();
535
536
}

537
void CpuCustomGBForce::calculateChainRuleForces(ThreadData& data, int numAtoms, float* posq, vector<double>* atomParameters,
peastman's avatar
peastman committed
538
        float* forces, const fvec4& boxSize, const fvec4& invBoxSize) {
539
540
541
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

peastman's avatar
peastman committed
542
        while (true) {
peastman's avatar
peastman committed
543
            int blockIndex = atomicCounter++;
peastman's avatar
peastman committed
544
545
            if (blockIndex >= neighborList->getNumBlocks())
                break;
546
547
            const int blockSize = neighborList->getBlockSize();
            const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
548
549
550
551
            const vector<int>& neighbors = neighborList->getBlockNeighbors(blockIndex);
            const vector<char>& blockExclusions = neighborList->getBlockExclusions(blockIndex);
            for (int i = 0; i < (int) neighbors.size(); i++) {
                int first = neighbors[i];
552
                for (int k = 0; k < blockSize; k++) {
553
554
555
                    if ((blockExclusions[i] & (1<<k)) == 0) {
                        int second = blockAtom[k];
                        bool isExcluded = (exclusions[first].find(second) != exclusions[first].end());
peastman's avatar
peastman committed
556
557
                        calculateOnePairChainRule(first, second, data, posq, atomParameters, forces, isExcluded, boxSize, invBoxSize);
                        calculateOnePairChainRule(second, first, data, posq, atomParameters, forces, isExcluded, boxSize, invBoxSize);
558
559
560
                    }
                }
            }
561
562
563
564
565
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

peastman's avatar
peastman committed
566
        while (true) {
peastman's avatar
peastman committed
567
            int i = atomicCounter++;
peastman's avatar
peastman committed
568
569
            if (i >= numAtoms)
                break;
570
571
            for (int j = i+1; j < numAtoms; j++) {
                bool isExcluded = (exclusions[i].find(j) != exclusions[i].end());
peastman's avatar
peastman committed
572
573
                calculateOnePairChainRule(i, j, data, posq, atomParameters, forces, isExcluded, boxSize, invBoxSize);
                calculateOnePairChainRule(j, i, data, posq, atomParameters, forces, isExcluded, boxSize, invBoxSize);
574
575
576
577
578
579
           }
        }
    }

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

peastman's avatar
peastman committed
580
    for (int i = data.firstAtom; i < data.lastAtom; i++) {
581
582
583
        data.x = posq[4*i];
        data.y = posq[4*i+1];
        data.z = posq[4*i+2];
584
        for (int j = 0; j < numParams; j++)
585
            data.param[j] = atomParameters[i][j];
586
        for (int j = 1; j < (int) values.size(); j++) {
587
            data.value[j-1] = values[j-1][i];
peastman's avatar
peastman committed
588
589
590
            data.dVdX[j] = 0.0;
            data.dVdY[j] = 0.0;
            data.dVdZ[j] = 0.0;
591
            for (int k = 1; k < j; k++) {
peastman's avatar
peastman committed
592
593
594
595
                float dVdV = (float) data.valueDerivExpressions[j][k].evaluate();
                data.dVdX[j] += dVdV*data.dVdX[k];
                data.dVdY[j] += dVdV*data.dVdY[k];
                data.dVdZ[j] += dVdV*data.dVdZ[k];
596
            }
peastman's avatar
peastman committed
597
598
599
600
601
602
            data.dVdX[j] += (float) data.valueGradientExpressions[j][0].evaluate();
            data.dVdY[j] += (float) data.valueGradientExpressions[j][1].evaluate();
            data.dVdZ[j] += (float) data.valueGradientExpressions[j][2].evaluate();
            forces[4*i+0] -= dEdV[j][i]*data.dVdX[j];
            forces[4*i+1] -= dEdV[j][i]*data.dVdY[j];
            forces[4*i+2] -= dEdV[j][i]*data.dVdZ[j];
603
604
        }
    }
605
606
607
608
        
    // Compute chain rule terms for derivatives with respect to parameters.

    for (int i = data.firstAtom; i < data.lastAtom; i++)
609
        for (int j = 0; j < data.value.size(); j++)
610
611
            for (int k = 0; k < dValuedParam[j].size(); k++)
                data.energyParamDerivs[k] += dEdV[j][i]*dValuedParam[j][k][i];
612
613
}

614
void CpuCustomGBForce::calculateOnePairChainRule(int atom1, int atom2, ThreadData& data, float* posq, vector<double>* atomParameters,
peastman's avatar
peastman committed
615
        float* forces, bool isExcluded, const fvec4& boxSize, const fvec4& invBoxSize) {
616
617
    // Compute the displacement.

618
619
620
621
622
    fvec4 deltaR;
    fvec4 pos1(posq+4*atom1);
    fvec4 pos2(posq+4*atom2);
    float r2;
    getDeltaR(pos2, pos1, deltaR, r2, periodic, boxSize, invBoxSize);
623
    if (cutoff && r2 >= cutoffDistance2)
624
        return;
625
626
    float r = sqrtf(r2);
    data.r = r;
627
628
629

    // Record variables for evaluating expressions.

630
    for (int i = 0; i < numParams; i++) {
631
632
633
634
635
636
637
638
639
640
        data.particleParam[i*2] = atomParameters[atom1][i];
        data.particleParam[i*2+1] = atomParameters[atom2][i];
        data.param[i] = atomParameters[atom1][i];
    }
    data.value[0] = values[0][atom1];
    data.x = posq[4*atom1];
    data.y = posq[4*atom1+1];
    data.z = posq[4*atom1+2];
    data.particleValue[0] = values[0][atom1];
    data.particleValue[1] = values[0][atom2];
641
642
643

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

644
    float rinv = 1/r;
645
    deltaR *= rinv;
646
    fvec4 f1(0.0f), f2(0.0f);
647
    if (!isExcluded || valueTypes[0] != CustomGBForce::ParticlePair) {
peastman's avatar
peastman committed
648
649
        data.dVdR1[0] = (float) data.valueDerivExpressions[0][0].evaluate();
        data.dVdR2[0] = -data.dVdR1[0];
650
651
        f1 -= deltaR*(dEdV[0][atom1]*data.dVdR1[0]);
        f2 -= deltaR*(dEdV[0][atom1]*data.dVdR2[0]);
652
    }
653
    for (int i = 1; i < (int) values.size(); i++) {
654
        data.value[i] = values[i][atom1];
peastman's avatar
peastman committed
655
656
        data.dVdR1[i] = 0.0;
        data.dVdR2[i] = 0.0;
657
        for (int j = 0; j < i; j++) {
peastman's avatar
peastman committed
658
659
660
            float dVdV = (float) data.valueDerivExpressions[i][j].evaluate();
            data.dVdR1[i] += dVdV*data.dVdR1[j];
            data.dVdR2[i] += dVdV*data.dVdR2[j];
661
        }
662
663
        f1 -= deltaR*(dEdV[i][atom1]*data.dVdR1[i]);
        f2 -= deltaR*(dEdV[i][atom1]*data.dVdR2[i]);
664
    }
665
666
    (fvec4(forces+4*atom1)+f1).store(forces+4*atom1);
    (fvec4(forces+4*atom2)+f2).store(forces+4*atom2);
667
668
669
670
671
672
673
}

void CpuCustomGBForce::getDeltaR(const fvec4& posI, const fvec4& posJ, fvec4& deltaR, float& r2, bool periodic, const fvec4& boxSize, const fvec4& invBoxSize) const {
    deltaR = posJ-posI;
    if (periodic) {
        fvec4 base = round(deltaR*invBoxSize)*boxSize;
        deltaR = deltaR-base;
674
    }
675
    r2 = dot3(deltaR, deltaR);
676
}