23 {
24 std::vector<std::shared_ptr<Matter>> mdSnapshots;
25 std::vector<double> mdTimes;
26 QUILL_LOG_DEBUG(
log,
"Starting dynamics NEB saddle search");
27
28 if (std::filesystem::exists("masses.dat")) {
29 QUILL_LOG_DEBUG(
log,
"Found mass weights file");
30 Eigen::VectorXd masses =
33 QUILL_LOG_DEBUG(
log,
"Applied mass weights");
34 } else {
35 QUILL_LOG_DEBUG(
log,
"No mass weights file found");
36 }
37
39 QUILL_LOG_DEBUG(
40 log,
"Initializing velocities from Maxwell-Boltzmann distribution");
41 dyn.setTemperature(
params.saddle_search_options.dynamics.temperature);
42 dyn.setThermalVelocity();
43
44 int dephaseSteps =
45 static_cast<int>(std::floor(
params.parallel_replica_options.dephase_time /
46 params.dynamics_options.time_step +
47 0.5));
48
49 while (true) {
50
51 QUILL_LOG_DEBUG(
log,
"Dephasing: {} steps", dephaseSteps);
52
54 dyn.setThermalVelocity();
55
56
57 for (int step = 1; step <= dephaseSteps; step++) {
58 dyn.oneStep(step);
59 }
60
61
64 min.relax();
65
67 QUILL_LOG_DEBUG(
log,
"Dephasing successful");
68 break;
69 } else {
70 QUILL_LOG_DEBUG(
log,
"Transition occured during dephasing; Restarting");
71 dephaseSteps /= 2;
72 if (dephaseSteps < 1)
73 dephaseSteps = 1;
74 }
75 }
76
78 if (
params.hyperdynamics_options.bias_potential ==
80 QUILL_LOG_DEBUG(
log,
"Initializing Bond Boost");
81 bondBoost.initialize();
82 }
83
84 int checkInterval = static_cast<int>(
85 params.saddle_search_options.dynamics.state_check_interval /
86 params.dynamics_options.time_step +
87 0.5);
88 int recordInterval =
89 static_cast<int>(
params.saddle_search_options.dynamics.record_interval /
90 params.dynamics_options.time_step +
91 0.5);
92
93 if (
params.debug_options.write_movies) {
95 QUILL_LOG_WARNING(
log,
"Failed to write dynamics movie header");
96 }
97 }
98
99 for (
int step = 1; step <=
params.dynamics_options.steps; step++) {
100 dyn.oneStep(step);
101
102 if (recordInterval != 0 && step % recordInterval == 0) {
103 QUILL_LOG_DEBUG(
104 log,
"recording configuration at step {} time {:.3f}", step,
105 step *
params.dynamics_options.time_step *
params.constants.timeUnit);
106
107 auto snapshot = std::make_shared<Matter>(*
saddle);
108 mdSnapshots.push_back(snapshot);
109 mdTimes.push_back(step *
params.dynamics_options.time_step);
110 }
111
112 if (
params.debug_options.write_movies) {
114 QUILL_LOG_WARNING(
log,
"Failed to append dynamics movie frame");
115 }
116 }
117
118 if (step % checkInterval == 0) {
119 QUILL_LOG_DEBUG(
log,
"Minimizing trajectory, step {}", step);
120
123
125 QUILL_LOG_DEBUG(
log,
"Found new state");
127 *
saddle = *mdSnapshots[image];
128 QUILL_LOG_DEBUG(
log,
"Found transition at snapshot image {}", image);
129 for (int ii = 0; ii < static_cast<int>(mdTimes.size()); ii++) {
130 QUILL_LOG_DEBUG(
log,
"MDTimes[{}] = {:.3f}", ii,
131 mdTimes[ii] *
params.constants.timeUnit);
132 }
133
134 time = mdTimes[image] -
135 params.saddle_search_options.dynamics.record_interval / 2.0;
136 QUILL_LOG_DEBUG(
log,
"Transition time {:.2f} fs",
138
140
141 if (!
params.saddle_search_options.dynamics.linear_interpolation) {
142 QUILL_LOG_DEBUG(
143 log,
"Interpolating initial band through MD transition state");
148 QUILL_LOG_DEBUG(
log,
"Initial band saved to neb_initial_band.con");
150 neb.path[0]->matter2con("neb_initial_band.con", false))) {
151 QUILL_LOG_WARNING(
log,
"Failed to write neb_initial_band.con");
152 }
153 int mid = neb.numImages / 2 + 1;
154 for (int img = 1; img <= neb.numImages; img++) {
155 if (img < mid) {
156 double frac = static_cast<double>(img) / static_cast<double>(mid);
157 neb.path[img]->setPositions(
reactant->getPositions() +
158 frac * reactantToSaddle);
159 } else if (img > mid) {
160 double frac = static_cast<double>(img - mid) /
161 static_cast<double>(neb.numImages - mid + 1);
162 neb.path[img]->setPositions(
saddle->getPositions() +
163 frac * saddleToProduct);
164 } else {
165 neb.path[img]->setPositions(
saddle->getPositions());
166 }
168 neb.path[img]->matter2con("neb_initial_band.con", true))) {
169 QUILL_LOG_WARNING(
log,
"Failed to append neb_initial_band frame");
170 }
171 }
173 "neb_initial_band.con", true))) {
174 QUILL_LOG_WARNING(
log,
175 "Failed to append neb_initial_band endpoint");
176 }
177 } else {
178 QUILL_LOG_DEBUG(
179 log,
"Linear interpolation between minima used for initial band");
181 neb.path[0]->matter2con("neb_initial_band.con", false))) {
182 QUILL_LOG_WARNING(
log,
"Failed to write neb_initial_band.con");
183 }
184 for (int j = 1; j <= neb.numImages + 1; j++) {
186 neb.path[j]->matter2con("neb_initial_band.con", true))) {
187 QUILL_LOG_WARNING(
log,
"Failed to append neb_initial_band frame");
188 }
189 }
190 }
191
193 if (
params.neb_options.max_iterations > 0) {
194 auto minModeMethod =
196
197 neb.compute();
198 neb.printImageData(true);
199 int extremumImage = -1;
200 int jExt = 0;
201 for (jExt = 0; jExt < neb.numExtrema; jExt++) {
202 if (neb.extremumCurvature[jExt] <
203 params.saddle_search_options.dynamics.max_init_curvature) {
204 extremumImage =
205 static_cast<int>(std::floor(neb.extremumPosition[jExt]));
206 *
saddle = *neb.path[extremumImage];
207 double interpDist = neb.extremumPosition[jExt] -
208 static_cast<double>(extremumImage);
210 saddle->pbc(neb.path[extremumImage + 1]->getPositions() -
211 neb.path[extremumImage]->getPositions());
212 saddle->setPositions(interpDist * bandDir +
214 mode =
saddle->pbc(neb.path[extremumImage + 1]->getPositions() -
216 mode.normalize();
219 QUILL_LOG_DEBUG(
log,
"extrema #{} has eigenvalue {:.8f}",
220 jExt + 1, ev);
221
222 if (ev < 0) {
223 QUILL_LOG_DEBUG(
224 log,
"chose image {} (extrema #{}) as extremum image",
225 extremumImage, jExt + 1);
226 break;
227 } else {
228 extremumImage = -1;
229 }
230 }
231 }
232
233 if (extremumImage != -1) {
234 *
saddle = *neb.path[extremumImage];
235 double interpDist =
236 neb.extremumPosition[jExt] - static_cast<double>(extremumImage);
237 QUILL_LOG_DEBUG(
log,
"interpDistance {}", interpDist);
239 saddle->pbc(neb.path[extremumImage + 1]->getPositions() -
240 neb.path[extremumImage]->getPositions());
241 saddle->setPositions(interpDist * bandDir +
saddle->getPositions());
242 mode =
saddle->pbc(neb.path[extremumImage + 1]->getPositions() -
244 mode.normalize();
245 } else {
246 QUILL_LOG_DEBUG(
247 log,
"no maxima found, using max energy non-endpoint image");
248 double maxEnergy = -std::numeric_limits<double>::infinity();
249 for (int img = 1; img <= neb.numImages; img++) {
250 double U = neb.path[img]->getPotentialEnergy();
251 if (U > maxEnergy) {
252 maxEnergy = U;
254 mode =
saddle->pbc(neb.path[img + 1]->getPositions() -
256 mode.normalize();
257 }
258 }
259 if (maxEnergy <= reactant->getPotentialEnergy()) {
260 QUILL_LOG_DEBUG(
log,
"warning: no barrier found");
262 }
263 }
264 } else {
265 neb.maxEnergyImage = neb.numImages / 2 + 1;
266 }
267
268 QUILL_LOG_DEBUG(
269 log,
"Initial saddle guess saved to saddle_initial_guess.con");
271 QUILL_LOG_WARNING(
log,
"Failed to write saddle_initial_guess.con");
272 }
273 MinModeSaddleSearch search = MinModeSaddleSearch(
275 int minModeStatus = search.
run();
276
278 QUILL_LOG_DEBUG(
log,
"error in min mode saddle search");
279 return minModeStatus;
280 }
281
285
286 double barrier =
288 QUILL_LOG_DEBUG(
log,
"found barrier of {:.3f}", barrier);
289 mdSnapshots.clear();
290 mdTimes.clear();
292 } else {
293 QUILL_LOG_DEBUG(
log,
"Still in original state");
294 mdTimes.clear();
295 mdSnapshots.clear();
296 }
297 }
298 }
299
300 mdSnapshots.clear();
303}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
int refineTransition(const std::vector< std::shared_ptr< Matter > > &snapshots, std::shared_ptr< Matter > product)
Binary search through MD snapshots to find the transition point.
static const char BOND_BOOST[]
AtomMatrix getEigenvector()
@ STATUS_BAD_MD_TRAJECTORY_TOO_SHORT
VectorXd loadMasses(std::string filename, int nAtoms)
constexpr bool io_ok(IoStatus s) noexcept
std::shared_ptr< EigenmodeStrategy > buildEigenmodeStrategy(std::shared_ptr< Matter > matter, const Parameters ¶ms, std::shared_ptr< Potential > pot)
Build the eigenmode solver from parameters.
void eigenmodeCompute(EigenmodeStrategy &s, std::shared_ptr< Matter > matter, AtomMatrix direction)
Dispatch compute() to the active variant.
double eigenmodeGetEigenvalue(EigenmodeStrategy &s)
Dispatch getEigenvalue() to the active variant.