"platforms/vscode:/vscode.git/clone" did not exist on "cd0ee42eb643abf26418b79aa408e557a0774eda"
NoseHooverIntegrator.cpp 18.8 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
/* -------------------------------------------------------------------------- *
 *                                   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.                                     *
 * -------------------------------------------------------------------------- */

32
#include "openmm/NoseHooverIntegrator.h"
33
34
35
#include "openmm/Context.h"
#include "openmm/Force.h"
#include "openmm/System.h"
36
#include "openmm/NoseHooverChain.h"
37
38
39
#include "openmm/OpenMMException.h"
#include "openmm/CMMotionRemover.h"
#include "openmm/internal/ContextImpl.h"
40
#include "openmm/internal/AssertionUtilities.h"
41
#include "openmm/kernels.h"
42
#include <iostream>
43
#include <string>
44
#include <algorithm>
45
46
47
48
49

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

50
NoseHooverIntegrator::NoseHooverIntegrator(double stepSize):
51
52
    forcesAreValid(false),
    hasSubsystemThermostats_(true)
53
{
54
55
    setStepSize(stepSize);
    setConstraintTolerance(1e-5);
Andy Simmonett's avatar
Andy Simmonett committed
56
    setMaximumPairDistance(0.0);
57
}
58
NoseHooverIntegrator::NoseHooverIntegrator(double temperature, double collisionFrequency, double stepSize,
59
60
61
62
                                           int chainLength, int numMTS, int numYoshidaSuzuki) : 
    forcesAreValid(false),
    hasSubsystemThermostats_(false) {

63
64
    setStepSize(stepSize);
    setConstraintTolerance(1e-5);
Andy Simmonett's avatar
Andy Simmonett committed
65
    setMaximumPairDistance(0.0);
66
    addThermostat(temperature, collisionFrequency, chainLength, numMTS, numYoshidaSuzuki);
67
}
68

69
NoseHooverIntegrator::~NoseHooverIntegrator() {}
70

71
std::pair<double, double> NoseHooverIntegrator::propagateChain(std::pair<double, double> kineticEnergy, int chainID) {
72
    return nhcKernel.getAs<NoseHooverChainKernel>().propagateChain(*context, noseHooverChains.at(chainID), kineticEnergy, getStepSize());
73
74
}

75
76

int NoseHooverIntegrator::addThermostat(double temperature, double collisionFrequency,
77
                                        int chainLength, int numMTS, int numYoshidaSuzuki) {
78
    hasSubsystemThermostats_ = false;
79
80
    return addSubsystemThermostat(std::vector<int>(), std::vector<std::pair<int, int>>(), temperature,
                                  collisionFrequency, temperature, collisionFrequency, chainLength, numMTS, numYoshidaSuzuki);
81
82
}

83
int NoseHooverIntegrator::addSubsystemThermostat(const std::vector<int>& thermostatedParticles,
84
                                                 const std::vector< std::pair< int, int> > &thermostatedPairs,
85
86
                                                 double temperature, double collisionFrequency,
                                                 double relativeTemperature, double relativeCollisionFrequency,
87
                                                 int chainLength, int numMTS, int numYoshidaSuzuki) {
88
    int chainID = noseHooverChains.size();
89
90
91
92
93
94
95
96
    // check if one thermostat already applies to all atoms or pairs
    if ( (chainID > 0) && (noseHooverChains[0].getThermostatedAtoms().size()*noseHooverChains[0].getThermostatedPairs().size() == 0) ) {
        throw OpenMMException(
            "Cannot add thermostat, since one of the thermostats already in the integrator applies to all particles. "
            "To manually add thermostats, use the constructor that takes only the "
            "step size as an input argument."
        );
    }
97
98
99
100
101
102
103
    int nDOF = 0; // is set in initializeThermostats()
    noseHooverChains.emplace_back(temperature, relativeTemperature,
                                 collisionFrequency, relativeCollisionFrequency,
                                 nDOF, chainLength, numMTS,
                                 numYoshidaSuzuki, chainID,
                                 thermostatedParticles, thermostatedPairs);
    return chainID;
104
105
}

106
const NoseHooverChain& NoseHooverIntegrator::getThermostat(int chainID) const {
107
108
109
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
    return noseHooverChains[chainID];
}
110

