Loading...
Searching...
No Matches
eonc::DynamicsSaddleSearch Class Reference

#include <DynamicsSaddleSearch.h>

Inheritance diagram for eonc::DynamicsSaddleSearch:

Public Member Functions

 DynamicsSaddleSearch (std::shared_ptr< Matter > matterPassed, const Parameters &parametersPassed)
 ~DynamicsSaddleSearch ()=default
int run ()
double getEigenvalue ()
AtomMatrix getEigenvector ()
std::string_view describeStatus (int status) const override
int getStatus () const override
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.
Public Member Functions inherited from eonc::SaddleSearchMethod
 SaddleSearchMethod (std::shared_ptr< Potential > potPassed, const Parameters &paramsPassed)
virtual ~SaddleSearchMethod ()
virtual int getIterationCount () const
virtual int getForceCalls () const

Public Attributes

double eigenvalue {0.0}
AtomMatrix eigenvector
double time {0.0}
std::shared_ptr< Matterproduct
std::shared_ptr< Matterreactant
std::shared_ptr< Mattersaddle
int status {0}

Private Attributes

eonc::log::Scoped log

Additional Inherited Members

Protected Attributes inherited from eonc::SaddleSearchMethod
std::shared_ptr< Potentialpot
const Parametersparams

Detailed Description

Definition at line 22 of file DynamicsSaddleSearch.h.

Constructor & Destructor Documentation

◆ DynamicsSaddleSearch()

eonc::DynamicsSaddleSearch::DynamicsSaddleSearch ( std::shared_ptr< Matter > matterPassed,
const Parameters & parametersPassed )
inline

Definition at line 24 of file DynamicsSaddleSearch.h.

26 : SaddleSearchMethod(nullptr, parametersPassed),
27 product{std::make_shared<Matter>(*matterPassed)},
28 reactant{std::make_shared<Matter>(*matterPassed)},
29 saddle{matterPassed} {
30 this->pot = matterPassed->getPotential();
31 eigenvector.resize(reactant->numberOfAtoms(), 3);
32 eigenvector.setZero();
33 };
std::shared_ptr< Matter > saddle
std::shared_ptr< Matter > reactant
std::shared_ptr< Matter > product
std::shared_ptr< Potential > pot
SaddleSearchMethod(std::shared_ptr< Potential > potPassed, const Parameters &paramsPassed)

◆ ~DynamicsSaddleSearch()

eonc::DynamicsSaddleSearch::~DynamicsSaddleSearch ( )
default

Member Function Documentation

◆ describeStatus()

std::string_view eonc::DynamicsSaddleSearch::describeStatus ( int status) const
inlineoverridevirtual

Implements eonc::SaddleSearchMethod.

Definition at line 39 of file DynamicsSaddleSearch.h.

39 {
41 }
static constexpr std::string_view statusMessage(int status)
Human-readable message for a status code.

◆ getEigenvalue()

double DynamicsSaddleSearch::getEigenvalue ( )
virtual

Implements eonc::SaddleSearchMethod.

Definition at line 337 of file DynamicsSaddleSearch.cpp.

◆ getEigenvector()

AtomMatrix DynamicsSaddleSearch::getEigenvector ( )
virtual

Implements eonc::SaddleSearchMethod.

Definition at line 339 of file DynamicsSaddleSearch.cpp.

339{ return eigenvector; }

◆ getStatus()

int eonc::DynamicsSaddleSearch::getStatus ( ) const
inlineoverridevirtual

Reimplemented from eonc::SaddleSearchMethod.

Definition at line 42 of file DynamicsSaddleSearch.h.

42{ return status; }

◆ refineTransition()

int DynamicsSaddleSearch::refineTransition ( const std::vector< std::shared_ptr< Matter > > & snapshots,
std::shared_ptr< Matter > product )

Binary search through MD snapshots to find the transition point.

Definition at line 306 of file DynamicsSaddleSearch.cpp.

308 {
309 int lo = 0;
310 int hi = static_cast<int>(snapshots.size()) - 1;
311 if (hi == 0) {
312 return 0;
313 }
314
315 QUILL_LOG_DEBUG(log, "refining transition time");
316
317 while ((hi - lo) > 1) {
318 int mid = lo + (hi - lo) / 2;
319 QUILL_LOG_DEBUG(log, "minimizing image {}", mid);
320 Matter snap(pot, params);
321 snap = *snapshots[mid];
322 snap.relax(false);
323
324 if (snap.compare(*reactant)) {
325 QUILL_LOG_DEBUG(log, "image {} minimizes to reactant", mid);
326 lo = mid;
327 } else {
328 QUILL_LOG_DEBUG(log, "image {} minimizes to product", mid);
329 *prod = snap;
330 hi = mid;
331 }
332 }
333
334 return (lo + hi) / 2;
335}

◆ run()

int DynamicsSaddleSearch::run ( void )
virtual

Implements eonc::SaddleSearchMethod.

