OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
LP.cpp
1/*
2 * Copyright (c) 2004-2021 The University of Notre Dame. All Rights Reserved.
3 *
4 * The University of Notre Dame grants you ("Licensee") a
5 * non-exclusive, royalty free, license to use, modify and
6 * redistribute this software in source and binary code form, provided
7 * that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * This software is provided "AS IS," without a warranty of any
18 * kind. All express or implied conditions, representations and
19 * warranties, including any implied warranty of merhantability,
20 * fitness for a particular purpose or non-infringement, are hereby
21 * excluded. The University of Notre Dame and its licensors shall not
22 * be liable for any damages suffered by licensee as a result of
23 * using, modifying or distributing the software or its
24 * derivatives. In no event will the University of Notre Dame or its
25 * licensors be liable for any lost revenue, profit or data, or for
26 * direct, indirect, special, consequential, incidental or punitive
27 * damages, however caused and regardless of the theory of liability,
28 * arising out of the use of or inability to use software, even if the
29 * University of Notre Dame has been advised of the possibility of
30 * such damages.
31 *
32 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
33 * research, please cite the appropriate papers when you publish your
34 * work. Good starting points are:
35 *
36 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
37 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
38 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
39 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
40 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
41 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
42 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
43 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
44 */
45
46#ifdef IS_MPI
47#include <mpi.h>
48#endif
49
50#include <cmath>
51#include <random>
52
53#include "brains/SimInfo.hpp"
54#include "brains/Thermo.hpp"
55#include "integrators/IntegratorCreator.hpp"
56#include "integrators/LangevinPiston.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_ = 3.0 * NkBT * tb2; // our eta scales all three box directions
70
71 // gamma_ has units of fs^-1
72 if (!simParams->haveLangevinPistonDrag()) {
73 sprintf(painCave.errMsg, "To use the LangevinPiston integrator, you must "
74 "set langevinPistonDrag "
75 "(fs^-1).\n");
76 painCave.severity = OPENMD_ERROR;
77 painCave.isFatal = 1;
78 simError();
79 } else {
80 gamma_ = simParams->getLangevinPistonDrag();
81 }
82
83#ifdef IS_MPI
84 if (worldRank == 0) {
85#endif
86 randNumGen_ = info->getRandomNumberGenerator();
87
88 // standard deviation units: amu Angs^2 fs^-2
89 RealType stdDev =
90 std::sqrt(2.0 * W_ * gamma_ * Constants::kB * targetTemp / dt);
91
92 forceDistribution_ = std::normal_distribution<RealType>(0.0, stdDev);
93#ifdef IS_MPI
94 }
95#endif
96
97 // randomForce will have units amu Ang^2 fs^-2:
98 genRandomForce(randomForce_);
99 }
100
101 void LangevinPiston::moveA() {
102 SimInfo::MoleculeIterator i;
103 Molecule::IntegrableObjectIterator j;
104 Molecule* mol;
105 StuntDouble* sd;
106 Vector3d Tb, ji;
107 RealType mass;
108 Vector3d vel;
109 Vector3d pos;
110 Vector3d frc;
111 Vector3d sc;
112 int index;
113
114 loadEta();
115
116 instaTemp = thermo.getTemperature();
117 press = thermo.getPressureTensor();
118 instaPress = Constants::pressureConvert *
119 (press(0, 0) + press(1, 1) + press(2, 2)) / 3.0;
120 instaVol = thermo.getVolume();
121
122 Vector3d COM = thermo.getCom();
123
124 // evolve velocity half step
125
126 calcVelScale();
127
128 for (mol = info_->beginMolecule(i); mol != NULL;
129 mol = info_->nextMolecule(i)) {
130 for (sd = mol->beginIntegrableObject(j); sd != NULL;
131 sd = mol->nextIntegrableObject(j)) {
132 vel = sd->getVel();
133 frc = sd->getFrc();
134
135 mass = sd->getMass();
136
137 getVelScaleA(sc, vel);
138
139 // velocity half step
140
141 vel += dt2 * Constants::energyConvert / mass * frc - dt2 * sc;
142 sd->setVel(vel);
143
144 if (sd->isDirectional()) {
145 // get and convert the torque to body frame
146
147 Tb = sd->lab2Body(sd->getTrq());
148
149 // get the angular momentum, and propagate a half step
150
151 ji = sd->getJ();
152
153 ji += dt2 * Constants::energyConvert * Tb;
154
155 rotAlgo_->rotate(sd, ji, dt);
156
157 sd->setJ(ji);
158 }
159 }
160 }
161 // evolve eta a half step
162
163 evolveEtaA();
164 flucQ_->moveA();
165
166 index = 0;
167 for (mol = info_->beginMolecule(i); mol != NULL;
168 mol = info_->nextMolecule(i)) {
169 for (sd = mol->beginIntegrableObject(j); sd != NULL;
170 sd = mol->nextIntegrableObject(j)) {
171 oldPos[index++] = sd->getPos();
172 }
173 }
174
175 // the first estimation of r(t+dt) is equal to r(t)
176
177 for (int k = 0; k < maxIterNum_; k++) {
178 index = 0;
179 for (mol = info_->beginMolecule(i); mol != NULL;
180 mol = info_->nextMolecule(i)) {
181 for (sd = mol->beginIntegrableObject(j); sd != NULL;
182 sd = mol->nextIntegrableObject(j)) {
183 vel = sd->getVel();
184 pos = sd->getPos();
185
186 this->getPosScale(pos, COM, index, sc);
187
188 pos = oldPos[index] + dt * (vel + sc);
189 sd->setPos(pos);
190
191 ++index;
192 }
193 }
194
195 rattle_->constraintA();
196 }
197
198 // Scale the box after all the positions have been moved:
199
200 this->scaleSimBox();
201
202 saveEta();
203 }
204
205 void LangevinPiston::moveB(void) {
206 SimInfo::MoleculeIterator i;
207 Molecule::IntegrableObjectIterator j;
208 Molecule* mol;
209 StuntDouble* sd;
210 int index;
211 Vector3d Tb;
212 Vector3d ji;
213 Vector3d sc;
214 Vector3d vel;
215 Vector3d frc;
216 RealType mass;
217
218 loadEta();
219
220 // save velocity and angular momentum
221 index = 0;
222 for (mol = info_->beginMolecule(i); mol != NULL;
223 mol = info_->nextMolecule(i)) {
224 for (sd = mol->beginIntegrableObject(j); sd != NULL;
225 sd = mol->nextIntegrableObject(j)) {
226 oldVel[index] = sd->getVel();
227
228 if (sd->isDirectional()) oldJi[index] = sd->getJ();
229
230 ++index;
231 }
232 }
233
234 instaVol = thermo.getVolume();
235
236 for (int k = 0; k < maxIterNum_; k++) {
237 instaTemp = thermo.getTemperature();
238 instaPress = thermo.getPressure();
239
240 // evolve eta
241 this->evolveEtaB();
242 this->calcVelScale();
243
244 index = 0;
245 for (mol = info_->beginMolecule(i); mol != NULL;
246 mol = info_->nextMolecule(i)) {
247 for (sd = mol->beginIntegrableObject(j); sd != NULL;
248 sd = mol->nextIntegrableObject(j)) {
249 frc = sd->getFrc();
250 mass = sd->getMass();
251
252 getVelScaleB(sc, index);
253
254 // velocity half step
255 vel = oldVel[index] + dt2 * Constants::energyConvert / mass * frc -
256 dt2 * sc;
257
258 sd->setVel(vel);
259
260 if (sd->isDirectional()) {
261 // get and convert the torque to body frame
262 Tb = sd->lab2Body(sd->getTrq());
263
264 ji = oldJi[index] + dt2 * Constants::energyConvert * Tb;
265
266 sd->setJ(ji);
267 }
268
269 ++index;
270 }
271 }
272
273 rattle_->constraintB();
274 if (this->etaConverged()) break;
275 }
276
277 flucQ_->moveB();
278 saveEta();
279 }
280
281 void LangevinPiston::evolveEtaA() {
282 // volume is Angs^3
283 // pressures are in atm
284 // pressureConvert takes amu*fs^-2*Ang^-1 -> atm
285 eta += dt2 * (instaVol * (instaPress - targetPressure) /
286 (Constants::pressureConvert * W_) -
287 gamma_ * eta + randomForce_ / W_);
288 oldEta = eta;
289 }
290
291 void LangevinPiston::evolveEtaB() {
292 prevEta = eta;
293
294 genRandomForce(randomForce_);
295
296 eta = oldEta + dt2 * (instaVol * (instaPress - targetPressure) /
297 (Constants::pressureConvert * W_) -
298 gamma_ * eta + randomForce_ / W_);
299 }
300
301 void LangevinPiston::calcVelScale() { vScale = eta; }
302
303 void LangevinPiston::getVelScaleA(Vector3d& sc, const Vector3d& vel) {
304 sc = vScale * vel;
305 }
306
307 void LangevinPiston::getVelScaleB(Vector3d& sc, int index) {
308 sc = vScale * oldVel[index];
309 }
310
311 void LangevinPiston::getPosScale(const Vector3d& pos, const Vector3d& COM,
312 int index, Vector3d& sc) {
313 Vector3d rj = (oldPos[index] + pos) / (RealType)2.0 - COM;
314 sc = eta * rj;
315 }
316
317 void LangevinPiston::scaleSimBox() {
318 RealType scaleFactor;
319
320 // This is from solving the first order equation that defines eta
321 scaleFactor = exp(dt * eta);
322
323 if ((scaleFactor > 1.1) || (scaleFactor < 0.9)) {
324 sprintf(painCave.errMsg,
325 "LangevinPiston error: Attempting a Box scaling of more than 10 "
326 "percent\n"
327 " check your tauBarostat, as it is probably too small!\n"
328 " eta = %lf, scaleFactor = %lf\n",
329 eta, scaleFactor);
330 painCave.isFatal = 1;
331 simError();
332 } else {
333 Mat3x3d hmat = snap->getHmat();
334 hmat *= scaleFactor;
335 snap->setHmat(hmat);
336 }
337 }
338
339 bool LangevinPiston::etaConverged() {
340 return (fabs(prevEta - eta) <= etaTolerance);
341 }
342
343 void LangevinPiston::loadEta() {
344 Mat3x3d etaMat = snap->getBarostat();
345 eta = etaMat(0, 0);
346 }
347
348 void LangevinPiston::saveEta() {
349 Mat3x3d etaMat(0.0);
350 etaMat(0, 0) = eta;
351 etaMat(1, 1) = eta;
352 etaMat(2, 2) = eta;
353 snap->setBarostat(etaMat);
354 }
355
356 void LangevinPiston::genRandomForce(RealType& randomForce) {
357#ifdef IS_MPI
358 if (worldRank == 0) {
359#endif
360 randomForce = forceDistribution_(*randNumGen_);
361#ifdef IS_MPI
362 }
363 // push this out to the other processors
364 // Same command on all nodes:
365 MPI_Bcast(&randomForce, 1, MPI_REALTYPE, 0, MPI_COMM_WORLD);
366#endif
367
368 return;
369 }
370} // 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.