ReferenceCCMAAlgorithm.cpp 12.7 KB
Newer Older
1

2
/* Portions copyright (c) 2006-2019 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
 * Contributors: Peter Eastman, Pande Group
 *
 * 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>

28
#include "SimTKOpenMMUtilities.h"
29
#include "ReferenceCCMAAlgorithm.h"
30
#include "ReferenceDynamics.h"
31
#include "quern.h"
32
#include "openmm/OpenMMException.h"
33
#include "openmm/Vec3.h"
34
#include "openmm/internal/ThreadPool.h"
35
#include <map>
36
#include <set>
37
#include <utility>
38

39
using namespace OpenMM;
40
using namespace std;
41

42
ReferenceCCMAAlgorithm::ReferenceCCMAAlgorithm(int numberOfAtoms,
43
44
                                               int numberOfConstraints,
                                               const vector<pair<int, int> >& atomIndices,
peastman's avatar
peastman committed
45
46
                                               const vector<double>& distance,
                                               vector<double>& masses,
47
                                               vector<AngleInfo>& angles,
peastman's avatar
peastman committed
48
                                               double elementCutoff) {
49
    _numberOfConstraints = numberOfConstraints;
50
    _elementCutoff = elementCutoff;
51
52
    _atomIndices = atomIndices;
    _distance = distance;
53

54
55
    _maximumNumberOfIterations = 150;
    _hasInitializedMasses = false;
56

57
    // work arrays
58
59

    if (_numberOfConstraints > 0) {
Peter Eastman's avatar
Peter Eastman committed
60
61
62
63
        r_ij.resize(numberOfConstraints);
        d_ij2.resize(numberOfConstraints);
        _distanceTolerance.resize(numberOfConstraints);
        reducedMasses.resize(numberOfConstraints);
64
65
66
67
68
69
70
71
72
    }
    if (numberOfConstraints > 0)
    {
        // Compute the constraint coupling matrix

        vector<vector<int> > atomAngles(numberOfAtoms);
        for (int i = 0; i < (int) angles.size(); i++)
            atomAngles[angles[i].atom2].push_back(i);
        vector<vector<pair<int, double> > > matrix(numberOfConstraints);
73
        vector<set<int> > atomConstraints(numberOfAtoms);
74
        for (int j = 0; j < numberOfConstraints; j++) {
75
76
77
78
79
80
81
82
83
84
85
            atomConstraints[_atomIndices[j].first].insert(j);
            atomConstraints[_atomIndices[j].second].insert(j);
        }
        for (int j = 0; j < numberOfConstraints; j++) {
            int atomj0 = _atomIndices[j].first;
            int atomj1 = _atomIndices[j].second;
            double invMass0 = 1/masses[atomj0];
            double invMass1 = 1/masses[atomj1];
            set<int> constraints = atomConstraints[atomj0];
            constraints.insert(atomConstraints[atomj1].begin(), atomConstraints[atomj1].end());
            for (int k : constraints) {
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
                if (j == k) {
                    matrix[j].push_back(pair<int, double>(j, 1.0));
                    continue;
                }
                double scale;
                int atomk0 = _atomIndices[k].first;
                int atomk1 = _atomIndices[k].second;
                int atoma, atomb, atomc;
                if (atomj0 == atomk0) {
                    atoma = atomj1;
                    atomb = atomj0;
                    atomc = atomk1;
                    scale = invMass0/(invMass0+invMass1);
                }
                else if (atomj1 == atomk1) {
                    atoma = atomj0;
                    atomb = atomj1;
                    atomc = atomk0;
                    scale = invMass1/(invMass0+invMass1);
                }
                else if (atomj0 == atomk1) {
                    atoma = atomj1;
                    atomb = atomj0;
                    atomc = atomk0;
                    scale = invMass0/(invMass0+invMass1);
                }
                else if (atomj1 == atomk0) {
                    atoma = atomj0;
                    atomb = atomj1;
                    atomc = atomk1;
                    scale = invMass1/(invMass0+invMass1);
                }
                else
                    continue; // These constraints are not connected.

                // Look for a third constraint forming a triangle with these two.

                bool foundConstraint = false;
124
125
                for (int other : atomConstraints[atoma]) {
                    if (_atomIndices[other].first == atomc || _atomIndices[other].second == atomc) {
126
127
128
129
130
131
132
133
134
135
136
137
                        double d1 = _distance[j];
                        double d2 = _distance[k];
                        double d3 = _distance[other];
                        matrix[j].push_back(pair<int, double>(k, scale*(d1*d1+d2*d2-d3*d3)/(2.0*d1*d2)));
                        foundConstraint = true;
                        break;
                    }
                }
                if (!foundConstraint) {
                    // We didn't find one, so look for an angle force field term.

                    const vector<int>& angleCandidates = atomAngles[atomb];
peastman's avatar
peastman committed
138
139
                    for (int candidate : angleCandidates) {
                        const AngleInfo& angle = angles[candidate];
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
                        if ((angle.atom1 == atoma && angle.atom3 == atomc) || (angle.atom3 == atoma && angle.atom1 == atomc)) {
                            matrix[j].push_back(pair<int, double>(k, scale*cos(angle.angle)));
                            break;
                        }
                    }
                }
            }
        }

        // Invert it using QR.

        vector<int> matrixRowStart;
        vector<int> matrixColIndex;
        vector<double> matrixValue;
        for (int i = 0; i < numberOfConstraints; i++) {
            matrixRowStart.push_back(matrixValue.size());
peastman's avatar
peastman committed
156
            for (auto& element : matrix[i]) {
157
158
159
160
161
162
163
164
165
                matrixColIndex.push_back(element.first);
                matrixValue.push_back(element.second);
            }
        }
        matrixRowStart.push_back(matrixValue.size());
        int *qRowStart, *qColIndex, *rRowStart, *rColIndex;
        double *qValue, *rValue;
        QUERN_compute_qr(numberOfConstraints, numberOfConstraints, &matrixRowStart[0], &matrixColIndex[0], &matrixValue[0], NULL,
                &qRowStart, &qColIndex, &qValue, &rRowStart, &rColIndex, &rValue);
peastman's avatar
peastman committed
166
        vector<vector<pair<int, double> > > transposedMatrix(numberOfConstraints);
167
        _matrix.resize(numberOfConstraints);
peastman's avatar
peastman committed
168
169
170
171

        // Extract columns from the inverse matrix one at a time.  It is done in parallel,
        // since this can be very slow.
        
172
        ThreadPool threads;
peastman's avatar
peastman committed
173
174
175
176
177
178
179
180
181
182
183
        threads.execute([&] (ThreadPool& pool, int threadIndex) {
            vector<double> rhs(numberOfConstraints);
            for (int i = threadIndex; i < numberOfConstraints; i += pool.getNumThreads()) {
                // Extract column i of the inverse matrix.

                for (int j = 0; j < numberOfConstraints; j++)
                    rhs[j] = (i == j ? 1.0 : 0.0);
                QUERN_multiply_with_q_transpose(numberOfConstraints, qRowStart, qColIndex, qValue, &rhs[0]);
                QUERN_solve_with_r(numberOfConstraints, rRowStart, rColIndex, rValue, &rhs[0], &rhs[0]);
                for (int j = 0; j < numberOfConstraints; j++) {
                    double value = rhs[j]*distance[i]/distance[j];
peastman's avatar
peastman committed
184
185
                    if (fabs(value) > elementCutoff)
                        transposedMatrix[i].push_back(pair<int, double>(j, value));
peastman's avatar
peastman committed
186
187
188
                }
            }
        });
189
        threads.waitForThreads();
190
191
192
193
194

        // For purposes of thread safety we extracted the matrix in transposed form, so we need to transpose it again.

        for (int i = 0; i < numberOfConstraints; i++) {
            for (int j = 0; j < transposedMatrix[i].size(); j++) {
peastman's avatar
peastman committed
195
                pair<int, double> value = transposedMatrix[i][j];
196
197
198
                _matrix[value.first].push_back(make_pair(i, value.second));
            }
        }
199
200
        QUERN_free_result(qRowStart, qColIndex, qValue);
        QUERN_free_result(rRowStart, rColIndex, rValue);
201
202
203
    }
}

204
205
int ReferenceCCMAAlgorithm::getNumberOfConstraints() const {
    return _numberOfConstraints;
206
207
}

208
209
int ReferenceCCMAAlgorithm::getMaximumNumberOfIterations() const {
    return _maximumNumberOfIterations;
210
211
}

212
213
void ReferenceCCMAAlgorithm::setMaximumNumberOfIterations(int maximumNumberOfIterations) {
    _maximumNumberOfIterations = maximumNumberOfIterations;
214
215
}

peastman's avatar
peastman committed
216
217
218
void ReferenceCCMAAlgorithm::apply(vector<Vec3>& atomCoordinates,
                                         vector<Vec3>& atomCoordinatesP,
                                         vector<double>& inverseMasses, double tolerance) {
219
    applyConstraints(atomCoordinates, atomCoordinatesP, inverseMasses, false, tolerance);
220
221
}

peastman's avatar
peastman committed
222
223
void ReferenceCCMAAlgorithm::applyToVelocities(std::vector<OpenMM::Vec3>& atomCoordinates,
               std::vector<OpenMM::Vec3>& velocities, std::vector<double>& inverseMasses, double tolerance) {
224
    applyConstraints(atomCoordinates, velocities, inverseMasses, true, tolerance);
225
226
}

peastman's avatar
peastman committed
227
228
229
void ReferenceCCMAAlgorithm::applyConstraints(vector<Vec3>& atomCoordinates,
                                         vector<Vec3>& atomCoordinatesP,
                                         vector<double>& inverseMasses, bool constrainingVelocities, double tolerance) {
230
231
232
233
234
235
236
237
238
239
    // calculate reduced masses on 1st pass

    if (!_hasInitializedMasses) {
        _hasInitializedMasses = true;
        for (int ii = 0; ii < _numberOfConstraints; ii++) {
           int atomI = _atomIndices[ii].first;
           int atomJ = _atomIndices[ii].second;
           reducedMasses[ii] = 0.5/(inverseMasses[atomI] + inverseMasses[atomJ]);
        }
    }
240

241
    // setup: r_ij for each (i,j) constraint
242

243
244
245
246
247
248
    for (int ii = 0; ii < _numberOfConstraints; ii++) {
        int atomI = _atomIndices[ii].first;
        int atomJ = _atomIndices[ii].second;
        r_ij[ii] = atomCoordinates[atomI] - atomCoordinates[atomJ];
        d_ij2[ii] = r_ij[ii].dot(r_ij[ii]);
    }
peastman's avatar
peastman committed
249
250
    double lowerTol = 1-2*tolerance+tolerance*tolerance;
    double upperTol = 1+2*tolerance+tolerance*tolerance;
251
252
253
254
255

    // main loop

    int iterations = 0;
    int numberConverged = 0;
peastman's avatar
peastman committed
256
257
    vector<double> constraintDelta(_numberOfConstraints);
    vector<double> tempDelta(_numberOfConstraints);
258
259
260
261
262
    while (iterations < getMaximumNumberOfIterations()) {
        numberConverged  = 0;
        for (int ii = 0; ii < _numberOfConstraints; ii++) {
            int atomI = _atomIndices[ii].first;
            int atomJ = _atomIndices[ii].second;
peastman's avatar
peastman committed
263
            Vec3 rp_ij = atomCoordinatesP[atomI] - atomCoordinatesP[atomJ];
264
            if (constrainingVelocities) {
peastman's avatar
peastman committed
265
                double rrpr = rp_ij.dot(r_ij[ii]);
266
                constraintDelta[ii] = -2*reducedMasses[ii]*rrpr/d_ij2[ii];
267
                if (fabs(constraintDelta[ii]) <= tolerance)
268
269
270
                    numberConverged++;
            }
            else {
peastman's avatar
peastman committed
271
272
273
                double rp2  = rp_ij.dot(rp_ij);
                double dist2 = _distance[ii]*_distance[ii];
                double diff = dist2 - rp2;
274
                constraintDelta[ii] = 0;
peastman's avatar
peastman committed
275
                double rrpr = DOT3(rp_ij, r_ij[ii]);
276
277
278
279
280
281
282
283
284
285
286
                constraintDelta[ii] = reducedMasses[ii]*diff/rrpr;
                if (rp2 >= lowerTol*dist2 && rp2 <= upperTol*dist2)
                    numberConverged++;
            }
        }
        if (numberConverged == _numberOfConstraints)
            break;
        iterations++;

        if (_matrix.size() > 0) {
            for (int i = 0; i < _numberOfConstraints; i++) {
peastman's avatar
peastman committed
287
                double sum = 0.0;
peastman's avatar
peastman committed
288
                for (auto& element : _matrix[i])
289
290
291
292
293
294
295
296
                    sum += element.second*constraintDelta[element.first];
                tempDelta[i] = sum;
            }
            constraintDelta = tempDelta;
        }
        for (int ii = 0; ii < _numberOfConstraints; ii++) {
            int atomI = _atomIndices[ii].first;
            int atomJ = _atomIndices[ii].second;
peastman's avatar
peastman committed
297
            Vec3 dr = r_ij[ii]*constraintDelta[ii];
298
299
300
301
            atomCoordinatesP[atomI] += dr*inverseMasses[atomI];
            atomCoordinatesP[atomJ] -= dr*inverseMasses[atomJ];
        }
    }
302
}
303

peastman's avatar
peastman committed
304
const vector<vector<pair<int, double> > >& ReferenceCCMAAlgorithm::getMatrix() const {
305
306
    return _matrix;
}