Loading...
Searching...
No Matches
ReplicaDynamicsJob.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*/
13#include "eon/BaseStructures.h"
14#include "eon/Dynamics.h"
15#include "eon/ForceCallTimer.h"
16#include "eon/HelperFunctions.h"
17#include <stdexcept>
18
19#include <format>
20#include <fstream>
21
22namespace eonc {
23
24std::vector<std::string> ReplicaDynamicsJob::run() {
25 auto seed = std::make_shared<Matter>(pot, params);
26 std::string reactantFilename =
27 eonc::helpers::getRelevantFile(params.main_options().conFilename);
28 if (!eonc::io::io_ok(seed->con2matter(reactantFilename))) {
29 QUILL_LOG_CRITICAL(log, "Failed to load {}", reactantFilename);
30 throw std::runtime_error("failed to load " + reactantFilename);
31 }
32 (void)runFromMatter(std::move(seed));
33 return returnFiles;
34}
35
36std::shared_ptr<Matter>
37ReplicaDynamicsJob::runFromMatter(std::shared_ptr<Matter> initial) {
38 if (!initial) {
39 throw std::runtime_error("runFromMatter: initial Matter is null");
40 }
41 current = initial;
42 current->setPotential(pot);
43 reactant = std::make_shared<Matter>(pot, params);
44 saddle = std::make_shared<Matter>(pot, params);
45 product = std::make_shared<Matter>(pot, params);
46 finalState = std::make_shared<Matter>(pot, params);
47 finalStateTmp = std::make_shared<Matter>(pot, params);
48
50 time = 0.0;
51
52 QUILL_LOG_DEBUG(log, "Minimizing initial reactant");
53 {
55 *reactant = *current;
56 reactant->relax();
57 }
58
59 initExtra();
60
61 int status = dynamics();
62
63 saveData(status);
65
66 return current;
67}
68
70 Matter tmp(pot, params);
71 tmp = *curr;
72 tmp.relax(true);
73 return !tmp.compare(*react);
74}
75
77 const double dt = params.dynamics_options().time_step;
78 auto to_steps = [&](double interval) -> long {
79 if (!(dt > 0.0) || !(interval > 0.0)) {
80 return 1;
81 }
82 const long n = static_cast<long>(interval / dt);
83 return n < 1 ? 1 : n;
84 };
85 PrdClock c;
86 c.state_check =
87 to_steps(params.parallel_replica_options().state_check_interval);
88 c.record = to_steps(params.parallel_replica_options().record_interval);
89 if (c.record > c.state_check) {
90 c.record = c.state_check;
91 }
92 c.buffer = c.state_check / c.record;
93 if (c.buffer < 1) {
94 c.buffer = 1;
95 }
96 return c;
97}
98
100 const std::vector<std::shared_ptr<Matter>> &buff, Matter *react) {
101 QUILL_LOG_TRACE_L1(log, "Refining transition time.");
102 const long n = static_cast<long>(buff.size());
103 if (n <= 1) {
104 throw std::runtime_error(
105 "ReplicaDynamics refine: need at least two snapshots");
106 }
107
108 long lo = 0;
109 long hi = n - 1;
110
111 while ((hi - lo) > 1) {
112 long mid = lo + (hi - lo) / 2;
113 if (!checkState(buff[static_cast<size_t>(mid)].get(), react)) {
114 lo = mid;
115 } else {
116 hi = mid;
117 }
118 }
119
120 long idx = (lo + hi) / 2 + 1;
121 if (idx < 1) {
122 idx = 1;
123 }
124 if (idx >= n) {
125 idx = n - 1;
126 }
127 return idx;
128}
129
131 const double dt = params.dynamics_options().time_step;
132 if (!(dt > 0.0)) {
133 throw std::invalid_argument(
134 "ReplicaDynamicsJob::dephase: time_step must be positive");
135 }
136 long DephaseSteps =
137 static_cast<long>(params.parallel_replica_options().dephase_time / dt);
138 Dynamics dephaseDynamics(current.get(), params);
139 QUILL_LOG_DEBUG(log, "Dephasing for {:.2f} fs",
140 params.parallel_replica_options().dephase_time *
141 params.constants().timeUnit);
142
143 long step = 0, loop = 0;
144
145 while (step < DephaseSteps) {
146 long dephaseBufferLength = DephaseSteps - step;
147 if (dephaseBufferLength < 1) {
148 break;
149 }
150 loop++;
151 std::vector<std::shared_ptr<Matter>> dephaseBuffer(dephaseBufferLength);
152
153 for (long i = 0; i < dephaseBufferLength; i++) {
154 dephaseBuffer[i] = std::make_shared<Matter>(pot, params);
155 dephaseDynamics.oneStep();
156 *dephaseBuffer[i] = *current;
157 }
158
159 bool transitionFlag = checkState(current.get(), reactant.get());
160
161 if (transitionFlag) {
162 if (dephaseBuffer.size() < 2) {
163 AtomMatrix velocity = current->getVelocities();
164 velocity = velocity * (-1);
165 current->setVelocities(velocity);
166 continue;
167 }
168 long dephaseRefineStep = refine(dephaseBuffer, reactant.get());
169 QUILL_LOG_DEBUG(log, "loop = {}; dephase refine step = {}", loop,
170 dephaseRefineStep);
171 long ts = dephaseRefineStep - 1;
172 ts = (ts > 0) ? ts : 0;
173 QUILL_LOG_DEBUG(
174 log,
175 "Dephasing warning: in a new state, inverse the momentum and restart "
176 "from step {}",
177 step + ts);
178 *current = *dephaseBuffer[ts];
179 AtomMatrix velocity = current->getVelocities();
180 velocity = velocity * (-1);
181 current->setVelocities(velocity);
182 step = step + ts;
183 } else {
184 step = step + dephaseBufferLength;
185 QUILL_LOG_TRACE_L1(log, "Successful dephasing for {} steps", step);
186 }
187
188 const long loop_max = params.parallel_replica_options().dephase_loop_max;
189 if (loop_max > 0 && loop >= loop_max) {
190 QUILL_LOG_DEBUG(
191 log,
192 "Reach dephase loop maximum, stop dephasing! Dephased for {} steps",
193 step);
194 break;
195 }
196 QUILL_LOG_DEBUG(log, "Successfully Dephased for {:.2f} fs",
197 step * params.dynamics_options().time_step *
198 params.constants().timeUnit);
199 }
200}
201
203 std::string resultsFilename("results.dat");
204 returnFiles.push_back(resultsFilename);
205 size_t totalFCalls = minimizeFCalls + mdFCalls + dephaseFCalls + refineFCalls;
206
207 {
208 std::ofstream out(resultsFilename, std::ios::binary);
209 if (out) {
210 out << std::format(
211 "{} potential_type\n",
212 magic_enum::enum_name<PotType>(params.potential_options().potential));
213 out << std::format("{} random_seed\n", params.main_options().randomSeed);
214 out << std::format("{:f} potential_energy_reactant\n",
215 reactant->getPotentialEnergy());
216 out << std::format("{} total_force_calls\n", totalFCalls);
217 out << std::format("{} force_calls_dephase\n", dephaseFCalls);
218 out << std::format("{} force_calls_dynamics\n", mdFCalls);
219 out << std::format("{} force_calls_minimize\n", minimizeFCalls);
220 out << std::format("{} force_calls_refine\n", refineFCalls);
221 out << std::format("{} transition_found\n", (newStateFlag) ? 1 : 0);
222
223 if (newStateFlag) {
224 out << std::format("{:e} transition_time_s\n",
225 minCorrectedTime * 1.0e-15 *
226 params.constants().timeUnit);
227 out << std::format("{:f} potential_energy_product\n",
228 product->getPotentialEnergy());
229 out << std::format("{:f} moved_distance\n",
230 product->distanceTo(*reactant));
231 }
232
233 out << std::format("{:e} simulation_time_s\n",
234 time * 1.0e-15 * params.constants().timeUnit);
235 out << std::format("{:f} speedup\n",
236 time / params.dynamics_options().steps /
237 params.dynamics_options().time_step);
238 }
239 }
240
241 std::string reactantFilename("reactant.con");
242 returnFiles.push_back(reactantFilename);
243 if (!eonc::io::io_ok(reactant->matter2con(reactantFilename))) {
244 QUILL_LOG_ERROR(log, "Failed to write {}", reactantFilename);
245 }
246
247 if (newStateFlag) {
248 std::string productFilename("product.con");
249 returnFiles.push_back(productFilename);
250 if (!eonc::io::io_ok(product->matter2con(productFilename))) {
251 QUILL_LOG_ERROR(log, "Failed to write {}", productFilename);
252 }
253
254 if (params.parallel_replica_options().refine_transition) {
255 std::string saddleFilename("saddle.con");
256 returnFiles.push_back(saddleFilename);
257 if (!eonc::io::io_ok(saddle->matter2con(saddleFilename))) {
258 QUILL_LOG_ERROR(log, "Failed to write {}", saddleFilename);
259 }
260 }
261 }
262}
263
264} // namespace eonc
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
void oneStep(int stepNumber=-1)
Definition Dynamics.cpp:57
RAII wrapper for tracking force calls over a scope.
std::shared_ptr< Potential > pot
Definition Job.h:63
Parameters params
Definition Job.h:58
bool relax(bool quiet=false, bool writeMovie=false, bool checkpoint=false, std::string prefixMovie=std::string(), std::string prefixCheckpoint=std::string(), bool retainMovieFrames=false)
Definition Matter.cpp:334
bool compare(const Matter &matter, bool indistinguishable=false)
Definition Matter.cpp:184
std::vector< std::string > returnFiles
std::vector< std::string > run() override
Virtual run; used solely for dynamic dispatch.
long refine(const std::vector< std::shared_ptr< Matter > > &buff, Matter *reactant)
Binary search for the transition frame in a snapshot buffer.
std::shared_ptr< Matter > saddle
std::shared_ptr< Matter > product
void saveData(int status)
Write results.dat and structure con files.
virtual int dynamics()=0
The accelerated dynamics loop. Returns status (1 = transition, 0 = none).
std::shared_ptr< Matter > finalState
virtual void initExtra()
Create any extra matter objects needed by the subclass.
virtual void reportResults()
Post-dynamics logging (override for job-specific messages).
std::shared_ptr< Matter > reactant
bool checkState(Matter *current, Matter *reactant)
Minimize a copy of current and compare to reactant.
std::shared_ptr< Matter > runFromMatter(std::shared_ptr< Matter > initial)
Matter-first entry: seed geometry, skip con2matter load.
std::shared_ptr< Matter > current
void dephase()
Dephase the trajectory to ensure thermal independence.
std::shared_ptr< Matter > finalStateTmp
std::string getRelevantFile(std::string filename)
constexpr bool io_ok(IoStatus s) noexcept
Definition ConFileIO.h:38
RAII resource manager for the ARTn C library with global synchronization.
Clamped PRD clock: state_check, record, and buffer length are all >= 1.