"plugins/vscode:/vscode.git/clone" did not exist on "48acdae03c78ca1abd0a3d0a203b9f4a0807dc1b"
NoseHooverIntegrator.cpp 18 KB
Newer Older
1
2
3
4
5
6
7
8
/* -------------------------------------------------------------------------- *
 *                                   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.               *
 *                                                                            *
9
 * Portions copyright (c) 2019-2020 Stanford University and the Authors.      *
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 * 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
int NoseHooverIntegrator::addThermostat(double temperature, double collisionFrequency,
72
                                        int chainLength, int numMTS, int numYoshidaSuzuki) {
73
    hasSubsystemThermostats_ = false;
74
75
    return addSubsystemThermostat(std::vector<int>(), std::vector<std::pair<int, int>>(), temperature,
                                  collisionFrequency, temperature, collisionFrequency, chainLength, numMTS, numYoshidaSuzuki);
76
77
}

78
int NoseHooverIntegrator::addSubsystemThermostat(const std::vector<int>& thermostatedParticles,
79
                                                 const std::vector< std::pair< int, int> > &thermostatedPairs,
80
81
                                                 double temperature, double collisionFrequency,
                                                 double relativeTemperature, double relativeCollisionFrequency,
82
                                                 int chainLength, int numMTS, int numYoshidaSuzuki) {
83
    int chainID = noseHooverChains.size();
84
85
86
87
88
89
90
91
    // 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."
        );
    }
92
93
94
95
96
97
98
    int nDOF = 0; // is set in initializeThermostats()
    noseHooverChains.emplace_back(temperature, relativeTemperature,
                                 collisionFrequency, relativeCollisionFrequency,
                                 nDOF, chainLength, numMTS,
                                 numYoshidaSuzuki, chainID,
                                 thermostatedParticles, thermostatedPairs);
    return chainID;
99
100
}

101
const NoseHooverChain& NoseHooverIntegrator::getThermostat(int chainID) const {
102
103
104
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
    return noseHooverChains[chainID];
}
105

106
void NoseHooverIntegrator::initializeThermostats(const System &system) {
107

108
109
110
111
112
    allAtoms.clear();
    allPairs.clear();
    std::set<int> allAtomsSet;
    for (int atom = 0; atom < system.getNumParticles(); ++atom) allAtomsSet.insert(atom);

113
114
115
    for (auto &thermostat : noseHooverChains) {
        const auto &thermostatedParticles = thermostat.getThermostatedAtoms();
        const auto &thermostatedPairs = thermostat.getThermostatedPairs();
116

117
118
119
        for( auto& pair : thermostatedPairs ) {
            allAtomsSet.erase(pair.first);
            allAtomsSet.erase(pair.second);
120
            allPairs.emplace_back(pair.first, pair.second, thermostat.getRelativeTemperature());
121
122
        }

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

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

165
        // set number of DoFs for chain 
166
        thermostat.setNumDegreesOfFreedom(nDOF);
167
    }
168
169
170
171
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


    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.");
                }
            }
208
        }
209

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

    for(const auto& atom : allAtomsSet) allAtoms.push_back(atom);
220
221
}

222
double NoseHooverIntegrator::getTemperature(int chainID) const {
223
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
224
    return noseHooverChains[chainID].getTemperature();
225
226
}

227
void NoseHooverIntegrator::setTemperature(double temperature, int chainID){
228
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
229
    noseHooverChains[chainID].setTemperature(temperature);
230
231
232

}

233
double NoseHooverIntegrator::getRelativeTemperature(int chainID) const {
234
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
235
    return noseHooverChains[chainID].getRelativeTemperature();
236
237
238
}

void NoseHooverIntegrator::setRelativeTemperature(double temperature, int chainID){
239
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
240
    noseHooverChains[chainID].setRelativeTemperature(temperature);
241
242
243
244

}

double NoseHooverIntegrator::getCollisionFrequency(int chainID) const {
245
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
246
    return noseHooverChains[chainID].getCollisionFrequency();
247
248
249
}

void NoseHooverIntegrator::setCollisionFrequency(double frequency, int chainID){
250
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
251
    noseHooverChains[chainID].setCollisionFrequency(frequency);
252
253
}

254
double NoseHooverIntegrator::getRelativeCollisionFrequency(int chainID) const {
255
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
256
    return noseHooverChains[chainID].getRelativeCollisionFrequency();
257
258
259
}

void NoseHooverIntegrator::setRelativeCollisionFrequency(double frequency, int chainID){
260
    ASSERT_VALID_INDEX(chainID, noseHooverChains);
261
    noseHooverChains[chainID].setRelativeCollisionFrequency(frequency);
262
263
264
}

double NoseHooverIntegrator::computeKineticEnergy() {
265
    forcesAreValid = false;
266
    double kE = 0.0;
267
    if(noseHooverChains.size() > 0) {
268
        for (const auto &nhc: noseHooverChains){
269
            kE += kernel.getAs<IntegrateNoseHooverStepKernel>().computeMaskedKineticEnergy(*context, nhc, true).first;
270
271
        }
    } else {
272
        kE = kernel.getAs<IntegrateNoseHooverStepKernel>().computeKineticEnergy(*context, *this);
273
274
    }
    return kE;
275
276
}

277
278
279
280
bool NoseHooverIntegrator::kineticEnergyRequiresForce() const {
    return false;
}

281
double NoseHooverIntegrator::computeHeatBathEnergy() {
282
283
    double energy = 0;
    for(auto &nhc : noseHooverChains) {
284
        if (context && (nhc.getNumDegreesOfFreedom() > 0)) {
285
            energy += kernel.getAs<IntegrateNoseHooverStepKernel>().computeHeatBathEnergy(*context, nhc);
Andy Simmonett's avatar
Andy Simmonett committed
286
        }
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
    owner = &contextRef.getOwner();
298
299
    kernel = context->getPlatform().createKernel(IntegrateNoseHooverStepKernel::Name(), contextRef);
    kernel.getAs<IntegrateNoseHooverStepKernel>().initialize(contextRef.getSystem(), *this);
300
    forcesAreValid = false;
301
302

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

321
    initializeThermostats(system);
322
323
}

324
void NoseHooverIntegrator::cleanup() {
325
    kernel = Kernel();
326
327
}

328
vector<string> NoseHooverIntegrator::getKernelNames() {
329
    std::vector<std::string> names;
330
    names.push_back(IntegrateNoseHooverStepKernel::Name());
331
332
333
    return names;
}

334
void NoseHooverIntegrator::step(int steps) {
335
336
337
    if (context == NULL)
        throw OpenMMException("This Integrator is not bound to a context!");
    for (int i = 0; i < steps; ++i) {
338
339
        if(context->updateContextState())
            forcesAreValid = false;
340
        context->calcForcesAndEnergy(true, false, getIntegrationForceGroups());
341
        kernel.getAs<IntegrateNoseHooverStepKernel>().execute(*context, *this, forcesAreValid);
342
343
    }
}
344
345
346
347
348
349
350
351

void NoseHooverIntegrator::createCheckpoint(std::ostream& stream) const {
    kernel.getAs<IntegrateNoseHooverStepKernel>().createCheckpoint(*context, stream);
}

void NoseHooverIntegrator::loadCheckpoint(std::istream& stream) {
    kernel.getAs<IntegrateNoseHooverStepKernel>().loadCheckpoint(*context, stream);
}