eOn 3.2.0
Long-timescale dynamics: aKMC, NEB, parallel replica
☾
Toggle main menu visibility
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
20
namespace
eonc
{
21
22
const
char
Dynamics::ANDERSEN
[] =
"andersen"
;
23
const
char
Dynamics::NOSE_HOOVER
[] =
"nose_hoover"
;
24
const
char
Dynamics::LANGEVIN
[] =
"langevin"
;
25
const
char
Dynamics::NONE
[] =
"none"
;
26
const
char
Dynamics::PILE
[] =
"pile"
;
27
const
char
Dynamics::PIGLET
[] =
"piglet"
;
28
29
Dynamics::Dynamics
(
Matter
*matter_in,
const
DynamicsConfig
&
config
)
30
:
matter
{matter_in},
31
m_config
{
config
} {
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)) {
42
++
nFreeCoords
;
43
}
44
}
45
}
46
temperature
=
m_config
.temperature;
47
kB
=
m_config
.kB;
48
vxi1
=
vxi2
=
xi1
=
xi2
= 0.0;
49
}
50
51
Dynamics::~Dynamics
() =
default
;
52
53
void
Dynamics::setTemperature
(
double
temperature_in) {
54
temperature
= temperature_in;
55
}
56
57
void
Dynamics::oneStep
(
int
stepNumber) {
58
if
(
m_config
.thermostat_kind ==
ANDERSEN
) {
59
andersenCollision
();
60
velocityVerlet
();
61
}
else
if
(
m_config
.thermostat_kind ==
NOSE_HOOVER
) {
62
noseHooverVerlet
();
63
}
else
if
(
m_config
.thermostat_kind ==
LANGEVIN
) {
64
langevinVerlet
();
65
}
else
if
(
m_config
.thermostat_kind ==
NONE
) {
66
velocityVerlet
();
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
91
void
Dynamics::velocityVerlet
() {
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
104
void
Dynamics::runPathIntegral
() {
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
}
123
pathintegral::Options
opt;
124
opt.
beads
=
m_config
.path_beads;
125
opt.
temperature
=
temperature
;
126
opt.
kB
=
kB
;
127
opt.
hbar
=
tunneling::kHbar
;
128
opt.
dt
=
dt
;
129
opt.
springs
=
m_config
.path_springs ==
"eco"
?
pathintegral::Springs::Eco
130
:
pathintegral::Springs::Trotter
;
131
opt.
thermostat
=
m_config
.thermostat_kind ==
PIGLET
132
?
pathintegral::Thermostat::Piglet
133
:
pathintegral::Thermostat::Pile
;
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
161
void
Dynamics::run
() {
162
if
(
m_config
.thermostat_kind ==
PILE
||
m_config
.thermostat_kind ==
PIGLET
) {
163
runPathIntegral
();
164
return
;
165
}
166
double
sumT = 0.0, sumT2 = 0.0;
167
168
setThermalVelocity
();
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
224
void
Dynamics::andersenCollision
() {
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
247
void
Dynamics::setThermalVelocity
() {
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
264
void
Dynamics::rescaleVelocity
() {
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
275
void
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
307
void
Dynamics::noseHooverVerlet
() {
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
332
void
Dynamics::langevinVerlet
() {
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
Dynamics.h
Matrix3d
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Definition
Eigen.h:35
AtomMatrix
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition
Eigen.h:37
EonLogger.h
PathIntegral.h
Tunneling.h
eonc::Dynamics::kB
double kB
Definition
Dynamics.h:111
eonc::Dynamics::NONE
static const char NONE[]
Definition
Dynamics.h:76
eonc::Dynamics::nFreeCoords
long nFreeCoords
Definition
Dynamics.h:105
eonc::Dynamics::setTemperature
void setTemperature(double temperature)
Definition
Dynamics.cpp:53
eonc::Dynamics::LANGEVIN
static const char LANGEVIN[]
Definition
Dynamics.h:75
eonc::Dynamics::setThermalVelocity
void setThermalVelocity()
Definition
Dynamics.cpp:247
eonc::Dynamics::~Dynamics
~Dynamics()
eonc::Dynamics::langevinVerlet
void langevinVerlet()
Langevin dynamics (velocity-Verlet with friction and random forces).
Definition
Dynamics.cpp:332
eonc::Dynamics::noseHooverVerlet
void noseHooverVerlet()
Nose-Hoover chain thermostat (Martyna-Klein-Tuckerman algorithm).
Definition
Dynamics.cpp:307
eonc::Dynamics::temperature
double temperature
Definition
Dynamics.h:112
eonc::Dynamics::oneStep
void oneStep(int stepNumber=-1)
Definition
Dynamics.cpp:57
eonc::Dynamics::run
void run()
Definition
Dynamics.cpp:161
eonc::Dynamics::rescaleVelocity
void rescaleVelocity()
Definition
Dynamics.cpp:264
eonc::Dynamics::andersenCollision
void andersenCollision()
Definition
Dynamics.cpp:224
eonc::Dynamics::dt
double dt
Definition
Dynamics.h:110
eonc::Dynamics::vxi2
double vxi2
Definition
Dynamics.h:113
eonc::Dynamics::ANDERSEN
static const char ANDERSEN[]
Definition
Dynamics.h:73
eonc::Dynamics::NOSE_HOOVER
static const char NOSE_HOOVER[]
Definition
Dynamics.h:74
eonc::Dynamics::nhcChainHalfStep
void nhcChainHalfStep(AtomMatrix &vel, double &kinE)
One Martyna-Klein-Tuckerman chain half-step.
Definition
Dynamics.cpp:275
eonc::Dynamics::matter
Matter * matter
Definition
Dynamics.h:107
eonc::Dynamics::xi1
double xi1
Definition
Dynamics.h:113
eonc::Dynamics::Dynamics
Dynamics(Matter *matter, const DynamicsConfig &config)
Definition
Dynamics.cpp:29
eonc::Dynamics::log
eonc::log::Scoped log
Definition
Dynamics.h:114
eonc::Dynamics::PILE
static const char PILE[]
Definition
Dynamics.h:77
eonc::Dynamics::xi2
double xi2
Definition
Dynamics.h:113
eonc::Dynamics::vxi1
double vxi1
Definition
Dynamics.h:113
eonc::Dynamics::nAtoms
long nAtoms
Definition
Dynamics.h:105
eonc::Dynamics::PIGLET
static const char PIGLET[]
Definition
Dynamics.h:78
eonc::Dynamics::runPathIntegral
void runPathIntegral()
Ring-polymer trajectory. Beads do not enter velocityVerlet.
Definition
Dynamics.cpp:104
eonc::Dynamics::m_config
DynamicsConfig m_config
Definition
Dynamics.h:108
eonc::Dynamics::velocityVerlet
void velocityVerlet()
Definition
Dynamics.cpp:91
eonc::Matter
Definition
Matter.h:90
eonc::pathintegral::RingPolymer
Ring-polymer NVT step.
Definition
PathIntegral.h:92
eonc::pathintegral::RingPolymer::centroidVelocity
VectorXd centroidVelocity() const
Definition
PathIntegral.cpp:594
eonc::pathintegral::RingPolymer::setAllBeads
void setAllBeads(const double *q)
Definition
PathIntegral.cpp:418
eonc::pathintegral::RingPolymer::centroid
VectorXd centroid() const
Definition
PathIntegral.cpp:585
eonc::pathintegral::RingPolymer::step
void step(Potential &pot, const double *box, bool record)
Definition
PathIntegral.cpp:768
eonc::config
Definition
ParametersINI.cpp:42
eonc::io::io_ok
constexpr bool io_ok(IoStatus s) noexcept
Definition
ConFileIO.h:38
eonc::pathintegral::Springs::Eco
@ Eco
Definition
PathIntegral.h:42
eonc::pathintegral::Springs::Trotter
@ Trotter
Definition
PathIntegral.h:42
eonc::pathintegral::Thermostat::Piglet
@ Piglet
Definition
PathIntegral.h:46
eonc::pathintegral::Thermostat::Pile
@ Pile
Definition
PathIntegral.h:46
eonc::pot
Definition
ExternalCommand.h:22
eonc::rng::randomDouble
double randomDouble()
Definition
RandomNumbers.cpp:72
eonc::rng::gaussRandom
double gaussRandom(double avg, double std)
Definition
RandomNumbers.cpp:90
eonc::tunneling::kHbar
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
eonc
RAII resource manager for the ARTn C library with global synchronization.
Definition
ARTnSaddleSearch.cpp:23
eonc::DynamicsConfig
Configuration extracted from Parameters for Dynamics.
Definition
Dynamics.h:26
eonc::pathintegral::Options
Definition
PathIntegral.h:48
eonc::pathintegral::Options::thermostat
Thermostat thermostat
Definition
PathIntegral.h:55
eonc::pathintegral::Options::ecoOmegaMax
double ecoOmegaMax
Highest physical frequency the economised springs reproduce.
Definition
PathIntegral.h:61
eonc::pathintegral::Options::dt
double dt
Definition
PathIntegral.h:53
eonc::pathintegral::Options::gleFile
std::string gleFile
Normal-mode GLE matrices.
Definition
PathIntegral.h:64
eonc::pathintegral::Options::seed
std::uint64_t seed
Definition
PathIntegral.h:65
eonc::pathintegral::Options::pileScale
double pileScale
Scales the critical PILE damping of the internal modes.
Definition
PathIntegral.h:59
eonc::pathintegral::Options::beads
long beads
Definition
PathIntegral.h:49
eonc::pathintegral::Options::pileTau
double pileTau
Centroid Langevin time, in the same time unit as dt.
Definition
PathIntegral.h:57
eonc::pathintegral::Options::hbar
double hbar
Definition
PathIntegral.h:52
eonc::pathintegral::Options::temperature
double temperature
Definition
PathIntegral.h:50
eonc::pathintegral::Options::springs
Springs springs
Definition
PathIntegral.h:54
eonc::pathintegral::Options::kB
double kB
Definition
PathIntegral.h:51
client
Dynamics.cpp
Generated by
1.17.0
Generated by
Doxygen 1.17.0
Analytics by
Antics
provided by
TurtleTech ehf