CpuCustomGBForce.cpp 31.1 KB
Newer Older
1

2
/* Portions copyright (c) 2009-2016 Stanford University and Simbios.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
40
41
42
43
44
45
46
47
48
49
class CpuCustomGBForce::ComputeForceTask : public ThreadPool::Task {
public:
    ComputeForceTask(CpuCustomGBForce& owner) : owner(owner) {
    }
    void execute(ThreadPool& threads, int threadIndex) {
        owner.threadComputeForce(threads, threadIndex);
    }
    CpuCustomGBForce& owner;
};

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

peastman's avatar
peastman committed
138
139
140
141
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,
142
                     const vector<vector<Lepton::CompiledExpression> >& valueParamDerivExpressions,
peastman's avatar
peastman committed
143
144
145
146
147
                     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,
148
                     const vector<vector<Lepton::CompiledExpression> >& energyParamDerivExpressions,
peastman's avatar
peastman committed
149
150
                     const vector<CustomGBForce::ComputationType>& energyTypes,
                     const vector<string>& parameterNames, ThreadPool& threads) :
151
152
            exclusions(exclusions), cutoff(false), periodic(false), valueTypes(valueTypes), energyTypes(energyTypes), numValues(valueNames.size()),
            numParams(parameterNames.size()), threads(threads) {
peastman's avatar
peastman committed
153
    for (int i = 0; i < threads.getNumThreads(); i++)
154
155
156
157
        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
158
159
160
161
    for (int i = 0; i < (int) values.size(); i++) {
        values[i].resize(numAtoms);
        dEdV[i].resize(numAtoms);
    }
162
163
164
    dValuedParam.resize(numValues);
    for (int i = 0; i < numValues; i++)
        dValuedParam[i].resize(valueParamDerivExpressions[0].size(), vector<float>(numAtoms));
peastman's avatar
peastman committed
165
166
}

167
CpuCustomGBForce::~CpuCustomGBForce() {
peastman's avatar
peastman committed
168
169
    for (int i = 0; i < (int) threadData.size(); i++)
        delete threadData[i];
170
171
}

172
void CpuCustomGBForce::setUseCutoff(float distance, const CpuNeighborList& neighbors) {
173
174
    cutoff = true;
    cutoffDistance = distance;
175
    cutoffDistance2 = distance*distance;
176
    neighborList = &neighbors;
177
  }
178
179
180
181
182
183
184
185
186
187
188
189
190

void CpuCustomGBForce::setPeriodic(RealVec& boxSize) {
    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];
  }

191
void CpuCustomGBForce::calculateIxn(int numberOfAtoms, float* posq, RealOpenMM** atomParameters,
peastman's avatar
peastman committed
192
                                           map<string, double>& globalParameters, vector<AlignedArray<float> >& threadForce,
193
                                           bool includeForce, bool includeEnergy, double& totalEnergy, double* energyParamDerivs) {
peastman's avatar
peastman committed
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
    // Record the parameters for the threads.
    
    this->numberOfAtoms = numberOfAtoms;
    this->posq = posq;
    this->atomParameters = atomParameters;
    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.

    ComputeForceTask task(*this);
    gmx_atomic_set(&counter, 0);
    threads.execute(task);
    threads.waitForThreads();
213
214
215
216
217
218
219
220

    // 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
221
222
223
224
225
    
    // Calculate the remaining computed values.
    
    threads.resumeThreads();
    threads.waitForThreads();
226
    
peastman's avatar
peastman committed
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
    // 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];
    }
253
254
    if (hasParamDerivs)
        for (int i = 0; i < threads.getNumThreads(); i++)
Peter Eastman's avatar
Peter Eastman committed
255
            for (int j = 0; j < threadData[i]->energyParamDerivs.size(); j++)
256
                energyParamDerivs[j] += threadData[i]->energyParamDerivs[j];
peastman's avatar
peastman committed
257
258
259
260
261
262
263
264
265
266
}

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];
267
268
    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
269
270
271
272
273
274
275
    for (map<string, double>::const_iterator iter = globalParameters->begin(); iter != globalParameters->end(); ++iter)
        data.expressionSet.setVariable(data.expressionSet.getVariableIndex(iter->first), iter->second);

    // Calculate the first computed value.

    for (int i = 0; i < (int) data.value0.size(); i++)
        data.value0[i] = 0.0f;
276
277
278
    for (int i = 0; i < (int) data.dValue0dParam.size(); i++)
        for (int j = 0; j < (int) data.dValue0dParam[i].size(); j++)
            data.dValue0dParam[i][j] = 0.0;
peastman's avatar
peastman committed
279
280
281
282
283
    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();
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    
    // 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
298

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

301
    int numValues = valueTypes.size();
peastman's avatar
peastman committed
302
303
304
305
306
    for (int atom = data.firstAtom; atom < data.lastAtom; atom++) {
        float sum = 0.0f;
        for (int j = 0; j < (int) threadData.size(); j++)
            sum += threadData[j]->value0[atom];
        values[0][atom] = sum;
307
308
309
        data.x = posq[4*atom];
        data.y = posq[4*atom+1];
        data.z = posq[4*atom+2];
310
        for (int j = 0; j < numParams; j++)
311
            data.param[j] = atomParameters[atom][j];
312
        for (int i = 1; i < numValues; i++) {
313
            data.value[i-1] = values[i-1][atom];
314
            values[i][atom] = (float) data.valueExpressions[i].evaluate();
315
316
317
318
319
320
321
322
323
324
325
326

            // 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];
                }
            }
327
        }
peastman's avatar
peastman committed
328
329
    }
    threads.syncThreads();
330
331
332

    // Now calculate the energy and its derivatives.

peastman's avatar
peastman committed
333
334
    for (int i = 0; i < (int) data.dEdV.size(); i++)
        for (int j = 0; j < (int) data.dEdV[i].size(); j++)
335
336
337
            data.dEdV[i][j] = 0.0f;
    for (int i = 0; i < (int) data.energyParamDerivs.size(); i++)
        data.energyParamDerivs[i] = 0.0f;
peastman's avatar
peastman committed
338
    for (int termIndex = 0; termIndex < (int) data.energyExpressions.size(); termIndex++) {
339
        if (energyTypes[termIndex] == CustomGBForce::SingleParticle)
peastman's avatar
peastman committed
340
            calculateSingleParticleEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, forces, energy);
341
        else if (energyTypes[termIndex] == CustomGBForce::ParticlePair)
peastman's avatar
peastman committed
342
            calculateParticlePairEnergyTerm(termIndex, data, numberOfAtoms, posq, atomParameters, true, forces, energy, boxSize, invBoxSize);
343
        else
peastman's avatar
peastman committed
344
345
346
347
348
349
350
351
352
353
354
355
356
            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;
            for (int j = 0; j < (int) threadData.size(); j++)
                sum += threadData[j]->dEdV[i][atom];
            dEdV[i][atom] = sum;
        }
357
    }
peastman's avatar
peastman committed
358
    threads.syncThreads();
359
360
361

    // Apply the chain rule to evaluate forces.

peastman's avatar
peastman committed
362
    calculateChainRuleForces(data, numberOfAtoms, posq, atomParameters, forces, boxSize, invBoxSize);
363
364
}

peastman's avatar
peastman committed
365
366
void CpuCustomGBForce::calculateParticlePairValue(int index, ThreadData& data, int numAtoms, float* posq, RealOpenMM** atomParameters,
        bool useExclusions, const fvec4& boxSize, const fvec4& invBoxSize) {
367
    for (int i = 0; i < numAtoms; i++)
368
        values[index][i] = 0.0f;
peastman's avatar
peastman committed
369
    vector<float>& valueArray = (index == 0 ? data.value0 : values[index]);
370
371
372
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

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

peastman's avatar
peastman committed
398
399
400
401
        while (true) {
            int i = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
            if (i >= numAtoms)
                break;
402
403
404
            for (int j = i+1; j < numAtoms; j++) {
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
peastman's avatar
peastman committed
405
406
                calculateOnePairValue(index, i, j, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
                calculateOnePairValue(index, j, i, data, posq, atomParameters, valueArray, boxSize, invBoxSize);
407
408
409
410
411
           }
        }
    }
}

peastman's avatar
peastman committed
412
413
void CpuCustomGBForce::calculateOnePairValue(int index, int atom1, int atom2, ThreadData& data, float* posq, RealOpenMM** atomParameters,
        vector<float>& valueArray, const fvec4& boxSize, const fvec4& invBoxSize) {
414
415
416
417
418
    fvec4 deltaR;
    fvec4 pos1(posq+4*atom1);
    fvec4 pos2(posq+4*atom2);
    float r2;
    getDeltaR(pos2, pos1, deltaR, r2, periodic, boxSize, invBoxSize);
419
    if (cutoff && r2 >= cutoffDistance2)
420
        return;
421
    data.r = sqrtf(r2);
422
    for (int i = 0; i < numParams; i++) {
423
424
        data.particleParam[i*2] = atomParameters[atom1][i];
        data.particleParam[i*2+1] = atomParameters[atom2][i];
425
426
    }
    for (int i = 0; i < index; i++) {
427
428
        data.particleValue[i*2] = values[i][atom1];
        data.particleValue[i*2+1] = values[i][atom2];
429
    }
peastman's avatar
peastman committed
430
    valueArray[atom1] += (float) data.valueExpressions[index].evaluate();
431
432
433
434
435
    
    // 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();
436
437
}

peastman's avatar
peastman committed
438
439
440
void CpuCustomGBForce::calculateSingleParticleEnergyTerm(int index, ThreadData& data, int numAtoms, float* posq,
        RealOpenMM** atomParameters, float* forces, double& totalEnergy) {
    for (int i = data.firstAtom; i < data.lastAtom; i++) {
441
442
443
        data.x = posq[4*i];
        data.y = posq[4*i+1];
        data.z = posq[4*i+2];
444
        for (int j = 0; j < numParams; j++)
445
            data.param[j] = atomParameters[i][j];
446
        for (int j = 0; j < (int) values.size(); j++)
447
            data.value[j] = values[j][i];
peastman's avatar
peastman committed
448
449
        if (includeEnergy)
            totalEnergy += (float) data.energyExpressions[index].evaluate();
450
        for (int j = 0; j < (int) values.size(); j++)
peastman's avatar
peastman committed
451
452
453
454
            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();
455
456
457
458
459
        
        // Compute derivatives with respect to parameters.
        
        for (int k = 0; k < data.energyParamDerivExpressions[index].size(); k++)
            data.energyParamDerivs[k] += data.energyParamDerivExpressions[index][k].evaluate();
460
461
462
    }
}

peastman's avatar
peastman committed
463
464
void CpuCustomGBForce::calculateParticlePairEnergyTerm(int index, ThreadData& data, int numAtoms, float* posq, RealOpenMM** atomParameters,
        bool useExclusions, float* forces, double& totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize) {
465
466
467
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

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

peastman's avatar
peastman committed
492
493
494
495
        while (true) {
            int i = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
            if (i >= numAtoms)
                break;
496
497
498
            for (int j = i+1; j < numAtoms; j++) {
                if (useExclusions && exclusions[i].find(j) != exclusions[i].end())
                    continue;
peastman's avatar
peastman committed
499
                calculateOnePairEnergyTerm(index, i, j, data, posq, atomParameters, forces, totalEnergy, boxSize, invBoxSize);
500
501
502
503
504
           }
        }
    }
}

peastman's avatar
peastman committed
505
506
void CpuCustomGBForce::calculateOnePairEnergyTerm(int index, int atom1, int atom2, ThreadData& data, float* posq, RealOpenMM** atomParameters,
        float* forces, double& totalEnergy, const fvec4& boxSize, const fvec4& invBoxSize) {
507
508
    // Compute the displacement.

509
510
511
512
513
    fvec4 deltaR;
    fvec4 pos1(posq+4*atom1);
    fvec4 pos2(posq+4*atom2);
    float r2;
    getDeltaR(pos2, pos1, deltaR, r2, periodic, boxSize, invBoxSize);
514
    if (cutoff && r2 >= cutoffDistance2)
515
        return;
516
    data.r = sqrtf(r2);
517
518
519

    // Record variables for evaluating expressions.

520
    for (int i = 0; i < numParams; i++) {
521
522
        data.particleParam[i*2] = atomParameters[atom1][i];
        data.particleParam[i*2+1] = atomParameters[atom2][i];
523
    }
524
    for (int i = 0; i < (int) values.size(); i++) {
525
526
        data.particleValue[i*2] = values[i][atom1];
        data.particleValue[i*2+1] = values[i][atom2];
527
528
529
530
    }

    // Evaluate the energy and its derivatives.

peastman's avatar
peastman committed
531
532
533
    if (includeEnergy)
        totalEnergy += (float) data.energyExpressions[index].evaluate();
    float dEdR = (float) data.energyDerivExpressions[index][0].evaluate();
534
    dEdR *= 1/data.r;
535
536
537
    fvec4 result = deltaR*dEdR;
    (fvec4(forces+4*atom1)-result).store(forces+4*atom1);
    (fvec4(forces+4*atom2)+result).store(forces+4*atom2);
538
    for (int i = 0; i < (int) values.size(); i++) {
peastman's avatar
peastman committed
539
540
        data.dEdV[i][atom1] += (float) data.energyDerivExpressions[index][2*i+1].evaluate();
        data.dEdV[i][atom2] += (float) data.energyDerivExpressions[index][2*i+2].evaluate();
541
    }
542
543
544
545
546
        
    // Compute derivatives with respect to parameters.

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

peastman's avatar
peastman committed
549
550
void CpuCustomGBForce::calculateChainRuleForces(ThreadData& data, int numAtoms, float* posq, RealOpenMM** atomParameters,
        float* forces, const fvec4& boxSize, const fvec4& invBoxSize) {
551
552
553
    if (cutoff) {
        // Loop over all pairs in the neighbor list.

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

peastman's avatar
peastman committed
578
579
580
581
        while (true) {
            int i = gmx_atomic_fetch_add(reinterpret_cast<gmx_atomic_t*>(atomicCounter), 1);
            if (i >= numAtoms)
                break;
582
583
            for (int j = i+1; j < numAtoms; j++) {
                bool isExcluded = (exclusions[i].find(j) != exclusions[i].end());
peastman's avatar
peastman committed
584
585
                calculateOnePairChainRule(i, j, data, posq, atomParameters, forces, isExcluded, boxSize, invBoxSize);
                calculateOnePairChainRule(j, i, data, posq, atomParameters, forces, isExcluded, boxSize, invBoxSize);
586
587
588
589
590
591
           }
        }
    }

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

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

    for (int i = data.firstAtom; i < data.lastAtom; i++)
621
        for (int j = 0; j < data.value.size(); j++)
622
623
            for (int k = 0; k < dValuedParam[j].size(); k++)
                data.energyParamDerivs[k] += dEdV[j][i]*dValuedParam[j][k][i];
624
625
}

peastman's avatar
peastman committed
626
627
void CpuCustomGBForce::calculateOnePairChainRule(int atom1, int atom2, ThreadData& data, float* posq, RealOpenMM** atomParameters,
        float* forces, bool isExcluded, const fvec4& boxSize, const fvec4& invBoxSize) {
628
629
    // Compute the displacement.

630
631
632
633
634
    fvec4 deltaR;
    fvec4 pos1(posq+4*atom1);
    fvec4 pos2(posq+4*atom2);
    float r2;
    getDeltaR(pos2, pos1, deltaR, r2, periodic, boxSize, invBoxSize);
635
    if (cutoff && r2 >= cutoffDistance2)
636
        return;
637
    data.r = sqrtf(r2);
638
639
640

    // Record variables for evaluating expressions.

641
    for (int i = 0; i < numParams; i++) {
642
643
644
645
646
647
648
649
650
651
        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];
652
653
654

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

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

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;
685
    }
686
    r2 = dot3(deltaR, deltaR);
687
}