VelocityVerletIntegrator.cpp 12.1 KB
Newer Older
1
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
31
32
33
34
35
36
37
38
39
40
/* -------------------------------------------------------------------------- *
 *                                   OpenMM                                   *
 * -------------------------------------------------------------------------- *
 * This is part of the OpenMM molecular simulation toolkit originating from   *
 * Simbios, the NIH National Center for Physics-Based Simulation of           *
 * Biological Structures at Stanford, funded under the NIH Roadmap for        *
 * Medical Research, grant U54 GM072970. See https://simtk.org.               *
 *                                                                            *
 * Portions copyright (c) 2019 Stanford University and the Authors.           *
 * Authors: Andreas Krämer and Andrew C. Simmonett                            *
 * Contributors:                                                              *
 *                                                                            *
 * 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 "openmm/VelocityVerletIntegrator.h"
#include "openmm/Context.h"
#include "openmm/Force.h"
#include "openmm/System.h"
#include "openmm/NoseHooverChain.h"
#include "openmm/OpenMMException.h"
#include "openmm/CMMotionRemover.h"
#include "openmm/internal/ContextImpl.h"
#include "openmm/kernels.h"
41
#include <iostream>
42
#include <string>
43
#include <algorithm>
44
45
46
47
48

using namespace OpenMM;
using std::string;
using std::vector;

49
50
51
VelocityVerletIntegrator::VelocityVerletIntegrator(double stepSize):
    forcesAreValid(false)
{
52
53
54
55
    setStepSize(stepSize);
    setConstraintTolerance(1e-5);
}

56
57
VelocityVerletIntegrator::~VelocityVerletIntegrator() {}

58
double VelocityVerletIntegrator::propagateChain(double kineticEnergy, int chainID) {
59
    return nhcKernel.getAs<NoseHooverChainKernel>().propagateChain(*context, noseHooverChains.at(chainID), kineticEnergy, getStepSize());
60
61
62
63
64
}

int VelocityVerletIntegrator::addNoseHooverChainThermostat(System& system, double temperature, double collisionFrequency,
                                                           int chainLength, int numMTS, int numYoshidaSuzuki) {
    int numForces = system.getNumForces();
Andy Simmonett's avatar
Andy Simmonett committed
65
66
    std::vector<int> thermostatedParticles;
    std::vector<int> parentParticles;
67
    for(int particle = 0; particle < system.getNumParticles(); ++particle) {
68
69
70
71
72
        double mass = system.getParticleMass(particle);
        if ( (mass > 0) && (mass < 0.8) ){
            std::cout << "Warning: Found particles with mass between 0.0 and 0.8 dalton. Did you mean to make a DrudeVelocityVerletIntegrator instead? "
                         "The thermostat you are about to use will not treat these particles as Drude particles!" << std::endl;
        }
73
74
75
        if(system.getParticleMass(particle) > 0) {
            thermostatedParticles.push_back(particle);
        }
76
    }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

    return addMaskedNoseHooverChainThermostat(system, thermostatedParticles, parentParticles, temperature, collisionFrequency, chainLength, numMTS, numYoshidaSuzuki);
}

int VelocityVerletIntegrator::addMaskedNoseHooverChainThermostat(System& system, const std::vector<int>& thermostatedParticles, const std::vector<int>& parentParticles,
                                         double temperature, double collisionFrequency,
                                         int chainLength, int numMTS, int numYoshidaSuzuki){
    const auto & mask = thermostatedParticles; // just an alias
    if (context) {
        throw OpenMMException("Nose-Hoover chains cannot be added after binding this integrator to a context.");
    }
    if ((parentParticles.size() != mask.size()) && (parentParticles.size() != 0)) {
        throw OpenMMException("The number of parent particles has to be either equal to the number of thermostated particles (to thermostat relative motion)"
                              " or zero (to thermostat absolute motion).");
    }
    // figure out the number of DOFs
    int nDOF = 3*mask.size();
    for (int constraintNum = 0; constraintNum < system.getNumConstraints(); constraintNum++) {
        int particle1, particle2;
        double distance;
        system.getConstraintParameters(constraintNum, particle1, particle2, distance);
98
99
        bool particle1_in_mask = (std::find(mask.begin(), mask.end(), particle1) != mask.end());
        bool particle2_in_mask = (std::find(mask.begin(), mask.end(), particle2) != mask.end());
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
        if ((system.getParticleMass(particle1) > 0) && (system.getParticleMass(particle2) > 0)){
            if ((particle1_in_mask && !particle2_in_mask) || (!particle1_in_mask && particle2_in_mask)){
                throw OpenMMException("Cannot add only one of particles " + std::to_string(particle1) + " and " + std::to_string(particle2)
                                        + " to NoseHooverChain, because they are connected by a constraint.");
            }
            if (particle1_in_mask && particle2_in_mask){
                nDOF -= 1;
            }
        }
    }
    int numForces = system.getNumForces();
    if (parentParticles.size() == 0){ // remove 3 degrees of freedom from thermostats that act on absolute motions
        for (int forceNum = 0; forceNum < numForces; ++forceNum) {
            if (dynamic_cast<CMMotionRemover*>(&system.getForce(forceNum))) nDOF -= 3;
        }
115
116
    }

117
118
119
    // make sure that thermostats do not overlap 
    for (auto &other_nhc: noseHooverChains) {
        for (auto &particle: mask){
120
121
122
            bool isParticleInOtherChain = (std::find(other_nhc.getThermostatedAtoms().begin(), 
                                                    other_nhc.getThermostatedAtoms().end(),
                                                    particle) == other_nhc.getThermostatedAtoms().begin());
123
124
125
126
127
128
            if (isParticleInOtherChain){
                throw OpenMMException("Found particle " + std::to_string(particle) + "in a different NoseHooverChain, "
                                      "but particles can only be thermostated by one thermostat.");
            }
        }
    }
129
130
131
132
133
134
135

    // create and add new chain 
    int chainID = noseHooverChains.size();
    auto nhc = NoseHooverChain(temperature, collisionFrequency, nDOF, chainLength,
                                    numMTS, numYoshidaSuzuki, chainID,
                                    thermostatedParticles, parentParticles);
    noseHooverChains.push_back(nhc);
136
137
138
139
140
141
142
143
144
    return chainID;
}

double VelocityVerletIntegrator::getTemperature(int chainID) const {
    if (chainID >= noseHooverChains.size()) {
        throw OpenMMException("Cannot get temperature for chainID " + std::to_string(chainID)
                + ". Only " + std::to_string(noseHooverChains.size()) + " have been registered with this integrator."
        );
    }
145
    return noseHooverChains.at(chainID).getDefaultTemperature();
146
147
148
149
150
151
152
153
}

void VelocityVerletIntegrator::setTemperature(double temperature, int chainID){
    if (chainID >= noseHooverChains.size()) {
        throw OpenMMException("Cannot set temperature for chainID " + std::to_string(chainID)
                + ". Only " + std::to_string(noseHooverChains.size()) + " have been registered with this integrator."
        );
    }
154
    noseHooverChains.at(chainID).setDefaultTemperature(temperature);
155
156
157
158
159
160
161
162
163

}

double VelocityVerletIntegrator::getCollisionFrequency(int chainID) const {
    if (chainID >= noseHooverChains.size()) {
        throw OpenMMException("Cannot get collision frequency for chainID " + std::to_string(chainID)
                + ". Only " + std::to_string(noseHooverChains.size()) + " have been registered with this integrator."
        );
    }
164
    return noseHooverChains.at(chainID).getDefaultCollisionFrequency();
165
166
167
168
169
170
171
} 
void VelocityVerletIntegrator::setCollisionFrequency(double frequency, int chainID){
    if (chainID >= noseHooverChains.size()) {
        throw OpenMMException("Cannot set collision frequency for chainID " + std::to_string(chainID)
                + ". Only " + std::to_string(noseHooverChains.size()) + " have been registered with this integrator."
        );
    }
172
    noseHooverChains.at(chainID).setDefaultCollisionFrequency(frequency);
173
174
175
}

double VelocityVerletIntegrator::computeKineticEnergy() {
176
177
178
179
180
181
182
183
184
185
186
    double kE = 0.0;
    if(noseHooverChains.size()) {
        for (const auto &nhc: noseHooverChains){
            if (nhc.getParentAtoms().size() == 0) {
                kE += nhcKernel.getAs<NoseHooverChainKernel>().computeMaskedKineticEnergy(*context, nhc, true);
            }
        }
    } else {
        kE = vvKernel.getAs<IntegrateVelocityVerletStepKernel>().computeKineticEnergy(*context, *this);
    }
    return kE;
187
188
}

189
190
191
double VelocityVerletIntegrator::computeHeatBathEnergy() {
    double energy = 0;
    for(auto &nhc : noseHooverChains) {
192
        energy += nhcKernel.getAs<NoseHooverChainKernel>().computeHeatBathEnergy(*context, nhc);
193
194
195
196
    }
    return energy;
}

197
198
199
200
201
202
203
void VelocityVerletIntegrator::initialize(ContextImpl& contextRef) {
    if (owner != NULL && &contextRef.getOwner() != owner)
        throw OpenMMException("This Integrator is already bound to a context");
    context = &contextRef;
    owner = &contextRef.getOwner();
    vvKernel = context->getPlatform().createKernel(IntegrateVelocityVerletStepKernel::Name(), contextRef);
    vvKernel.getAs<IntegrateVelocityVerletStepKernel>().initialize(contextRef.getSystem(), *this);
204
205
    nhcKernel = context->getPlatform().createKernel(NoseHooverChainKernel::Name(), contextRef);
    nhcKernel.getAs<NoseHooverChainKernel>().initialize();
206
    forcesAreValid = false;
207
208
209
210
}

void VelocityVerletIntegrator::cleanup() {
    vvKernel = Kernel();
211
    nhcKernel = Kernel();
212
213
214
215
}

vector<string> VelocityVerletIntegrator::getKernelNames() {
    std::vector<std::string> names;
216
    names.push_back(NoseHooverChainKernel::Name());
217
218
219
220
221
222
223
    names.push_back(IntegrateVelocityVerletStepKernel::Name());
    return names;
}

void VelocityVerletIntegrator::step(int steps) {
    if (context == NULL)
        throw OpenMMException("This Integrator is not bound to a context!");
224
    double scale, kineticEnergy;
225
226
    for (int i = 0; i < steps; ++i) {
        context->updateContextState();
227
        for(auto &nhc : noseHooverChains) {
228
            kineticEnergy = nhcKernel.getAs<NoseHooverChainKernel>().computeMaskedKineticEnergy(*context, nhc, false);
229
230
            scale = nhcKernel.getAs<NoseHooverChainKernel>().propagateChain(*context, nhc, kineticEnergy, getStepSize());
            nhcKernel.getAs<NoseHooverChainKernel>().scaleVelocities(*context, nhc, scale);
231
        }
232
        vvKernel.getAs<IntegrateVelocityVerletStepKernel>().execute(*context, *this, forcesAreValid);
233
        for(auto &nhc : noseHooverChains) {
234
            kineticEnergy = nhcKernel.getAs<NoseHooverChainKernel>().computeMaskedKineticEnergy(*context, nhc, false);
235
236
            scale = nhcKernel.getAs<NoseHooverChainKernel>().propagateChain(*context, nhc, kineticEnergy, getStepSize());
            nhcKernel.getAs<NoseHooverChainKernel>().scaleVelocities(*context, nhc, scale);
237
        }
238
239
    }
}