ReferenceCustomManyParticleIxn.cpp 9.55 KB
Newer Older
1
/* Portions copyright (c) 2009-2021 Stanford University and Simbios.
2
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 <utility>

#include "SimTKOpenMMUtilities.h"
#include "ReferenceForce.h"
#include "ReferenceCustomManyParticleIxn.h"
31
#include "ReferencePointFunctions.h"
32
33
34
35
36
37
38
39
40
41
#include "ReferenceTabulatedFunction.h"
#include "openmm/internal/CustomManyParticleForceImpl.h"
#include "lepton/CustomFunction.h"

using namespace OpenMM;
using namespace std;

ReferenceCustomManyParticleIxn::ReferenceCustomManyParticleIxn(const CustomManyParticleForce& force) : useCutoff(false), usePeriodic(false) {
    numParticlesPerSet = force.getNumParticlesPerSet();
    numPerParticleParameters = force.getNumPerParticleParameters();
42
    centralParticleMode = (force.getPermutationMode() == CustomManyParticleForce::UniqueCentralParticle);
43
44
45
46
47
48
49
    
    // Create custom functions for the tabulated functions.

    map<string, Lepton::CustomFunction*> functions;
    for (int i = 0; i < (int) force.getNumTabulatedFunctions(); i++)
        functions[force.getTabulatedFunctionName(i)] = createReferenceTabulatedFunction(force.getTabulatedFunction(i));

50
51
52
53
54
55
    // Create implementations of point functions.

    functions["pointdistance"] = new ReferencePointDistanceFunction(force.usesPeriodicBoundaryConditions(), &periodicBoxVectors);
    functions["pointangle"] = new ReferencePointAngleFunction(force.usesPeriodicBoundaryConditions(), &periodicBoxVectors);
    functions["pointdihedral"] = new ReferencePointDihedralFunction(force.usesPeriodicBoundaryConditions(), &periodicBoxVectors);

56
57
    // Parse the expression and create the object used to calculate the interaction.

58
    Lepton::ParsedExpression energyExpr = CustomManyParticleForceImpl::prepareExpression(force, functions);
59
60
61
62
63
64
    energyExpression = energyExpr.createProgram();
    if (force.getNonbondedMethod() != CustomManyParticleForce::NoCutoff)
        setUseCutoff(force.getCutoffDistance());

    // Delete the custom functions.

peastman's avatar
peastman committed
65
66
    for (auto& function : functions)
        delete function.second;
67
68

    // Differentiate the energy to get expressions for the force.
69
70
71
72
73
74
75

    particleParamNames.resize(numParticlesPerSet);
    for (int i = 0; i < numParticlesPerSet; i++) {
        stringstream xname, yname, zname;
        xname << 'x' << (i+1);
        yname << 'y' << (i+1);
        zname << 'z' << (i+1);
76
77
78
        particleTerms.push_back(ReferenceCustomManyParticleIxn::ParticleTermInfo(xname.str(), i, 0, energyExpr.differentiate(xname.str()).optimize().createProgram()));
        particleTerms.push_back(ReferenceCustomManyParticleIxn::ParticleTermInfo(yname.str(), i, 1, energyExpr.differentiate(yname.str()).optimize().createProgram()));
        particleTerms.push_back(ReferenceCustomManyParticleIxn::ParticleTermInfo(zname.str(), i, 2, energyExpr.differentiate(zname.str()).optimize().createProgram()));
79
80
        for (int j = 0; j < numPerParticleParameters; j++) {
            stringstream paramname;
81
            paramname << force.getPerParticleParameterName(j) << (i+1);
82
83
84
            particleParamNames[i].push_back(paramname.str());
        }
    }
85
86
87
88
89
90
91
92
93
94
    
    // Record exclusions.
    
    exclusions.resize(force.getNumParticles());
    for (int i = 0; i < (int) force.getNumExclusions(); i++) {
        int p1, p2;
        force.getExclusionParticles(i, p1, p2);
        exclusions[p1].insert(p2);
        exclusions[p2].insert(p1);
    }
95
96
97
98
    
    // Record information about type filters.
    
    CustomManyParticleForceImpl::buildFilterArrays(force, numTypes, particleTypes, orderIndex, particleOrder);
99
100
}

101
ReferenceCustomManyParticleIxn::~ReferenceCustomManyParticleIxn() {
102
103
}

104
void ReferenceCustomManyParticleIxn::calculateIxn(vector<Vec3>& atomCoordinates, vector<vector<double> >& particleParameters,
peastman's avatar
peastman committed
105
106
                                                  const map<string, double>& globalParameters, vector<Vec3>& forces,
                                                  double* totalEnergy) const {
107
108
109
110
111
    map<string, double> variables = globalParameters;
    vector<int> particles(numParticlesPerSet);
    loopOverInteractions(particles, 0, atomCoordinates, particleParameters, variables, forces, totalEnergy);
}

peastman's avatar
peastman committed
112
void ReferenceCustomManyParticleIxn::setUseCutoff(double distance) {
113
114
115
116
    useCutoff = true;
    cutoffDistance = distance;
}

peastman's avatar
peastman committed
117
void ReferenceCustomManyParticleIxn::setPeriodic(Vec3* vectors) {
118
    assert(useCutoff);
119
120
121
    assert(vectors[0][0] >= 2.0*cutoffDistance);
    assert(vectors[1][1] >= 2.0*cutoffDistance);
    assert(vectors[2][2] >= 2.0*cutoffDistance);
122
    usePeriodic = true;
123
    periodicBoxVectors = vectors;
124
125
}

peastman's avatar
peastman committed
126
void ReferenceCustomManyParticleIxn::loopOverInteractions(vector<int>& particles, int loopIndex, vector<OpenMM::Vec3>& atomCoordinates,
127
                                                          vector<vector<double> >& particleParameters, map<string, double>& variables, vector<OpenMM::Vec3>& forces,
peastman's avatar
peastman committed
128
                                                          double* totalEnergy) const {
129
    int numParticles = atomCoordinates.size();
130
131
    int firstPartialLoop = (centralParticleMode ? 2 : 1);
    int start = (loopIndex < firstPartialLoop ? 0 : particles[loopIndex-1]+1);
132
    for (int i = start; i < numParticles; i++) {
133
134
        if (loopIndex > 0 && i == particles[0])
            continue;
135
136
        particles[loopIndex] = i;
        if (loopIndex == numParticlesPerSet-1)
137
            calculateOneIxn(particles, atomCoordinates, particleParameters, variables, forces, totalEnergy);
138
139
140
141
142
        else
            loopOverInteractions(particles, loopIndex+1, atomCoordinates, particleParameters, variables, forces, totalEnergy);
    }
}

peastman's avatar
peastman committed
143
void ReferenceCustomManyParticleIxn::calculateOneIxn(const vector<int>& particles, vector<Vec3>& atomCoordinates,
144
                        vector<vector<double> >& particleParameters, map<string, double>& variables, vector<Vec3>& forces, double* totalEnergy) const {
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    // Select the ordering to use for the particles.
    
    vector<int> permutedParticles(numParticlesPerSet);
    if (particleOrder.size() == 1) {
        // There are no filters, so we don't need to worry about ordering.
        
        permutedParticles = particles;
    }
    else {
        int index = 0;
        for (int i = numParticlesPerSet-1; i >= 0; i--)
            index = particleTypes[particles[i]]+numTypes*index;
        int order = orderIndex[index];
        if (order == -1)
            return;
        for (int i = 0; i < numParticlesPerSet; i++)
            permutedParticles[i] = particles[particleOrder[order][i]];
    }
    
164
165
    // Decide whether to include this interaction.
    
166
    for (int i = 0; i < numParticlesPerSet; i++) {
167
        int p1 = permutedParticles[i];
168
        for (int j = i+1; j < numParticlesPerSet; j++) {
169
            int p2 = permutedParticles[j];
170
171
            if (exclusions[p1].find(p2) != exclusions[p1].end())
                return;
172
            if (useCutoff && (i == 0 || !centralParticleMode)) {
peastman's avatar
peastman committed
173
                double delta[ReferenceForce::LastDeltaRIndex];
174
175
176
177
178
179
                computeDelta(p1, p2, delta, atomCoordinates);
                if (delta[ReferenceForce::RIndex] >= cutoffDistance)
                    return;
            }
        }
    }
180
181
182
183
184
185

    // Record per-particle parameters.
    
    for (int i = 0; i < numParticlesPerSet; i++)
        for (int j = 0; j < numPerParticleParameters; j++)
            variables[particleParamNames[i][j]] = particleParameters[permutedParticles[i]][j];
186
    
187
    // Record particle coordinates.
188

peastman's avatar
peastman committed
189
    for (auto& term : particleTerms)
190
        variables[term.name] = atomCoordinates[permutedParticles[term.atom]][term.component];
191

192
    // Apply forces based on particle coordinates.
193

194
195
    for (auto& term : particleTerms)
        forces[permutedParticles[term.atom]][term.component] -= term.forceExpression.evaluate(variables);
196
197
198
199

    // Add the energy

    if (totalEnergy)
peastman's avatar
peastman committed
200
        *totalEnergy += energyExpression.evaluate(variables);
201
202
}

peastman's avatar
peastman committed
203
void ReferenceCustomManyParticleIxn::computeDelta(int atom1, int atom2, double* delta, vector<Vec3>& atomCoordinates) const {
204
    if (usePeriodic)
205
        ReferenceForce::getDeltaRPeriodic(atomCoordinates[atom1], atomCoordinates[atom2], periodicBoxVectors, delta);
206
207
208
209
    else
        ReferenceForce::getDeltaR(atomCoordinates[atom1], atomCoordinates[atom2], delta);
}

peastman's avatar
peastman committed
210
211
212
213
double ReferenceCustomManyParticleIxn::computeAngle(double* vec1, double* vec2) {
    double dot = DOT3(vec1, vec2);
    double cosine = dot/sqrt((vec1[ReferenceForce::R2Index]*vec2[ReferenceForce::R2Index]));
    double angle;
214
215
216
217
218
    if (cosine >= 1)
        angle = 0;
    else if (cosine <= -1)
        angle = PI_M;
    else
peastman's avatar
peastman committed
219
        angle = acos(cosine);
220
221
    return angle;
}