43 {
44 if (!initial) {
45 throw std::runtime_error("ParallelReplicaJob::runFromMatter: null Matter");
46 }
49
50 QUILL_LOG_DEBUG(
log,
"[ParallelReplica] Minimizing initial position");
53 QUILL_LOG_ERROR(
log,
"Failed to write reactant.con");
54 }
55
56 auto trajectory = std::make_shared<Matter>(
pot,
params);
59 BondBoost bondBoost(trajectory.get(),
params);
60
61 BondBoost *bias = nullptr;
62 if (
params.hyperdynamics_options().bias_potential ==
64 bondBoost.initialize();
65 trajectory->setBiasPotential(&bondBoost);
66 bias = &bondBoost;
67 }
68
70
71 int stateCheckInterval = static_cast<int>(
72 std::floor(
params.parallel_replica_options().state_check_interval /
73 params.dynamics_options().time_step +
74 0.5));
75 int recordInterval = static_cast<int>(
76 std::floor(
params.parallel_replica_options().record_interval /
77 params.dynamics_options().time_step +
78 0.5));
79 if (stateCheckInterval < 1)
80 stateCheckInterval = 1;
81 if (recordInterval < 1)
82 recordInterval = 1;
83
84 std::vector<std::shared_ptr<Matter>> mdSnapshots;
85 std::vector<double> mdTimes;
86 double transitionTime = 0;
88 size_t refineForceCalls = 0;
89
90 double simulationTime = 0.0;
92 QUILL_LOG_DEBUG(
93 log,
"[ParallelReplica] {:>8} {:>12} {:>10} {:>12} {:>12} {:>10}",
94 "Step", "Time (s)", "KE", "PE", "TE", "KinT");
95 } else {
96 QUILL_LOG_DEBUG(
98 "[ParallelReplica] {:>8} {:>12} {:>10} {:>10} {:>12} {:>12} {:>10}",
99 "Step", "Time (s)", "Boost", "KE", "PE", "TE", "KinT");
100 }
101
102 for (
int step = 1; step <=
params.dynamics_options().steps; step++) {
103 if (
params.hyperdynamics_options().bias_potential ==
105
106
107
108 bondBoost.advance();
109 }
110 dynamics.oneStep();
111 double boost = 1.0;
112 if (
params.hyperdynamics_options().bias_potential ==
114 double boostPotential = bondBoost.boost();
115 double kB =
params.constants().kB;
116 boost = std::exp(boostPotential / kB /
params.main_options().temperature);
117 simulationTime +=
params.dynamics_options().time_step * boost;
118 } else {
119 simulationTime +=
params.dynamics_options().time_step;
120 }
121
122 double kinE = trajectory->getKineticEnergy();
123 double potE = trajectory->getPotentialEnergy();
124 double kinT = (2.0 * kinE / (trajectory->numberOfFreeAtoms() * 3) /
126
127 if (step %
params.debug_options().write_movies_interval == 0) {
128 if (
params.hyperdynamics_options().bias_potential ==
131 "[ParallelReplica] {:>8} {:>12.4e} {:>10.4f} "
132 "{:>12.4f} {:>12.4f} {:>10.2f}",
133 step,
134 simulationTime *
params.constants().timeUnit * 1e-15,
135 kinE, potE, kinE + potE, kinT);
136 } else {
137 double boostPotential = bondBoost.boost();
138 QUILL_LOG_DEBUG(
140 "[ParallelReplica] {:>8} {:>12.4e} {:>10.3e} "
141 "{:>10.4f} {:>12.4f} {:>12.4f} {:>10.2f}",
142 step, simulationTime *
params.constants().timeUnit * 1e-15, boost,
143 kinE, potE + boostPotential, kinE + potE + boostPotential, kinT);
144 }
145 }
146
147
148 if (step % recordInterval == 0 &&
149 params.parallel_replica_options().refine_transition) {
150 auto snap = std::make_shared<Matter>(
pot,
params);
151 *snap = *trajectory;
152 mdSnapshots.push_back(std::move(snap));
153 mdTimes.push_back(simulationTime);
154 }
155
156
157 if (step % stateCheckInterval == 0 ||
158 step ==
params.dynamics_options().steps) {
159 QUILL_LOG_DEBUG(
log,
"[ParallelReplica] Checking for transition");
160
162 minimized = *trajectory;
163 minimized.relax();
164
165 if (!minimized.compare(*
reactant) && transitionTime == 0) {
166 QUILL_LOG_DEBUG(
log,
"[ParallelReplica] Transition occurred");
167
168 if (
params.parallel_replica_options().refine_transition &&
169 !mdSnapshots.empty() && !mdTimes.empty()) {
170 QUILL_LOG_DEBUG(
log,
"[ParallelReplica] Refining transition time");
171 int snapshotIndex;
172 {
173 eonc::ForceCallTimer timer(refineForceCalls);
175 }
176 if (snapshotIndex < 0)
177 snapshotIndex = 0;
178 if (snapshotIndex >= static_cast<int>(mdSnapshots.size()))
179 snapshotIndex = static_cast<int>(mdSnapshots.size()) - 1;
180
181 transitionTime = mdTimes[static_cast<size_t>(snapshotIndex)];
182 transitionStructure =
183 *mdSnapshots[static_cast<size_t>(snapshotIndex)];
184 } else {
185 transitionStructure = *trajectory;
186 transitionTime = simulationTime;
187 }
188 QUILL_LOG_DEBUG(
log,
"[ParallelReplica] Transition time: {:.3e} s",
189 transitionTime *
params.constants().timeUnit * 1e-15);
190
191
192 trajectory->assignKeepingBias(transitionStructure);
193
194 if (
params.parallel_replica_options().auto_stop) {
195 break;
196 }
197
198 }
else if (step + 1 ==
params.dynamics_options().steps &&
199 transitionTime == 0) {
200
201 if (
params.parallel_replica_options().refine_transition &&
202 !mdSnapshots.empty()) {
203 QUILL_LOG_DEBUG(
205 "[ParallelReplica] Simulation ended without seeing a transition");
206 QUILL_LOG_DEBUG(
207 log,
"[ParallelReplica] Refining anyways to prevent bias...");
208 {
209 eonc::ForceCallTimer timer(refineForceCalls);
211 }
212 }
213 transitionStructure = *trajectory;
214 }
215
216 mdSnapshots.clear();
217 mdTimes.clear();
218 }
219 }
220
221 std::unique_ptr<Matter> product;
222 if (transitionTime != 0) {
223 int decorrelationSteps = static_cast<int>(
224 std::floor(
params.parallel_replica_options().corr_time /
225 params.dynamics_options().time_step +
226 0.5));
227 if (decorrelationSteps < 0) {
228 decorrelationSteps = 0;
229 }
230 QUILL_LOG_DEBUG(
log,
"[ParallelReplica] Decorrelating: {} steps",
231 decorrelationSteps);
232 for (int dstep = 1; dstep <= decorrelationSteps; ++dstep) {
233 dynamics.oneStep(dstep);
234 }
235 product = std::make_unique<Matter>(
pot,
params);
236 *product = *trajectory;
237 product->relax();
239 QUILL_LOG_ERROR(
log,
"Failed to write product.con");
240 }
241 }
242
243
244 std::string resultsFilename("results.dat");
246 {
247 std::ofstream out(resultsFilename, std::ios::binary);
248 if (out) {
249 out << std::format(
250 "{} potential_type\n",
251 magic_enum::enum_name<PotType>(
params.potential_options().potential));
252 out << std::format(
"{} random_seed\n",
params.main_options().randomSeed);
253 out << std::format("{:f} potential_energy_reactant\n",
255 out << std::format("{} force_calls_refine\n", refineForceCalls);
256 out << std::format("{} total_force_calls\n",
258
259 if (transitionTime == 0) {
260 out << "0 transition_found\n";
261 out << std::format("{:e} simulation_time_s\n",
262 simulationTime *
params.constants().timeUnit *
263 1.0e-15);
264 } else {
265 out << "1 transition_found\n";
266 out << std::format("{:e} transition_time_s\n",
267 transitionTime *
params.constants().timeUnit *
268 1.0e-15);
269 out << std::format("{:e} correlation_time_s\n",
270 params.parallel_replica_options().corr_time *
271 params.constants().timeUnit * 1.0e-15);
272 out << std::format("{:f} potential_energy_product\n",
273 product->getPotentialEnergy());
274 }
275 out << std::format("{:f} speedup\n",
276 simulationTime /
277 (
params.dynamics_options().steps *
278 params.dynamics_options().time_step));
279 }
280 }
281
282
283
284
285
286 trajectory->setBiasPotential(nullptr);
287
288 return trajectory;
289}
static const char BOND_BOOST[]
void dephase(Matter &trajectory, BondBoost *bias)
int refineTransition(const std::vector< std::shared_ptr< Matter > > &snapshots, bool fake=false)
static PotRegistry & get() noexcept
Process-lifetime singleton.