111
void NoseHooverIntegrator::initializeThermostats(const System &system) {
112

113
114
115
116
117
    allAtoms.clear();
    allPairs.clear();
    std::set<int> allAtomsSet;
    for (int atom = 0; atom < system.getNumParticles(); ++atom) allAtomsSet.insert(atom);

118
119
120
    for (auto &thermostat : noseHooverChains) {
        const auto &thermostatedParticles = thermostat.getThermostatedAtoms();
        const auto &thermostatedPairs = thermostat.getThermostatedPairs();
121

122
123
124
        for( auto& pair : thermostatedPairs ) {
            allAtomsSet.erase(pair.first);
            allAtomsSet.erase(pair.second);
125
            allPairs.emplace_back(pair.first, pair.second, thermostat.getRelativeTemperature());
126
127
        }

128
        // figure out the number of DOFs
129
        int nDOF = 3*(thermostatedParticles.size() + thermostatedPairs.size());
130
131
132
133
        for (int constraintNum = 0; constraintNum < system.getNumConstraints(); constraintNum++) {
            int particle1, particle2;
            double distance;
            system.getConstraintParameters(constraintNum, particle1, particle2, distance);
134
135
136
137
138
            bool particle1_in_thermostatedParticles = ((std::find(thermostatedParticles.begin(),
                                                                  thermostatedParticles.end(), particle1)
                                                                    != thermostatedParticles.end())) ||
                                                      (std::find_if(thermostatedPairs.begin(),
                                                                    thermostatedPairs.end(),
139
140
                                                                    [&particle1](const std::pair<int, int>& pair){
                                                                           return pair.first == particle1 || pair.second == particle1;})
141
142
143
144
145
146
                                                                      != thermostatedPairs.end());
            bool particle2_in_thermostatedParticles = ((std::find(thermostatedParticles.begin(),
                                                                  thermostatedParticles.end(), particle2)
                                                                    != thermostatedParticles.end())) ||
                                                      (std::find_if(thermostatedPairs.begin(),
                                                                    thermostatedPairs.end(),
147
148
                                                                    [&particle2](const std::pair<int, int>& pair){
                                                                           return pair.first == particle2 || pair.second == particle2;})
149
                                                                      != thermostatedPairs.end());
150
151
152
153
154
155
156
157
158
            if ((system.getParticleMass(particle1) > 0) && (system.getParticleMass(particle2) > 0)){
                if ((particle1_in_thermostatedParticles && !particle2_in_thermostatedParticles) ||
                     (!particle1_in_thermostatedParticles && particle2_in_thermostatedParticles)){
                    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_thermostatedParticles && particle2_in_thermostatedParticles){
                    nDOF -= 1;
                }
159
            }
160
161
162
163
        }

        // remove 3 degrees of freedom from thermostats that act on absolute motions
        int numForces = system.getNumForces();
164
        if (thermostatedPairs.size() == 0){
165
166
            for (int forceNum = 0; forceNum < numForces; ++forceNum) {
                if (dynamic_cast<const CMMotionRemover*>(&system.getForce(forceNum))) nDOF -= 3;
167
168
            }
        }
169

170
        // set number of DoFs for chain 
171
        thermostat.setNumDegreesOfFreedom(nDOF);
172
    }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212


    for (int chain1 = 0; chain1 < noseHooverChains.size(); ++chain1){
        const auto& nhc = noseHooverChains[chain1];

       // make sure that thermostats do not overlap
        for (int chain2 = 0; chain2 < chain1; ++chain2){
            const auto& other_nhc = noseHooverChains[chain2];
            for (auto &particle: nhc.getThermostatedAtoms()){
                bool isParticleInOtherChain = (std::find(other_nhc.getThermostatedAtoms().begin(),
                                                         other_nhc.getThermostatedAtoms().end(),
                                                         particle) != other_nhc.getThermostatedAtoms().end()) ||
                                              (std::find_if(other_nhc.getThermostatedPairs().begin(),
                                                            other_nhc.getThermostatedPairs().end(),
                                               [&particle](const std::pair<int, int>& pair){ return pair.first == particle || pair.second == particle;})
                                                  != other_nhc.getThermostatedPairs().end());
                if (isParticleInOtherChain){
                    throw OpenMMException("Found particle " + std::to_string(particle) + "in a different NoseHooverChain, "
                                          "but particles can only be thermostated by one thermostat.");
                }
            }
            for (auto &pair: nhc.getThermostatedPairs()){
                bool isParticleInOtherChain = (std::find(other_nhc.getThermostatedAtoms().begin(),
                                                         other_nhc.getThermostatedAtoms().end(),
                                                         pair.first) != other_nhc.getThermostatedAtoms().end()) ||
                                              (std::find(other_nhc.getThermostatedAtoms().begin(),
                                                         other_nhc.getThermostatedAtoms().end(),
                                                         pair.second) != other_nhc.getThermostatedAtoms().end()) ||
                                              (std::find_if(other_nhc.getThermostatedPairs().begin(),
                                                            other_nhc.getThermostatedPairs().end(),
                                               [&pair](const std::pair<int, int>& other_pair){
                                                        return pair.first == other_pair.first || pair.first == other_pair.second ||
                                                               pair.second == other_pair.first || pair.second == other_pair.second;})
                                                  != other_nhc.getThermostatedPairs().end());
                if (isParticleInOtherChain){
                    throw OpenMMException("Found pair " + std::to_string(pair.first) + "," +
                                          std::to_string(pair.second) + " in a different NoseHooverChain, "
                                          "but particles can only be thermostated by one thermostat.");
                }
            }
213
        }
214

215
216
217
        // make sure that massless particles are not thermostated
        for(auto particle: nhc.getThermostatedAtoms()){
            double mass = system.getParticleMass(particle);
218
            if (mass == 0.0) {
219
                throw OpenMMException("Found a particle with no mass (" + std::to_string(particle) + ") in a thermostat. Massless particles cannot be thermostated.");
220
221
222
            }
        }
    }
223
224

    for(const auto& atom : allAtomsSet) allAtoms.push_back(atom);
225
226
}