Definition at line 23 of file DynamicsSaddleSearch.cpp.

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 =
31 eonc::helpers::loadMasses("masses.dat", saddle->numberOfAtoms());
32 saddle->setMasses(masses);
33 QUILL_LOG_DEBUG(log, "Applied mass weights");
34 } else {
35 QUILL_LOG_DEBUG(log, "No mass weights file found");
36 }
37
38 Dynamics dyn(saddle.get(), params);
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 // always start from the initial configuration
53 *saddle = *reactant;
54 dyn.setThermalVelocity();
55
56 // Dephase MD trajectory
57 for (int step = 1; step <= dephaseSteps; step++) {
58 dyn.oneStep(step);
59 }
60
61 // Check to see if a transition occured
62 Matter min(pot, params);
63 min = *saddle;
64 min.relax();
65
66 if (min.compare(*reactant)) {
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
77 BondBoost bondBoost(saddle.get(), params);
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) {
94 if (!eonc::io::io_ok(saddle->matter2con("dynamics", false))) {
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 // BUG FIX: was sharing ownership with saddle instead of copying
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) {
113 if (!eonc::io::io_ok(saddle->matter2con("dynamics", true))) {
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
121 product = std::make_shared<Matter>(*saddle);
122 product->relax(false, false);
123
124 if (!product->compare(*reactant)) {
125 QUILL_LOG_DEBUG(log, "Found new state");
126 int image = refineTransition(mdSnapshots, product);
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 // Subtract half the record interval to avoid systematic bias
134 time = mdTimes[image] -
135 params.saddle_search_options.dynamics.record_interval / 2.0;
136 QUILL_LOG_DEBUG(log, "Transition time {:.2f} fs",
137 time * params.constants.timeUnit);
138
139 NudgedElasticBand neb(reactant, product, params, pot);
140
141 if (!params.saddle_search_options.dynamics.linear_interpolation) {
142 QUILL_LOG_DEBUG(
143 log, "Interpolating initial band through MD transition state");
144 AtomMatrix reactantToSaddle =
145 saddle->pbc(saddle->getPositions() - reactant->getPositions());
146 AtomMatrix saddleToProduct =
147 saddle->pbc(product->getPositions() - saddle->getPositions());
148 QUILL_LOG_DEBUG(log, "Initial band saved to neb_initial_band.con");
149 if (!eonc::io::io_ok(
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 }
167 if (!eonc::io::io_ok(
168 neb.path[img]->matter2con("neb_initial_band.con", true))) {
169 QUILL_LOG_WARNING(log, "Failed to append neb_initial_band frame");
170 }
171 }
172 if (!eonc::io::io_ok(neb.path[neb.numImages + 1]->matter2con(
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");
180 if (!eonc::io::io_ok(
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++) {
185 if (!eonc::io::io_ok(
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
192 AtomMatrix mode;
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);
209 AtomMatrix bandDir =
210 saddle->pbc(neb.path[extremumImage + 1]->getPositions() -
211 neb.path[extremumImage]->getPositions());
212 saddle->setPositions(interpDist * bandDir +
213 saddle->getPositions());
214 mode = saddle->pbc(neb.path[extremumImage + 1]->getPositions() -
215 saddle->getPositions());
216 mode.normalize();
217 eonc::eigenmodeCompute(*minModeMethod, saddle, mode);
218 double ev = eonc::eigenmodeGetEigenvalue(*minModeMethod);
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);
238 AtomMatrix bandDir =
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() -
243 saddle->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;
253 *saddle = *neb.path[img];
254 mode = saddle->pbc(neb.path[img + 1]->getPositions() -
255 saddle->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");
270 if (!eonc::io::io_ok(saddle->matter2con("saddle_initial_guess.con"))) {
271 QUILL_LOG_WARNING(log, "Failed to write saddle_initial_guess.con");
272 }
273 MinModeSaddleSearch search = MinModeSaddleSearch(
274 saddle, mode, reactant->getPotentialEnergy(), params, pot);
275 int minModeStatus = search.run();
276
277 if (minModeStatus != MinModeSaddleSearch::STATUS_GOOD) {
278 QUILL_LOG_DEBUG(log, "error in min mode saddle search");
279 return minModeStatus;
280 }
281
282 eigenvalue = search.getEigenvalue();
283 eigenvector = search.getEigenvector();
284 QUILL_LOG_DEBUG(log, "eigenvalue: {:.3f}", eigenvalue);
285
286 double barrier =
287 saddle->getPotentialEnergy() - reactant->getPotentialEnergy();
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();
301 time = params.dynamics_options.steps * params.dynamics_options.time_step;
303}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
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[]
Definition BondBoost.h:77
VectorXd loadMasses(std::string filename, int nAtoms)
constexpr bool io_ok(IoStatus s) noexcept
Definition ConFileIO.h:38
std::shared_ptr< EigenmodeStrategy > buildEigenmodeStrategy(std::shared_ptr< Matter > matter, const Parameters &params, 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.

Member Data Documentation

◆ eigenvalue

double eonc::DynamicsSaddleSearch::eigenvalue {0.0}

Definition at line 47 of file DynamicsSaddleSearch.h.

47{0.0};

◆ eigenvector

AtomMatrix eonc::DynamicsSaddleSearch::eigenvector

Definition at line 48 of file DynamicsSaddleSearch.h.

◆ log

eonc::log::Scoped eonc::DynamicsSaddleSearch::log
private

Definition at line 59 of file DynamicsSaddleSearch.h.

◆ product

std::shared_ptr<Matter> eonc::DynamicsSaddleSearch::product

Definition at line 52 of file DynamicsSaddleSearch.h.

◆ reactant

std::shared_ptr<Matter> eonc::DynamicsSaddleSearch::reactant

Definition at line 53 of file DynamicsSaddleSearch.h.

◆ saddle

std::shared_ptr<Matter> eonc::DynamicsSaddleSearch::saddle

Definition at line 54 of file DynamicsSaddleSearch.h.

◆ status

int eonc::DynamicsSaddleSearch::status {0}

Definition at line 56 of file DynamicsSaddleSearch.h.

56{0};

◆ time

double eonc::DynamicsSaddleSearch::time {0.0}

Definition at line 50 of file DynamicsSaddleSearch.h.

50{0.0};

The documentation for this class was generated from the following files: