Loading...
Searching...
No Matches
Dynamics.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/Dynamics.h"
13#include "eon/EonLogger.h"
14#include "eon/PathIntegral.h"
15#include "eon/Tunneling.h"
16
17#include <cmath>
18#include <stdexcept>
19
20namespace eonc {
21
22const char Dynamics::ANDERSEN[] = "andersen";
23const char Dynamics::NOSE_HOOVER[] = "nose_hoover";
24const char Dynamics::LANGEVIN[] = "langevin";
25const char Dynamics::NONE[] = "none";
26const char Dynamics::PILE[] = "pile";
27const char Dynamics::PIGLET[] = "piglet";
28
30 : matter{matter_in},
32 if (!matter) {
33 throw std::invalid_argument("Dynamics: null Matter");
34 }
35 dt = m_config.time_step;
36 nAtoms = matter->numberOfAtoms();
37 // Unfixed axes only. A partly fixed atom is not three degrees of freedom.
38 nFreeCoords = 0;
39 for (long i = 0; i < nAtoms; ++i) {
40 for (int axis = 0; axis < 3; ++axis) {
41 if (!matter->getFixed(i, axis)) {
43 }
44 }
45 }
46 temperature = m_config.temperature;
47 kB = m_config.kB;
48 vxi1 = vxi2 = xi1 = xi2 = 0.0;
49}
50
51Dynamics::~Dynamics() = default;
52
53void Dynamics::setTemperature(double temperature_in) {
54 temperature = temperature_in;
55}
56
57void Dynamics::oneStep(int stepNumber) {
58 if (m_config.thermostat_kind == ANDERSEN) {
61 } else if (m_config.thermostat_kind == NOSE_HOOVER) {
63 } else if (m_config.thermostat_kind == LANGEVIN) {
65 } else if (m_config.thermostat_kind == NONE) {
67 } else if (m_config.thermostat_kind == PILE ||
68 m_config.thermostat_kind == PIGLET) {
69 throw std::invalid_argument(
70 "path-integral dynamics is a ring-polymer trajectory, not a "
71 "velocity Verlet step");
72 }
73
74 if (stepNumber != -1) {
75 if (stepNumber == 1) {
76 QUILL_LOG_DEBUG(log, "{} {:8s} {:10s} {:12s} {:12s} {:10s}\n",
77 "[Dynamics]", "Step", "KE", "PE", "TE", "KinT");
78 }
79 double kinE = matter->getKineticEnergy();
80 double potE = matter->getPotentialEnergy();
81 const double kinT =
82 (nFreeCoords > 0 && kB > 0.0) ? 2.0 * kinE / nFreeCoords / kB : 0.0;
83
84 if (stepNumber % m_config.write_movies_interval == 0) {
85 QUILL_LOG_DEBUG(log, "{} {:8} {:10.4} {:12.4} {:12.4} {:10.2}\n",
86 "[Dynamics]", stepNumber, kinE, potE, kinE + potE, kinT);
87 }
88 }
89}
90
92 AtomMatrix positions = matter->getPositions();
93 AtomMatrix velocities = matter->getVelocities();
94 AtomMatrix accInit = matter->getAccelerations();
95
96 positions += dt * velocities + 0.5 * dt * dt * accInit;
97 matter->setPositions(positions);
98
99 AtomMatrix accFinal = matter->getAccelerations();
100 velocities += 0.5 * dt * (accInit + accFinal);
101 matter->setVelocities(velocities);
102}
103
105 auto pot = matter->getPotential();
106 if (!pot) {
107 throw std::invalid_argument("path-integral dynamics has no potential");
108 }
109 const long atoms = matter->numberOfAtoms();
110 std::vector<double> masses(static_cast<size_t>(atoms));
111 std::vector<int> numbers(static_cast<size_t>(atoms));
112 std::vector<char> free(static_cast<size_t>(3 * atoms), 1);
113 const VectorXi z = matter->getAtomicNrs();
114 for (long i = 0; i < atoms; ++i) {
115 masses[static_cast<size_t>(i)] = matter->getMass(i);
116 numbers[static_cast<size_t>(i)] = z[i];
117 for (int axis = 0; axis < 3; ++axis) {
118 if (matter->getFixed(i, axis)) {
119 free[static_cast<size_t>(3 * i + axis)] = 0;
120 }
121 }
122 }
124 opt.beads = m_config.path_beads;
126 opt.kB = kB;
128 opt.dt = dt;
129 opt.springs = m_config.path_springs == "eco" ? pathintegral::Springs::Eco
131 opt.thermostat = m_config.thermostat_kind == PIGLET
134 opt.pileTau = m_config.path_pile_tau;
135 opt.pileScale = m_config.path_pile_scale;
136 opt.ecoOmegaMax = m_config.path_eco_omega_max;
137 opt.gleFile = m_config.path_gle_file;
138 opt.seed = m_config.path_seed;
139 pathintegral::RingPolymer ring(atoms, masses, numbers, free, opt);
140 const AtomMatrix pos = matter->getPositions();
141 ring.setAllBeads(pos.data());
142 const Matrix3d box =
143 matter->getPeriodic() ? matter->getCell() : Matrix3d::Zero().eval();
144 for (long step = 0; step < m_config.steps; ++step) {
145 ring.step(*pot, box.data(), false);
146 }
147 const VectorXd centroid = ring.centroid();
148 const VectorXd velocity = ring.centroidVelocity();
149 AtomMatrix out = pos;
150 AtomMatrix vel = matter->getVelocities();
151 for (long i = 0; i < atoms; ++i) {
152 for (int axis = 0; axis < 3; ++axis) {
153 out(i, axis) = centroid[3 * i + axis];
154 vel(i, axis) = velocity[3 * i + axis];
155 }
156 }
157 matter->setPositions(out);
158 matter->setVelocities(vel);
159}
160
162 if (m_config.thermostat_kind == PILE || m_config.thermostat_kind == PIGLET) {
164 return;
165 }
166 double sumT = 0.0, sumT2 = 0.0;
167
169
170 if (m_config.thermostat_kind != NONE) {
171 QUILL_LOG_DEBUG(log,
172 "{} Running NVT molecular dynamics: {:8.2f} K for {} "
173 "steps ({:.4e} s)\n",
174 "[Dynamics]", temperature, m_config.steps,
175 1e-15 * m_config.time_step * m_config.timeUnit *
176 m_config.steps);
177 } else {
178 QUILL_LOG_DEBUG(log, "{} Running NVE molecular dynamics: {} steps\n",
179 "[Dynamics]", m_config.steps);
180 }
181
182 if (m_config.write_movies) {
183 if (!eonc::io::io_ok(matter->matter2con("dynamics", false))) {
184 QUILL_LOG_WARNING(log, "Failed to write dynamics movie header frame");
185 }
186 }
187
188 QUILL_LOG_DEBUG(log, "{} {:8} {:10} {:12} {:12} {:10}\n", "[Dynamics]",
189 "step", "KE", "PE", "TE", "kinT");
190
191 for (long step = 0; step < m_config.steps; step++) {
192 oneStep();
193
194 double kinE = matter->getKineticEnergy();
195 double potE = matter->getPotentialEnergy();
196 const double kinT =
197 (nFreeCoords > 0 && kB > 0.0) ? 2.0 * kinE / nFreeCoords / kB : 0.0;
198 sumT += kinT;
199 sumT2 += kinT * kinT;
200
201 if (step % m_config.write_movies_interval == 0) {
202 QUILL_LOG_DEBUG(log, "{} {} {} {} {} {}\n", "[Dynamics]", step, kinE,
203 potE, kinE + potE, kinT);
204 }
205
206 if (m_config.write_movies && (step % m_config.write_movies_interval == 0)) {
207 if (!eonc::io::io_ok(matter->matter2con("dynamics", true))) {
208 QUILL_LOG_WARNING(log, "Failed to append dynamics movie frame");
209 }
210 }
211 }
212
213 const double nstat = static_cast<double>(std::max(m_config.steps, 1L));
214 double avgT = sumT / nstat;
215 double varT = sumT2 / nstat - avgT * avgT;
216 double stdT = std::sqrt(varT);
217 QUILL_LOG_DEBUG(log,
218 "{} Temperature : Average = {:.2f} ; StdDev = {:.2f} ; "
219 "Factor = {:.2f}\n",
220 "[Dynamics]", avgT, stdT,
221 varT / avgT / avgT * nFreeCoords / 2.0);
222}
223
225 double alpha = m_config.andersen_alpha;
226 double tCol = m_config.andersen_tcol;
227 double pCol = 1.0 - std::exp(-m_config.time_step / tCol);
228
229 AtomMatrix velocity = matter->getVelocities();
230 auto mass = matter->getMasses();
231
232 for (long i = 0; i < nAtoms; i++) {
233 if (eonc::rng::randomDouble() < pCol && !matter->getFixed(i)) {
234 for (int j = 0; j < 3; j++) {
235 double vOld = velocity(i, j);
236 const double vNew = (mass[i] > 0.0 && kB > 0.0 && temperature > 0.0)
237 ? std::sqrt(kB * temperature / mass[i]) *
238 eonc::rng::gaussRandom(0.0, 1.0)
239 : 0.0;
240 velocity(i, j) = std::sqrt(1.0 - alpha * alpha) * vOld + alpha * vNew;
241 }
242 }
243 }
244 matter->setVelocities(velocity);
245}
246
248 AtomMatrix velocity = matter->getVelocities();
249 auto mass = matter->getMasses();
250
251 for (long i = 0; i < nAtoms; i++) {
252 if (!matter->getFixed(i)) {
253 for (int j = 0; j < 3; j++) {
254 velocity(i, j) = (mass[i] > 0.0 && kB > 0.0 && temperature > 0.0)
255 ? std::sqrt(kB * temperature / mass[i]) *
256 eonc::rng::gaussRandom(0.0, 1.0)
257 : 0.0;
258 }
259 }
260 }
261 matter->setVelocities(velocity);
262}
263
265 AtomMatrix velocity = matter->getVelocities();
266 double kinE = matter->getKineticEnergy();
267 const double kinT =
268 (nFreeCoords > 0 && kB > 0.0) ? 2.0 * kinE / nFreeCoords / kB : 0.0;
269 if (!(kinT > 0.0) || !(temperature > 0.0)) {
270 return;
271 }
272 matter->setVelocities(velocity * std::sqrt(temperature / kinT));
273}
274
275void Dynamics::nhcChainHalfStep(AtomMatrix &vel, double &kinE) {
276 const double dt2 = 0.5 * dt;
277 const double dt4 = 0.25 * dt;
278 const double dt8 = 0.125 * dt;
279 const double q1 = m_config.nose_mass;
280 const double q2 = q1;
281 const double Temp = kB * temperature;
282 if (!(q1 > 0.0)) {
283 throw std::invalid_argument("thermostat.nose_mass must be positive");
284 }
285
286 // Martyna, Klein, Tuckerman JCP 97, 2635 (1992): G2 = (Q1 v_ξ1² − kT) / Q2.
287 auto g2 = [&]() { return (q1 * vxi1 * vxi1 - Temp) / q2; };
288 auto g1 = [&]() { return (2.0 * kinE - nFreeCoords * Temp) / q1; };
289
290 vxi2 += g2() * dt4;
291 vxi1 *= std::exp(-vxi2 * dt8);
292 vxi1 += g1() * dt4;
293 vxi1 *= std::exp(-vxi2 * dt8);
294 xi1 += vxi1 * dt2;
295 xi2 += vxi2 * dt2;
296 const double s = std::exp(-vxi1 * dt2);
297 vel *= s;
298 kinE *= s * s;
299 vxi1 *= std::exp(-vxi2 * dt8);
300 vxi1 += g1() * dt4;
301 vxi1 *= std::exp(-vxi2 * dt8);
302 vxi2 += g2() * dt4;
303}
304
308 const double dt2 = 0.5 * dt;
309 AtomMatrix vel = matter->getVelocities();
310 AtomMatrix pos = matter->getPositions();
311 double kinE = matter->getKineticEnergy();
312
313 nhcChainHalfStep(vel, kinE);
314
315 pos += vel * dt2;
316 matter->setPositions(pos);
317 AtomMatrix acc = matter->getAccelerations();
318 vel += acc * dt;
319 pos += vel * dt2;
320 matter->setPositions(pos);
321 matter->setVelocities(vel);
322 kinE = matter->getKineticEnergy();
323
324 nhcChainHalfStep(vel, kinE);
325
326 matter->setVelocities(vel);
327}
328
333 const double gamma = m_config.langevin_friction;
334 AtomMatrix pos = matter->getPositions();
335 AtomMatrix vel = matter->getVelocities();
336 AtomMatrix acc = matter->getAccelerations();
337 AtomMatrix noise = AtomMatrix::Zero(nAtoms, 3);
338 const auto mass = matter->getMasses();
339
340 // Zero a frozen axis by assignment. Multiplying by the free mask leaves
341 // a NaN in place, and setPositions stores every component.
342 auto holdFixed = [&](AtomMatrix &m) {
343 for (long i = 0; i < nAtoms; i++) {
344 for (int j = 0; j < 3; j++) {
345 if (matter->getFixed(i, j)) {
346 m(i, j) = 0.0;
347 }
348 }
349 }
350 };
351 auto addNoise = [&]() {
352 noise.setZero();
353 for (long i = 0; i < nAtoms; i++) {
354 for (int j = 0; j < 3; j++) {
355 if (matter->getFixed(i, j)) {
356 continue;
357 }
358 noise(i, j) = std::sqrt(4.0 * gamma * kB * temperature / dt / mass[i]) *
359 eonc::rng::gaussRandom(0.0, 1.0);
360 }
361 }
362 };
363
364 addNoise();
365 AtomMatrix friction = -gamma * vel;
366 holdFixed(friction);
367 holdFixed(acc);
368 acc += friction + noise;
369
370 vel += acc * 0.5 * dt;
371 holdFixed(vel);
372 pos += vel * dt;
373 matter->setPositions(pos);
374
375 acc = matter->getAccelerations();
376 addNoise();
377 friction = -gamma * vel;
378 holdFixed(friction);
379 holdFixed(acc);
380 acc += friction + noise;
381 vel += 0.5 * dt * acc;
382 holdFixed(vel);
383 matter->setVelocities(vel);
384}
385
386} // namespace eonc
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Definition Eigen.h:35
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
static const char NONE[]
Definition Dynamics.h:76
void setTemperature(double temperature)
Definition Dynamics.cpp:53
static const char LANGEVIN[]
Definition Dynamics.h:75
void setThermalVelocity()
Definition Dynamics.cpp:247
void langevinVerlet()
Langevin dynamics (velocity-Verlet with friction and random forces).
Definition Dynamics.cpp:332
void noseHooverVerlet()
Nose-Hoover chain thermostat (Martyna-Klein-Tuckerman algorithm).
Definition Dynamics.cpp:307
double temperature
Definition Dynamics.h:112
void oneStep(int stepNumber=-1)
Definition Dynamics.cpp:57
void rescaleVelocity()
Definition Dynamics.cpp:264
void andersenCollision()
Definition Dynamics.cpp:224
static const char ANDERSEN[]
Definition Dynamics.h:73
static const char NOSE_HOOVER[]
Definition Dynamics.h:74
void nhcChainHalfStep(AtomMatrix &vel, double &kinE)
One Martyna-Klein-Tuckerman chain half-step.
Definition Dynamics.cpp:275
Matter * matter
Definition Dynamics.h:107
Dynamics(Matter *matter, const DynamicsConfig &config)
Definition Dynamics.cpp:29
eonc::log::Scoped log
Definition Dynamics.h:114
static const char PILE[]
Definition Dynamics.h:77
static const char PIGLET[]
Definition Dynamics.h:78
void runPathIntegral()
Ring-polymer trajectory. Beads do not enter velocityVerlet.
Definition Dynamics.cpp:104
DynamicsConfig m_config
Definition Dynamics.h:108
void velocityVerlet()
Definition Dynamics.cpp:91
Ring-polymer NVT step.
void setAllBeads(const double *q)
void step(Potential &pot, const double *box, bool record)
constexpr bool io_ok(IoStatus s) noexcept
Definition ConFileIO.h:38
double randomDouble()
double gaussRandom(double avg, double std)
constexpr double kHbar
hbar in eV^0.5 amu^0.5 Angstrom, correctly rounded from the exact SI values (h = 6....
Definition Tunneling.h:37
RAII resource manager for the ARTn C library with global synchronization.
Configuration extracted from Parameters for Dynamics.
Definition Dynamics.h:26
double ecoOmegaMax
Highest physical frequency the economised springs reproduce.
std::string gleFile
Normal-mode GLE matrices.
double pileScale
Scales the critical PILE damping of the internal modes.
double pileTau
Centroid Langevin time, in the same time unit as dt.