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
}

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

    threadEnergy[threadIndex] = 0;
    double& energy = threadEnergy[threadIndex];
    float* forces = &(*threadForce)[threadIndex][0];
    ThreadData& data = *threadData[threadIndex];
253
254
    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
255
256
    for (auto& param : *globalParameters)
        data.expressionSet.setVariable(data.expressionSet.getVariableIndex(param.first), param.second);
peastman's avatar
peastman committed
257
258
259

    // Calculate the first computed value.

peastman's avatar
peastman committed
260
261
262
263
264
    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
265
266
267
268
269
    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();
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    
    // 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
284

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

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

            // 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];
                }
            }
313
        }
peastman's avatar
peastman committed
314
315
    }
    threads.syncThreads();
316
317
318

    // Now calculate the energy and its derivatives.

peastman's avatar
peastman committed
319
320
321
322
323
    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
324
    for (int termIndex = 0; termIndex < (int) data.energyExpressions.size(); termIndex++) {
325
        if (energyTypes[termIndex] == CustomGBForce::SingleParticle)
peastman's avatar
peastman committed
326
            calculateSingleParticleEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, forces, energy);
327
        else if (energyTypes[termIndex] == CustomGBForce::ParticlePair)
peastman's avatar
peastman committed
328
            calculateParticlePairEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, true, forces, energy, boxSize, invBoxSize);
329
        else
peastman's avatar
peastman committed
330
331
332
333
334
335
336
337
338
            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
339
340
            for (auto& data : threadData)
                sum += data->dEdV[i][atom];
peastman's avatar
peastman committed
341
342
            dEdV[i][atom] = sum;
        }
343
    }
peastman's avatar
peastman committed
344
    threads.syncThreads();
345
346
347

    // Apply the chain rule to evaluate forces.

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

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

peastman's avatar
peastman committed
359
        while (true) {
peastman's avatar
peastman committed
360
            int blockIndex = atomicCounter++;
peastman's avatar
peastman committed
361
362
            if (blockIndex >= neighborList->getNumBlocks())
                break;
363
364
            const int blockSize = neighborList->getBlockSize();
            const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
365
366
367
368
            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];
369
                for (int k = 0; k < blockSize; k++) {
370
371
372
373
                    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
374
375
                        calculateOnePairValue(index, first, second, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
                        calculateOnePairValue(index, second, first, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
376
377
378
                    }
                }
            }
379
380
381
382
383
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

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

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

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

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

peastman's avatar
peastman committed
454
        while (true) {
peastman's avatar
peastman committed
455
            int blockIndex = atomicCounter++;
peastman's avatar
peastman committed
456
457
            if (blockIndex >= neighborList->getNumBlocks())
                break;
458
459
            const int blockSize = neighborList->getBlockSize();
            const int* blockAtom = &neighborList->getSortedAtoms()[blockSize*blockIndex];
460
461
462
463
            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];
464
                for (int k = 0; k < blockSize; k++) {
465
466
467
468
                    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
469
                        calculateOnePairEnergyTerm(index, first, second, data, posq, atomParameters, forces, totalEnergy, boxSize, invBoxSize);
470
471
472
                    }
                }
            }
473
474
475
476
477
        }
    }
    else {
        // Perform an O(N^2) loop over all atom pairs.

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

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

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

    // Record variables for evaluating expressions.

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

    // Evaluate the energy and its derivatives.

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

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

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

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

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

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

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

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

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

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

    // Record variables for evaluating expressions.

629
    for (int i = 0; i < numParams; i++) {
630
631
632
633
634
635
636
637
638
639
        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];
640
641
642

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

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

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;
673
    }
674
    r2 = dot3(deltaR, deltaR);
675
}