227
double NoseHooverIntegrator::getTemperature(int chainID) const {
228
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
229
    return noseHooverChains[chainID].getTemperature();
230
231
}

232
void NoseHooverIntegrator::setTemperature(double temperature, int chainID){
233
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
234
    noseHooverChains[chainID].setTemperature(temperature);
235
236
237

}

238
double NoseHooverIntegrator::getRelativeTemperature(int chainID) const {
239
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
240
    return noseHooverChains[chainID].getRelativeTemperature();
241
242
243
}

void NoseHooverIntegrator::setRelativeTemperature(double temperature, int chainID){
244
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
245
    noseHooverChains[chainID].setRelativeTemperature(temperature);
246
247
248
249

}

double NoseHooverIntegrator::getCollisionFrequency(int chainID) const {
250
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
251
    return noseHooverChains[chainID].getCollisionFrequency();
252
253
254
}

void NoseHooverIntegrator::setCollisionFrequency(double frequency, int chainID){
255
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
256
    noseHooverChains[chainID].setCollisionFrequency(frequency);
257
258
}

259
double NoseHooverIntegrator::getRelativeCollisionFrequency(int chainID) const {
260
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
261
    return noseHooverChains[chainID].getRelativeCollisionFrequency();
262
263
264
}

void NoseHooverIntegrator::setRelativeCollisionFrequency(double frequency, int chainID){
265
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
266
    noseHooverChains[chainID].setRelativeCollisionFrequency(frequency);
267
268
269
}

double NoseHooverIntegrator::computeKineticEnergy() {
270
    double kE = 0.0;
271
    if(noseHooverChains.size() > 0) {
272
        for (const auto &nhc: noseHooverChains){
273
            kE += nhcKernel.getAs<NoseHooverChainKernel>().computeMaskedKineticEnergy(*context, nhc, true).first;
274
275
276
277
278
        }
    } else {
        kE = vvKernel.getAs<IntegrateVelocityVerletStepKernel>().computeKineticEnergy(*context, *this);
    }
    return kE;
279
280
}

