"csrc/vscode:/vscode.git/clone" did not exist on "dcaf73e5ffbbf3e2de6a4ed3e070740158f76ab3"
CpuCustomGBForce.cpp 29.9 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
 * 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"
31
#include "openmm/internal/gmx_atomic.h"
32

33
34
using namespace OpenMM;
using namespace std;
35

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

peastman's avatar
peastman committed
128
129
130
131
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,
132
                     const vector<vector<Lepton::CompiledExpression> >& valueParamDerivExpressions,
peastman's avatar
peastman committed
133
134
135
136
137
                     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,
138
                     const vector<vector<Lepton::CompiledExpression> >& energyParamDerivExpressions,
peastman's avatar
peastman committed
139
140
                     const vector<CustomGBForce::ComputationType>& energyTypes,
                     const vector<string>& parameterNames, ThreadPool& threads) :
141
142
            exclusions(exclusions), cutoff(false), periodic(false), valueTypes(valueTypes), energyTypes(energyTypes), numValues(valueNames.size()),
            numParams(parameterNames.size()), threads(threads) {
peastman's avatar
peastman committed
143
    for (int i = 0; i < threads.getNumThreads(); i++)
144
145
146
147
        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
148
149
150
151
    for (int i = 0; i < (int) values.size(); i++) {
        values[i].resize(numAtoms);
        dEdV[i].resize(numAtoms);
    }
152
153
154
    dValuedParam.resize(numValues);
    for (int i = 0; i < numValues; i++)
        dValuedParam[i].resize(valueParamDerivExpressions[0].size(), vector<float>(numAtoms));
peastman's avatar
peastman committed
155
156
}

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

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

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

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

    // Calculate the first computed value.

peastman's avatar
peastman committed
199
    auto task = [&] (ThreadPool& threads, int threadIndex) { threadComputeForce(threads, threadIndex); };
peastman's avatar
peastman committed
200
201
202
    gmx_atomic_set(&counter, 0);
    threads.execute(task);
    threads.waitForThreads();
203
204
205
206
207
208
209
210

    // 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
211
212
213
214
215
    
    // Calculate the remaining computed values.
    
    threads.resumeThreads();
    threads.waitForThreads();
216
    
peastman's avatar
peastman committed
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
    // Calculate the energy terms.

    for (int i = 0; i < (int) threadData[0]->energyExpressions.size(); i++) {
        gmx_atomic_set(&counter, 0);
        threads.execute(task);
        threads.waitForThreads();
    }

    // Sum the energy derivatives.

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

    gmx_atomic_set(&counter, 0);
    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];
    }
243
244
    if (hasParamDerivs)
        for (int i = 0; i < threads.getNumThreads(); i++)
Peter Eastman's avatar
Peter Eastman committed
245
            for (int j = 0; j < threadData[i]->energyParamDerivs.size(); j++)
246
                energyParamDerivs[j] += threadData[i]->energyParamDerivs[j];
peastman's avatar
peastman committed
247
248
249
250
251
252
253
254
255
256
}

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];
257
258
    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
259
260
    for (auto& param : *globalParameters)
        data.expressionSet.setVariable(data.expressionSet.getVariableIndex(param.first), param.second);
peastman's avatar
peastman committed
261
262
263

    // Calculate the first computed value.

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

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

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

            // 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];
                }
            }
317
        }
peastman's avatar
peastman committed
318
319
    }
    threads.syncThreads();
320
321
322

    // Now calculate the energy and its derivatives.

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

    // Apply the chain rule to evaluate forces.

peastman's avatar
peastman committed
352
    calculateChainRuleForces(data, numberOfAtoms, posq, atomParameters, forces, boxSize, invBoxSize);
353
354
}

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

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

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

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

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

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

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

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

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

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

    // Record variables for evaluating expressions.

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

    // Evaluate the energy and its derivatives.

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

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

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

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

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

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

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

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

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

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

    // Record variables for evaluating expressions.

633
    for (int i = 0; i < numParams; i++) {
634
635
636
637
638
639
640
641
642
643
        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];
644
645
646

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

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

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;
677
    }
678
    r2 = dot3(deltaR, deltaR);
679
}