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

peastman's avatar
peastman committed
179
void CpuCustomGBForce::setPeriodic(Vec3& boxSize) {
180
181
182
183
184
185
186
187
188
189
190
    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];
  }

peastman's avatar
peastman committed
191
void CpuCustomGBForce::calculateIxn(int numberOfAtoms, float* posq, double** 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
void CpuCustomGBForce::calculateParticlePairValue(int index, ThreadData& data, int numAtoms, float* posq, double** atomParameters,
peastman's avatar
peastman committed
366
        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
void CpuCustomGBForce::calculateOnePairValue(int index, int atom1, int atom2, ThreadData& data, float* posq, double** atomParameters,
peastman's avatar
peastman committed
413
        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
void CpuCustomGBForce::calculateSingleParticleEnergyTerm(int index, ThreadData& data, int numAtoms, float* posq,
peastman's avatar
peastman committed
439
        double** atomParameters, float* forces, double& totalEnergy) {
peastman's avatar
peastman committed
440
    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
void CpuCustomGBForce::calculateParticlePairEnergyTerm(int index, ThreadData& data, int numAtoms, float* posq, double** atomParameters,
peastman's avatar
peastman committed
464
        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
void CpuCustomGBForce::calculateOnePairEnergyTerm(int index, int atom1, int atom2, ThreadData& data, float* posq, double** atomParameters,
peastman's avatar
peastman committed
506
        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
517
    float r = sqrtf(r2);
    data.r = r;
518
519
520

    // Record variables for evaluating expressions.

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

    // Evaluate the energy and its derivatives.

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

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

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

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

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

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

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

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

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

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

    // Record variables for evaluating expressions.

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

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

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

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