OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
LangevinPiston.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#include "integrators/LangevinPiston.hpp"
46
47#include <cmath>
48#include <random>
49
50#ifdef IS_MPI
51#include <mpi.h>
52#endif
53
54#include "brains/SimInfo.hpp"
55#include "brains/Thermo.hpp"
56#include "integrators/IntegratorCreator.hpp"
58#include "utils/Constants.hpp"
59#include "utils/simError.h"
60
61namespace OpenMD {
62
63 LangevinPiston::LangevinPiston(SimInfo* info) : NPT(info) {
64 // NkBT has units of amu Ang^2 fs^-2 :
65 NkBT = info_->getNGlobalIntegrableObjects() * Constants::kB * targetTemp;
66
67 // W_ has units of amu Ang^2
68 // W_ = 3.0 * NkBT * tb2;
69 W_ = NkBT * tb2; // our eta scales all three box directions
70
71 // gamma_ has units of fs^-1
72 if (!simParams->haveLangevinPistonDrag()) {
73 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
74 "To use the LangevinPiston integrator, you must "
75 "set langevinPistonDrag "
76 "(fs^-1).\n");
77 painCave.severity = OPENMD_ERROR;
78 painCave.isFatal = 1;
79 simError();
80 } else {
81 gamma_ = simParams->getLangevinPistonDrag();
82 }
83
84#ifdef IS_MPI
85 if (worldRank == 0) {
86#endif
87 randNumGen_ = info->getRandomNumberGenerator();
88
89 // standard deviation units: amu Angs^2 fs^-2
90 RealType stdDev =
91 std::sqrt(2.0 * W_ * gamma_ * Constants::kB * targetTemp / dt);
92
93 forceDistribution_ = std::normal_distribution<RealType>(0.0, stdDev);
94#ifdef IS_MPI
95 }
96#endif
97
98 // randomForce will have units amu Ang^2 fs^-2:
99 genRandomForce(randomForce_);
100 }
101
102 void LangevinPiston::moveA() {
103 SimInfo::MoleculeIterator i;
104 Molecule::IntegrableObjectIterator j;
105 Molecule* mol;
106 StuntDouble* sd;
107 Vector3d Tb, ji;
108 RealType mass;
109 Vector3d vel;
110 Vector3d pos;
111 Vector3d frc;
112 Vector3d sc;
113 int index;
114
115 loadEta();
116
117 instaTemp = thermo.getTemperature();
118 press = thermo.getPressureTensor();
119 instaPress = Constants::pressureConvert *
120 (press(0, 0) + press(1, 1) + press(2, 2)) / 3.0;
121 instaVol = thermo.getVolume();
122
123 Vector3d COM = thermo.getCom();
124
125 // evolve velocity half step
126
127 calcVelScale();
128
129 for (mol = info_->beginMolecule(i); mol != NULL;
130 mol = info_->nextMolecule(i)) {
131 for (sd = mol->beginIntegrableObject(j); sd != NULL;
132 sd = mol->nextIntegrableObject(j)) {
133 vel = sd->getVel();
134 frc = sd->getFrc();
135
136 mass = sd->getMass();
137
138 getVelScaleA(sc, vel);
139
140 // velocity half step
141
142 vel += dt2 * Constants::energyConvert / mass * frc - dt2 * sc;
143 sd->setVel(vel);
144
145 if (sd->isDirectional()) {
146 // get and convert the torque to body frame
147
148 Tb = sd->lab2Body(sd->getTrq());
149
150 // get the angular momentum, and propagate a half step
151
152 ji = sd->getJ();
153
154 ji += dt2 * Constants::energyConvert * Tb;
155
156 rotAlgo_->rotate(sd, ji, dt);
157
158 sd->setJ(ji);
159 }
160 }
161 }
162 // evolve eta a half step
163
164 evolveEtaA();
165 flucQ_->moveA();
166
167 index = 0;
168 for (mol = info_->beginMolecule(i); mol != NULL;
169 mol = info_->nextMolecule(i)) {
170 for (sd = mol->beginIntegrableObject(j); sd != NULL;
171 sd = mol->nextIntegrableObject(j)) {
172 oldPos[index++] = sd->getPos();
173 }
174 }
175
176 // the first estimation of r(t+dt) is equal to r(t)
177
178 for (int k = 0; k < maxIterNum_; k++) {
179 index = 0;
180 for (mol = info_->beginMolecule(i); mol != NULL;
181 mol = info_->nextMolecule(i)) {
182 for (sd = mol->beginIntegrableObject(j); sd != NULL;
183 sd = mol->nextIntegrableObject(j)) {
184 vel = sd->getVel();
185 pos = sd->getPos();
186
187 this->getPosScale(pos, COM, index, sc);
188
189 pos = oldPos[index] + dt * (vel + sc);
190 sd->setPos(pos);
191
192 ++index;
193 }
194 }
195
196 rattle_->constraintA();
197 }
198
199 // Scale the box after all the positions have been moved:
200
201 this->scaleSimBox();
202
203 saveEta();
204 }
205
206 void LangevinPiston::moveB(void) {
207 SimInfo::MoleculeIterator i;
208 Molecule::IntegrableObjectIterator j;
209 Molecule* mol;
210 StuntDouble* sd;
211 int index;
212 Vector3d Tb;
213 Vector3d ji;
214 Vector3d sc;
215 Vector3d vel;
216 Vector3d frc;
217 RealType mass;
218
219 loadEta();
220
221 // save velocity and angular momentum
222 index = 0;
223 for (mol = info_->beginMolecule(i); mol != NULL;
224 mol = info_->nextMolecule(i)) {
225 for (sd = mol->beginIntegrableObject(j); sd != NULL;
226 sd = mol->nextIntegrableObject(j)) {
227 oldVel[index] = sd->getVel();
228
229 if (sd->isDirectional()) oldJi[index] = sd->getJ();
230
231 ++index;
232 }
233 }
234
235 instaVol = thermo.getVolume();
236
237 for (int k = 0; k < maxIterNum_; k++) {
238 instaTemp = thermo.getTemperature();
239 instaPress = thermo.getPressure();
240
241 // evolve eta
242 this->evolveEtaB();
243 this->calcVelScale();
244
245 index = 0;
246 for (mol = info_->beginMolecule(i); mol != NULL;
247 mol = info_->nextMolecule(i)) {
248 for (sd = mol->beginIntegrableObject(j); sd != NULL;
249 sd = mol->nextIntegrableObject(j)) {
250 frc = sd->getFrc();
251 mass = sd->getMass();
252
253 getVelScaleB(sc, index);
254
255 // velocity half step
256 vel = oldVel[index] + dt2 * Constants::energyConvert / mass * frc -
257 dt2 * sc;
258
259 sd->setVel(vel);
260
261 if (sd->isDirectional()) {
262 // get and convert the torque to body frame
263 Tb = sd->lab2Body(sd->getTrq());
264
265 ji = oldJi[index] + dt2 * Constants::energyConvert * Tb;
266
267 sd->setJ(ji);
268 }
269
270 ++index;
271 }
272 }
273
274 rattle_->constraintB();
275 if (this->etaConverged()) break;
276 }
277
278 flucQ_->moveB();
279 saveEta();
280 }
281
282 void LangevinPiston::evolveEtaA() {
283 // volume is Angs^3
284 // pressures are in atm
285 // pressureConvert takes amu*fs^-2*Ang^-1 -> atm
286 eta += dt2 * (instaVol * (instaPress - targetPressure) /
287 (Constants::pressureConvert * W_) -
288 gamma_ * eta + randomForce_ / W_);
289 oldEta = eta;
290 }
291
292 void LangevinPiston::evolveEtaB() {
293 prevEta = eta;
294
295 genRandomForce(randomForce_);
296
297 eta = oldEta + dt2 * (instaVol * (instaPress - targetPressure) /
298 (Constants::pressureConvert * W_) -
299 gamma_ * eta + randomForce_ / W_);
300 }
301
302 void LangevinPiston::calcVelScale() { vScale = eta; }
303
304 void LangevinPiston::getVelScaleA(Vector3d& sc, const Vector3d& vel) {
305 sc = vScale * vel;
306 }
307
308 void LangevinPiston::getVelScaleB(Vector3d& sc, int index) {
309 sc = vScale * oldVel[index];
310 }
311
312 void LangevinPiston::getPosScale(const Vector3d& pos, const Vector3d& COM,
313 int index, Vector3d& sc) {
314 Vector3d rj = (oldPos[index] + pos) / (RealType)2.0 - COM;
315 sc = eta * rj;
316 }
317
318 void LangevinPiston::scaleSimBox() {
319 RealType scaleFactor;
320
321 // This is from solving the first order equation that defines eta
322 scaleFactor = exp(dt * eta);
323
324 if ((scaleFactor > 1.1) || (scaleFactor < 0.9)) {
325 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
326 "LangevinPiston error: Attempting a Box scaling of more than 10 "
327 "percent\n"
328 " check your tauBarostat, as it is probably too small!\n"
329 " eta = %lf, scaleFactor = %lf\n",
330 eta, scaleFactor);
331 painCave.isFatal = 1;
332 simError();
333 } else {
334 Mat3x3d hmat = snap->getHmat();
335 hmat *= scaleFactor;
336 snap->setHmat(hmat);
337 }
338 }
339
340 bool LangevinPiston::etaConverged() {
341 return (fabs(prevEta - eta) <= etaTolerance);
342 }
343
344 void LangevinPiston::loadEta() {
345 Mat3x3d etaMat = snap->getBarostat();
346 eta = etaMat(0, 0);
347 }
348
349 void LangevinPiston::saveEta() {
350 Mat3x3d etaMat(0.0);
351 etaMat(0, 0) = eta;
352 etaMat(1, 1) = eta;
353 etaMat(2, 2) = eta;
354 snap->setBarostat(etaMat);
355 }
356
357 void LangevinPiston::genRandomForce(RealType& randomForce) {
358#ifdef IS_MPI
359 if (worldRank == 0) {
360#endif
361 randomForce = forceDistribution_(*randNumGen_);
362#ifdef IS_MPI
363 }
364 // push this out to the other processors
365 // Same command on all nodes:
366 MPI_Bcast(&randomForce, 1, MPI_REALTYPE, 0, MPI_COMM_WORLD);
367#endif
368
369 return;
370 }
371} // namespace OpenMD
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:93
Molecule * beginMolecule(MoleculeIterator &i)
Returns the first molecule in this SimInfo and intialize the iterator.
Definition SimInfo.cpp:240
Molecule * nextMolecule(MoleculeIterator &i)
Returns the next avaliable Molecule based on the iterator.
Definition SimInfo.cpp:245
Mat3x3d getHmat()
Returns the H-Matrix.
Definition Snapshot.cpp:214
void setHmat(const Mat3x3d &m)
Sets the H-Matrix.
Definition Snapshot.cpp:217
"Don't move, or you're dead! Stand up! Captain, we've got them!"
Vector3d getTrq()
Returns the current torque of this stuntDouble.
Vector3d lab2Body(const Vector3d &v)
Converts a lab fixed vector to a body fixed vector.
Vector3d getVel()
Returns the current velocity of this stuntDouble.
RealType getMass()
Returns the mass of this stuntDouble.
Vector3d getPos()
Returns the current position of this stuntDouble.
void setPos(const Vector3d &pos)
Sets the current position of this stuntDouble.
void setVel(const Vector3d &vel)
Sets the current velocity of this stuntDouble.
Vector3d getJ()
Returns the current angular momentum of this stuntDouble (body -fixed).
bool isDirectional()
Tests if this stuntDouble is a directional one.
Vector3d getFrc()
Returns the current force of this stuntDouble.
void setJ(const Vector3d &angMom)
Sets the current angular momentum of this stuntDouble (body-fixed).
Mat3x3d getPressureTensor()
gives the pressure tensor in amu*fs^-2*Ang^-1
Definition Thermo.cpp:443
Vector3d getCom()
Returns the center of the mass of the whole system.
Definition Thermo.cpp:805
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.