OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
NPA.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 following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#include "integrators/NPA.hpp"
49
50#include "brains/SimInfo.hpp"
51#include "brains/Thermo.hpp"
52#include "integrators/IntegratorCreator.hpp"
54#include "utils/Constants.hpp"
55#include "utils/simError.h"
56
57namespace OpenMD {
58
59 void NPA::moveA() {
60 SimInfo::MoleculeIterator i;
61 Molecule::IntegrableObjectIterator j;
62 Molecule* mol;
63 StuntDouble* sd;
64 Vector3d Tb, ji;
65 RealType mass;
66 Vector3d vel;
67 Vector3d pos;
68 Vector3d frc;
69 Vector3d sc;
70 int index;
71
72 loadEta();
73
74 instaTemp = thermo.getTemperature();
75 press = thermo.getPressureTensor();
76 instaPress = Constants::pressureConvert *
77 (press(0, 0) + press(1, 1) + press(2, 2)) / 3.0;
78 instaVol = thermo.getVolume();
79
80 Vector3d COM = thermo.getCom();
81
82 // evolve velocity half step
83
84 calcVelScale();
85
86 for (mol = info_->beginMolecule(i); mol != NULL;
87 mol = info_->nextMolecule(i)) {
88 for (sd = mol->beginIntegrableObject(j); sd != NULL;
89 sd = mol->nextIntegrableObject(j)) {
90 vel = sd->getVel();
91 frc = sd->getFrc();
92
93 mass = sd->getMass();
94
95 getVelScaleA(sc, vel);
96
97 // velocity half step (use chi from previous step here):
98
99 vel += dt2 * Constants::energyConvert / mass * frc - dt2 * sc;
100 sd->setVel(vel);
101
102 if (sd->isDirectional()) {
103 // get and convert the torque to body frame
104
105 Tb = sd->lab2Body(sd->getTrq());
106
107 // get the angular momentum, and propagate a half step
108
109 ji = sd->getJ();
110
111 ji += dt2 * Constants::energyConvert * Tb;
112
113 rotAlgo_->rotate(sd, ji, dt);
114
115 sd->setJ(ji);
116 }
117 }
118 }
119 // evolve eta a half step
120
121 evolveEtaA();
122 flucQ_->moveA();
123
124 index = 0;
125 for (mol = info_->beginMolecule(i); mol != NULL;
126 mol = info_->nextMolecule(i)) {
127 for (sd = mol->beginIntegrableObject(j); sd != NULL;
128 sd = mol->nextIntegrableObject(j)) {
129 oldPos[index++] = sd->getPos();
130 }
131 }
132
133 // the first estimation of r(t+dt) is equal to r(t)
134
135 for (int k = 0; k < maxIterNum_; k++) {
136 index = 0;
137 for (mol = info_->beginMolecule(i); mol != NULL;
138 mol = info_->nextMolecule(i)) {
139 for (sd = mol->beginIntegrableObject(j); sd != NULL;
140 sd = mol->nextIntegrableObject(j)) {
141 vel = sd->getVel();
142 pos = sd->getPos();
143
144 this->getPosScale(pos, COM, index, sc);
145
146 pos = oldPos[index] + dt * (vel + sc);
147 sd->setPos(pos);
148
149 ++index;
150 }
151 }
152
153 rattle_->constraintA();
154 }
155
156 // Scale the box after all the positions have been moved:
157
158 this->scaleSimBox();
159
160 saveEta();
161 }
162
163 void NPA::moveB(void) {
164 SimInfo::MoleculeIterator i;
165 Molecule::IntegrableObjectIterator j;
166 Molecule* mol;
167 StuntDouble* sd;
168 int index;
169 Vector3d Tb;
170 Vector3d ji;
171 Vector3d sc;
172 Vector3d vel;
173 Vector3d frc;
174 RealType mass;
175
176 loadEta();
177
178 // save velocity and angular momentum
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 oldVel[index] = sd->getVel();
185
186 if (sd->isDirectional()) oldJi[index] = sd->getJ();
187
188 ++index;
189 }
190 }
191
192 instaVol = thermo.getVolume();
193 for (int k = 0; k < maxIterNum_; k++) {
194 instaTemp = thermo.getTemperature();
195 instaPress = thermo.getPressure();
196
197 // evolve eta
198 this->evolveEtaB();
199 this->calcVelScale();
200
201 index = 0;
202 for (mol = info_->beginMolecule(i); mol != NULL;
203 mol = info_->nextMolecule(i)) {
204 for (sd = mol->beginIntegrableObject(j); sd != NULL;
205 sd = mol->nextIntegrableObject(j)) {
206 frc = sd->getFrc();
207 mass = sd->getMass();
208
209 getVelScaleB(sc, index);
210
211 // velocity half step
212 vel = oldVel[index] + dt2 * Constants::energyConvert / mass * frc -
213 dt2 * sc;
214
215 sd->setVel(vel);
216
217 if (sd->isDirectional()) {
218 // get and convert the torque to body frame
219 Tb = sd->lab2Body(sd->getTrq());
220
221 ji = oldJi[index] + dt2 * Constants::energyConvert * Tb;
222
223 sd->setJ(ji);
224 }
225
226 ++index;
227 }
228 }
229
230 rattle_->constraintB();
231
232 if (this->etaConverged()) break;
233 }
234
235 flucQ_->moveB();
236 saveEta();
237 }
238
239 void NPA::evolveEtaA() {
240 eta(2, 2) += dt2 * instaVol *
241 (press(2, 2) - targetPressure / Constants::pressureConvert) /
242 (NkBT * tb2);
243 oldEta = eta;
244 }
245
246 void NPA::evolveEtaB() {
247 prevEta = eta;
248 eta(2, 2) =
249 oldEta(2, 2) +
250 dt2 * instaVol *
251 (press(2, 2) - targetPressure / Constants::pressureConvert) /
252 (NkBT * tb2);
253 }
254
255 void NPA::calcVelScale() {
256 for (int i = 0; i < 3; i++) {
257 for (int j = 0; j < 3; j++) {
258 vScale(i, j) = eta(i, j);
259 }
260 }
261 }
262
263 void NPA::getVelScaleA(Vector3d& sc, const Vector3d& vel) {
264 sc = vScale * vel;
265 }
266
267 void NPA::getVelScaleB(Vector3d& sc, int index) {
268 sc = vScale * oldVel[index];
269 }
270
271 void NPA::getPosScale(const Vector3d& pos, const Vector3d& COM, int index,
272 Vector3d& sc) {
273 Vector3d rj = (oldPos[index] + pos) / (RealType)2.0 - COM;
274 sc = eta * rj;
275 }
276
277 void NPA::scaleSimBox() {
278 Mat3x3d scaleMat;
279
280 for (int i = 0; i < 3; i++) {
281 for (int j = 0; j < 3; j++) {
282 scaleMat(i, j) = 0.0;
283 if (i == j) { scaleMat(i, j) = 1.0; }
284 }
285 }
286
287 scaleMat(2, 2) = exp(dt * eta(2, 2));
288 Mat3x3d hmat = snap->getHmat();
289 hmat = hmat * scaleMat;
290 snap->setHmat(hmat);
291 }
292
293 bool NPA::etaConverged() {
294 int i;
295 RealType diffEta, sumEta;
296
297 sumEta = 0;
298 for (i = 0; i < 3; i++) {
299 sumEta += pow(prevEta(i, i) - eta(i, i), 2);
300 }
301
302 diffEta = sqrt(sumEta / 3.0);
303
304 return (diffEta <= etaTolerance);
305 }
306
307 RealType NPA::calcConservedQuantity() {
308 loadEta();
309
310 // We need NkBT a lot, so just set it here: This is the RAW number
311 // of integrableObjects, so no subtraction or addition of constraints or
312 // orientational degrees of freedom:
313 NkBT = info_->getNGlobalIntegrableObjects() * Constants::kB * targetTemp;
314
315 RealType conservedQuantity;
316 RealType totalEnergy;
317 RealType barostat_kinetic;
318 RealType barostat_potential;
319 RealType trEta;
320
321 totalEnergy = thermo.getTotalEnergy();
322
323 SquareMatrix<RealType, 3> tmp = eta.transpose() * eta;
324 trEta = tmp.trace();
325
326 barostat_kinetic = NkBT * tb2 * trEta / (2.0 * Constants::energyConvert);
327
328 barostat_potential =
329 (targetPressure * thermo.getVolume() / Constants::pressureConvert) /
330 Constants::energyConvert;
331
332 conservedQuantity = totalEnergy + barostat_kinetic + barostat_potential;
333
334 return conservedQuantity;
335 }
336
337 void NPA::loadEta() { eta = snap->getBarostat(); }
338
339 void NPA::saveEta() { snap->setBarostat(eta); }
340} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.