Virtual run; used solely for dynamic dispatch.
32 {
33 bool swapMove;
34 double swap_accept = 0.0;
38
39 int quench_displacements = 0;
40 int consecutive_rejected_trials = 0;
41 double totalAccept = 0.0;
42 std::unique_ptr<Matter> minTrial = std::make_unique<Matter>(
pot,
params);
43 std::unique_ptr<Matter> swapTrial = std::make_unique<Matter>(
pot,
params);
44
45 std::string conFilename =
48 QUILL_LOG_CRITICAL(
log,
"Failed to load {}", conFilename);
49 throw std::runtime_error("failed to load " + conFilename);
50 }
51
52
53 std::vector<long> Elements;
55 if (
params.basin_hopping_options().swap_probability > 0 &&
56 Elements.size() == 1) {
58 QUILL_LOG_CRITICAL(
log,
59 "error: [Basin Hopping] swap move probability must be "
60 "zero if there is only one element type\n");
61 throw std::invalid_argument(
62 "[Basin Hopping] swap_probability must be zero with one element type");
63 }
64
65 double randomProb =
66 params.basin_hopping_options().initial_random_structure_probability;
67 if (randomProb > 0.0) {
68 QUILL_LOG_DEBUG(
log,
"generating random structure with probability {:.4f}",
69 randomProb);
70 }
72 if (u <
params.basin_hopping_options().initial_random_structure_probability) {
74 for (
int i = 0; i <
current->numberOfFreeAtoms(); i++) {
75 for (int j = 0; j < 3; j++) {
77 }
78 }
79 randomPositions *=
current->getCell();
80 current->setPositionsFree(randomPositions);
81
84 }
85
88
90
91 double currentEnergy =
current->getPotentialEnergy();
92 double minimumEnergy = currentEnergy;
93
94 auto minimumEnergyStructure = std::make_shared<Matter>(
pot,
params);
95 *minimumEnergyStructure = *
current;
96 int nsteps =
params.basin_hopping_options().steps +
97 params.basin_hopping_options().quenching_steps;
98
99 QUILL_LOG_DEBUG(
100 log,
"[Basin Hopping] {:4s} {:12s} {:12s} {:12s} {:4s} {:5s} {:5s}",
101 "step", "current", "trial", "global min", "fc", "ar", "md");
102 QUILL_LOG_DEBUG(
103 log,
"[Basin Hopping] {:4s} {:12s} {:12s} {:12s} {:4s} {:5s} {:5s}",
104 "----", "-------", "-----", "----------", "--", "--", "--");
105
106 int recentAccept = 0;
107 double curDisplacement =
params.basin_hopping_options().displacement;
108
109 for (int step = 0; step < nsteps; step++) {
110
111
113 params.basin_hopping_options().swap_probability &&
114 step <
params.basin_hopping_options().steps) {
117 swapMove = true;
118 *minTrial = *swapTrial;
119 } else {
122 if (step >=
params.basin_hopping_options().steps) {
123 quench_displacements++;
124 }
125
126 trial->setPositions(
current->getPositions() + displacement);
127 swapMove = false;
129 trial,
params.basin_hopping_options().push_apart_distance);
130
132 }
133
134 if (
params.debug_options().write_movies) {
136 QUILL_LOG_WARNING(
log,
"Failed to append trials movie frame");
137 }
138 }
139
140 minTrial->relax(true);
141
142 double deltaE = minTrial->getPotentialEnergy() - currentEnergy;
143 double p = 0.0;
144 if (step >=
params.basin_hopping_options().steps) {
145 if (deltaE <= 0.0) {
146 p = 1.0;
147 }
148 } else {
149
151 params.main_options().temperature);
152 }
153
154 bool accepted = false;
156 accepted = true;
157 if (
params.basin_hopping_options().significant_structure) {
159 } else if (swapMove) {
161 } else {
163 }
164 if (swapMove) {
165 swap_accept += 1;
166 }
167 if (step <
params.basin_hopping_options().steps) {
168 totalAccept += 1;
169 recentAccept += 1;
170 }
171
172 currentEnergy = minTrial->getPotentialEnergy();
173
174 if (currentEnergy < minimumEnergy) {
175 minimumEnergy = currentEnergy;
176 *minimumEnergyStructure = *minTrial;
178 QUILL_LOG_WARNING(
log,
"Failed to write min.con");
179 }
180 }
181
182 if (
params.basin_hopping_options().write_unique) {
183 bool newStructure = true;
185
186
188 params.structure_comparison_options().energy_difference) {
191 params.structure_comparison_options()
192 .indistinguishable_atoms)) {
193 newStructure = false;
194 }
195 }
196 }
197
198 if (newStructure) {
200 auto currentCopy = std::make_shared<Matter>(
pot,
params);
203
204 char fname[128];
205 snprintf(fname, 128, "min_%.5i.con", step + 1);
207 QUILL_LOG_WARNING(
log,
"Failed to write {}", fname);
208 }
210
211 snprintf(fname, 128, "energy_%.5i.dat", step + 1);
213 {
214 std::ofstream fh(fname);
215 if (fh)
216 fh << std::format("{:.10e}\n", currentEnergy);
217 }
218 }
219 }
220
221 consecutive_rejected_trials = 0;
222 } else {
223 consecutive_rejected_trials++;
224 }
225
226 if (
params.debug_options().write_movies) {
228 QUILL_LOG_WARNING(
log,
"Failed to append basin-hopping movie frame");
229 }
230 }
231
232 if (minimumEnergy <
params.basin_hopping_options().stop_energy) {
233 break;
234 }
235
236 if (consecutive_rejected_trials ==
237 params.basin_hopping_options().jump_max &&
238 step <
params.basin_hopping_options().steps) {
239 consecutive_rejected_trials = 0;
241 for (
int j = 0; j <
params.basin_hopping_options().jump_steps; j++) {
245
246
247 if (
params.basin_hopping_options().significant_structure) {
249 current,
params.basin_hopping_options().push_apart_distance);
251 currentEnergy =
current->getPotentialEnergy();
252 if (currentEnergy < minimumEnergy) {
253 minimumEnergy = currentEnergy;
254 *minimumEnergyStructure = *
current;
255 }
256 }
257 }
258 }
259
260 int nadjust =
params.basin_hopping_options().adjust_period;
261 double adjustFraction =
params.basin_hopping_options().adjust_fraction;
262
263 if (nadjust != 0 && (step + 1) % nadjust == 0 &&
264 params.basin_hopping_options().adjust_displacement) {
265 double recentRatio =
266 static_cast<double>(recentAccept) / static_cast<double>(nadjust);
267 if (recentRatio >
params.basin_hopping_options().target_ratio) {
268 curDisplacement *= 1.0 + adjustFraction;
269 } else {
270 curDisplacement *= 1.0 - adjustFraction;
271 }
272
273 recentAccept = 0;
274 }
275 }
276
277
278 std::string resultsFilename("results.dat");
279
280 if (
params.debug_options().write_movies) {
281 std::string movieFilename("movie.con");
283 }
284
285 {
289 env.job_type = "basin_hopping";
290 env.random_seed =
params.main_options().randomSeed;
291 env.extras.emplace_back("minimum_energy", minimumEnergy);
292 const double nsteps_ratio =
params.basin_hopping_options().steps;
293 env.extras.emplace_back("acceptance_ratio",
294 nsteps_ratio ? totalAccept / nsteps_ratio : 0.0);
295 if (
params.basin_hopping_options().swap_probability > 0) {
296 env.extras.emplace_back(
297 "swap_acceptance_ratio",
299 }
300 env.extras.emplace_back(
301 "total_normal_displacement_steps",
303 env.extras.emplace_back("total_jump_steps",
305 env.extras.emplace_back("total_swap_steps",
307 env.writeResultsDat(resultsFilename);
309 }
310
311 std::string productFilename("min.con");
312 if (
eonc::io::io_ok(minimumEnergyStructure->matter2con(productFilename))) {
314 } else {
315 QUILL_LOG_ERROR(
log,
"Failed to write {}", productFilename);
316 }
317
318
320}
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 double metropolisProbability(double de, double kB, double temperature)
Metropolis weight for energy change de.
static PotRegistry & get() noexcept
Process-lifetime singleton.
size_t total_force_calls() const noexcept
void pushApart(std::shared_ptr< Matter > m1, double minDistance)
std::string getRelevantFile(std::string filename)
constexpr bool io_ok(IoStatus s) noexcept
double random(long newSeed=0)
static JobResultEnvelope fromMinimization(RunStatus status, PotType pot, std::uint64_t fcalls, bool hasE, double energy)