Virtual run; used solely for dynamic dispatch.
29 {
30 bool swapMove;
31 double swap_accept = 0.0;
35 int consecutive_rejected_trials = 0;
36 double totalAccept = 0.0;
37 std::unique_ptr<Matter> minTrial = std::make_unique<Matter>(
pot,
params);
38 std::unique_ptr<Matter> swapTrial = std::make_unique<Matter>(
pot,
params);
39
42 QUILL_LOG_CRITICAL(
log,
"Failed to load {}", conFilename);
43 throw std::runtime_error("failed to load " + conFilename);
44 }
45
46
47 std::vector<long> Elements;
49 if (
params.basin_hopping_options.swap_probability > 0 &&
50 Elements.size() == 1) {
52 QUILL_LOG_CRITICAL(
log,
53 "error: [Basin Hopping] swap move probability must be "
54 "zero if there is only one element type\n");
55 throw std::invalid_argument(
56 "[Basin Hopping] swap_probability must be zero with one element type");
57 }
58
59 double randomProb =
60 params.basin_hopping_options.initial_random_structure_probability;
61 if (randomProb > 0.0) {
62 QUILL_LOG_DEBUG(
log,
"generating random structure with probability {:.4f}",
63 randomProb);
64 }
66 if (u <
params.basin_hopping_options.initial_random_structure_probability) {
68 for (
int i = 0; i <
current->numberOfFreeAtoms(); i++) {
69 for (int j = 0; j < 3; j++) {
71 }
72 }
73 randomPositions *=
current->getCell();
74 current->setPositionsFree(randomPositions);
75
77 }
78
81
83
84 double currentEnergy =
current->getPotentialEnergy();
85 double minimumEnergy = currentEnergy;
86
87 auto minimumEnergyStructure = std::make_shared<Matter>(
pot,
params);
88 *minimumEnergyStructure = *
current;
89 int nsteps =
params.basin_hopping_options.steps +
90 params.basin_hopping_options.quenching_steps;
91 long totalfc;
92
93 QUILL_LOG_DEBUG(
94 log,
"[Basin Hopping] {:4s} {:12s} {:12s} {:12s} {:4s} {:5s} {:5s}",
95 "step", "current", "trial", "global min", "fc", "ar", "md");
96 QUILL_LOG_DEBUG(
97 log,
"[Basin Hopping] {:4s} {:12s} {:12s} {:12s} {:4s} {:5s} {:5s}",
98 "----", "-------", "-----", "----------", "--", "--", "--");
99
100 int recentAccept = 0;
101 double curDisplacement =
params.basin_hopping_options.displacement;
102
103 for (int step = 0; step < nsteps; step++) {
104
105
107 step <
params.basin_hopping_options.steps) {
110 swapMove = true;
111 *minTrial = *swapTrial;
112 } else {
115
116 trial->setPositions(
current->getPositions() + displacement);
117 swapMove = false;
119
121 }
122
123 if (
params.debug_options.write_movies) {
125 QUILL_LOG_WARNING(
log,
"Failed to append trials movie frame");
126 }
127 }
128
129
130 minTrial->relax(true);
131
132
133 double deltaE = minTrial->getPotentialEnergy() - currentEnergy;
134 double p = 0.0;
135 if (step >=
params.basin_hopping_options.steps) {
136 if (deltaE <= 0.0) {
137 p = 1.0;
138 }
139 } else {
140 if (deltaE <= 0.0) {
141 p = 1.0;
142 } else {
143 p = std::exp(-deltaE /
144 (
params.main_options.temperature * 8.6173324e-5));
145 }
146 }
147
148 bool accepted = false;
150 accepted = true;
151 if (
params.basin_hopping_options.significant_structure) {
153 } else {
155 }
156 if (swapMove) {
157 swap_accept += 1;
158 }
159 if (step <
params.basin_hopping_options.steps) {
160 totalAccept += 1;
161 recentAccept += 1;
162 }
163
164 currentEnergy = minTrial->getPotentialEnergy();
165
166 if (currentEnergy < minimumEnergy) {
167 minimumEnergy = currentEnergy;
168 *minimumEnergyStructure = *minTrial;
170 QUILL_LOG_WARNING(
log,
"Failed to write min.con");
171 }
172 }
173
174 if (
params.basin_hopping_options.write_unique) {
175 bool newStructure = true;
177
178
180 params.structure_comparison_options.energy_difference) {
182 params.structure_comparison_options
183 .indistinguishable_atoms)) {
184 newStructure = false;
185 }
186 }
187 }
188
189 if (newStructure) {
191 auto currentCopy = std::make_shared<Matter>(
pot,
params);
194
195 char fname[128];
196 snprintf(fname, 128, "min_%.5i.con", step + 1);
198 QUILL_LOG_WARNING(
log,
"Failed to write {}", fname);
199 }
201
202 snprintf(fname, 128, "energy_%.5i.dat", step + 1);
204 {
205 std::ofstream fh(fname);
206 if (fh)
207 fh << std::format("{:.10e}\n", currentEnergy);
208 }
209 }
210 }
211
212 consecutive_rejected_trials = 0;
213 } else {
214 consecutive_rejected_trials++;
215 }
216
217 if (
params.debug_options.write_movies) {
219 QUILL_LOG_WARNING(
log,
"Failed to append basin-hopping movie frame");
220 }
221 }
222
223
224 char acceptReject[2];
225 acceptReject[1] = '\0';
226 if (accepted) {
227 acceptReject[0] = 'A';
228 } else {
229 acceptReject[0] = 'R';
230 }
231
232
233
234
235
236
237
238
239 if (minimumEnergy <
params.basin_hopping_options.stop_energy) {
240 break;
241 }
242
243 if (consecutive_rejected_trials ==
params.basin_hopping_options.jump_max &&
244 step <
params.basin_hopping_options.steps) {
245 consecutive_rejected_trials = 0;
247 for (
int j = 0; j <
params.basin_hopping_options.jump_steps; j++) {
251 if (
params.basin_hopping_options.significant_structure) {
254 }
255 currentEnergy =
current->getPotentialEnergy();
256 if (currentEnergy < minimumEnergy) {
257 minimumEnergy = currentEnergy;
258 *minimumEnergyStructure = *
current;
259 }
260 }
261 }
262
263 int nadjust =
params.basin_hopping_options.adjust_period;
264 double adjustFraction =
params.basin_hopping_options.adjust_fraction;
265 if ((step + 1) % nadjust == 0 &&
266 params.basin_hopping_options.adjust_displacement) {
267 double recentRatio =
268 static_cast<double>(recentAccept) / static_cast<double>(nadjust);
269 if (recentRatio >
params.basin_hopping_options.target_ratio) {
270 curDisplacement *= 1.0 + adjustFraction;
271 } else {
272 curDisplacement *= 1.0 - adjustFraction;
273 }
274
275
276
277 recentAccept = 0;
278 }
279 }
280
281
282 std::string resultsFilename("results.dat");
283
284 if (
params.debug_options.write_movies) {
285 std::string movieFilename("movie.con");
287 }
288
289 {
290 std::ofstream out(resultsFilename, std::ios::binary);
291 if (!out) {
292 QUILL_LOG_CRITICAL(
log,
"Failed to open {}", resultsFilename);
293 throw std::runtime_error("failed to open " + resultsFilename);
294 }
295 out << std::format("{} termination_reason\n", 0);
296 out << "GOOD termination_reason_text\n";
297 out << "basin_hopping job_type\n";
298 out << std::format("{:.12e} minimum_energy\n", minimumEnergy);
299 out << std::format(
"{} random_seed\n",
params.main_options.randomSeed);
300 const double nsteps_ratio =
params.basin_hopping_options.steps;
301 out << std::format("{:.3f} acceptance_ratio\n",
302 nsteps_ratio ? totalAccept / nsteps_ratio : 0.0);
303 if (
params.basin_hopping_options.swap_probability > 0) {
304 out << std::format(
305 "{:.3f} swap_acceptance_ratio\n",
307 }
308 out << std::format("{} total_normal_displacement_steps\n",
310 params.basin_hopping_options.quenching_steps);
311 out << std::format(
"{} total_jump_steps\n",
jump_count);
312 out << std::format(
"{} total_swap_steps\n",
swap_count);
313 out << std::format("{} total_force_calls\n",
315 out.close();
316 if (!out) {
317 QUILL_LOG_CRITICAL(
log,
"Failed to write {}", resultsFilename);
318 throw std::runtime_error("failed to write " + resultsFilename);
319 }
321 }
322
323 std::string productFilename("min.con");
324 if (
eonc::io::io_ok(minimumEnergyStructure->matter2con(productFilename))) {
326 } else {
327 QUILL_LOG_ERROR(
log,
"Failed to write {}", productFilename);
328 }
329
330
332}
std::vector< std::string > returnFiles
std::vector< std::shared_ptr< Matter > > uniqueStructures
void randomSwap(Matter *matter)
AtomMatrix displaceRandom(double maxDisplacement)
std::vector< double > uniqueEnergies
static PotRegistry & get() noexcept
Process-lifetime singleton.
double random(long newSeed=0)
void pushApart(std::shared_ptr< Matter > m1, double minDistance)
std::string getRelevantFile(std::string filename)
constexpr bool io_ok(IoStatus s) noexcept