281
double NoseHooverIntegrator::computeHeatBathEnergy() {
282
283
    double energy = 0;
    for(auto &nhc : noseHooverChains) {
284
        if (context && (nhc.getNumDegreesOfFreedom() > 0)) {
Andy Simmonett's avatar
Andy Simmonett committed
285
286
            energy += nhcKernel.getAs<NoseHooverChainKernel>().computeHeatBathEnergy(*context, nhc);
        }
287
288
289
290
    }
    return energy;
}

291
void NoseHooverIntegrator::initialize(ContextImpl& contextRef) {
292
293
    if (owner != NULL && &contextRef.getOwner() != owner)
        throw OpenMMException("This Integrator is already bound to a context");
294

295
    context = &contextRef;
296
    const System& system = context->getSystem();
297
298
299
    owner = &contextRef.getOwner();
    vvKernel = context->getPlatform().createKernel(IntegrateVelocityVerletStepKernel::Name(), contextRef);
    vvKernel.getAs<IntegrateVelocityVerletStepKernel>().initialize(contextRef.getSystem(), *this);
300
301
    nhcKernel = context->getPlatform().createKernel(NoseHooverChainKernel::Name(), contextRef);
    nhcKernel.getAs<NoseHooverChainKernel>().initialize();
302
    forcesAreValid = false;
303
304

    // check for drude particles and build the Nose-Hoover Chains
305
    for (auto& thermostat: noseHooverChains){
306
        // if there are no thermostated particles or pairs in the lists this is a regular thermostat for the whole (non-Drude) system
307
308
        if ( (thermostat.getThermostatedAtoms().size() == 0) && (thermostat.getThermostatedPairs().size() == 0) ){
            std::vector<int> thermostatedParticles;
309
310
311
            for(int particle = 0; particle < system.getNumParticles(); ++particle) {
                double mass = system.getParticleMass(particle);
                if ( (mass > 0) && (mass < 0.8) ){
312
                    std::cout << "Warning: Found particles with mass between 0.0 and 0.8 dalton. Did you mean to make a DrudeNoseHooverIntegrator instead? "
313
314
315
                                 "The thermostat you are about to use will not treat these particles as Drude particles!" << std::endl;
                }
                if(system.getParticleMass(particle) > 0) {
316
                   thermostatedParticles.push_back(particle);
317
318
                }
            }
319
            thermostat.setThermostatedAtoms(thermostatedParticles);
320
321
322
        }
    }

323
    initializeThermostats(system);
324
325
}

326
void NoseHooverIntegrator::cleanup() {
327
    vvKernel = Kernel();
328
    nhcKernel = Kernel();
329
330
}

331
vector<string> NoseHooverIntegrator::getKernelNames() {
332
    std::vector<std::string> names;
333
    names.push_back(NoseHooverChainKernel::Name());
334
335
336
337
    names.push_back(IntegrateVelocityVerletStepKernel::Name());
    return names;
}

338
void NoseHooverIntegrator::step(int steps) {
339
340
    if (context == NULL)
        throw OpenMMException("This Integrator is not bound to a context!");
341
    std::pair<double, double> scale, kineticEnergy;
342
343
    for (int i = 0; i < steps; ++i) {
        context->updateContextState();
344
        for(auto &nhc : noseHooverChains) {
345
            kineticEnergy = nhcKernel.getAs<NoseHooverChainKernel>().computeMaskedKineticEnergy(*context, nhc, false);
346
347
            scale = nhcKernel.getAs<NoseHooverChainKernel>().propagateChain(*context, nhc, kineticEnergy, getStepSize());
            nhcKernel.getAs<NoseHooverChainKernel>().scaleVelocities(*context, nhc, scale);
348
        }
349
        vvKernel.getAs<IntegrateVelocityVerletStepKernel>().execute(*context, *this, forcesAreValid);
350
        for(auto &nhc : noseHooverChains) {
351
            kineticEnergy = nhcKernel.getAs<NoseHooverChainKernel>().computeMaskedKineticEnergy(*context, nhc, false);
352
353
            scale = nhcKernel.getAs<NoseHooverChainKernel>().propagateChain(*context, nhc, kineticEnergy, getStepSize());
            nhcKernel.getAs<NoseHooverChainKernel>().scaleVelocities(*context, nhc, scale);
354
        }
355
356
    }
}