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);
42 QUILL_LOG_CRITICAL(
log,
"Failed to load {}", conFilename);
43 throw std::runtime_error(
"failed to load " + conFilename);
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");
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}",
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++) {
73 randomPositions *=
current->getCell();
74 current->setPositionsFree(randomPositions);
84 double currentEnergy =
current->getPotentialEnergy();
85 double minimumEnergy = currentEnergy;
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;
94 log,
"[Basin Hopping] {:4s} {:12s} {:12s} {:12s} {:4s} {:5s} {:5s}",
95 "step",
"current",
"trial",
"global min",
"fc",
"ar",
"md");
97 log,
"[Basin Hopping] {:4s} {:12s} {:12s} {:12s} {:4s} {:5s} {:5s}",
98 "----",
"-------",
"-----",
"----------",
"--",
"--",
"--");
100 int recentAccept = 0;
101 double curDisplacement =
params.basin_hopping_options.displacement;
103 for (
int step = 0; step < nsteps; step++) {
107 step <
params.basin_hopping_options.steps) {
111 *minTrial = *swapTrial;
116 trial->setPositions(
current->getPositions() + displacement);
123 if (
params.debug_options.write_movies) {
125 QUILL_LOG_WARNING(
log,
"Failed to append trials movie frame");
130 minTrial->relax(
true);
133 double deltaE = minTrial->getPotentialEnergy() - currentEnergy;
135 if (step >=
params.basin_hopping_options.steps) {
143 p = std::exp(-deltaE /
144 (
params.main_options.temperature * 8.6173324e-5));
148 bool accepted =
false;
151 if (
params.basin_hopping_options.significant_structure) {
159 if (step <
params.basin_hopping_options.steps) {
164 currentEnergy = minTrial->getPotentialEnergy();
166 if (currentEnergy < minimumEnergy) {
167 minimumEnergy = currentEnergy;
168 *minimumEnergyStructure = *minTrial;
170 QUILL_LOG_WARNING(
log,
"Failed to write min.con");
174 if (
params.basin_hopping_options.write_unique) {
175 bool newStructure =
true;
180 params.structure_comparison_options.energy_difference) {
182 params.structure_comparison_options
183 .indistinguishable_atoms)) {
184 newStructure =
false;
191 auto currentCopy = std::make_shared<Matter>(
pot,
params);
196 snprintf(fname, 128,
"min_%.5i.con", step + 1);
198 QUILL_LOG_WARNING(
log,
"Failed to write {}", fname);
202 snprintf(fname, 128,
"energy_%.5i.dat", step + 1);
205 std::ofstream fh(fname);
207 fh << std::format(
"{:.10e}\n", currentEnergy);
212 consecutive_rejected_trials = 0;
214 consecutive_rejected_trials++;
217 if (
params.debug_options.write_movies) {
219 QUILL_LOG_WARNING(
log,
"Failed to append basin-hopping movie frame");
224 char acceptReject[2];
225 acceptReject[1] =
'\0';
227 acceptReject[0] =
'A';
229 acceptReject[0] =
'R';
239 if (minimumEnergy <
params.basin_hopping_options.stop_energy) {
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) {
255 currentEnergy =
current->getPotentialEnergy();
256 if (currentEnergy < minimumEnergy) {
257 minimumEnergy = currentEnergy;
258 *minimumEnergyStructure = *
current;
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) {
268 static_cast<double>(recentAccept) /
static_cast<double>(nadjust);
269 if (recentRatio >
params.basin_hopping_options.target_ratio) {
270 curDisplacement *= 1.0 + adjustFraction;
272 curDisplacement *= 1.0 - adjustFraction;
282 std::string resultsFilename(
"results.dat");
284 if (
params.debug_options.write_movies) {
285 std::string movieFilename(
"movie.con");
290 std::ofstream out(resultsFilename, std::ios::binary);
292 QUILL_LOG_CRITICAL(
log,
"Failed to open {}", resultsFilename);
293 throw std::runtime_error(
"failed to open " + resultsFilename);
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) {
305 "{:.3f} swap_acceptance_ratio\n",
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",
317 QUILL_LOG_CRITICAL(
log,
"Failed to write {}", resultsFilename);
318 throw std::runtime_error(
"failed to write " + resultsFilename);
323 std::string productFilename(
"min.con");
324 if (
eonc::io::io_ok(minimumEnergyStructure->matter2con(productFilename))) {
327 QUILL_LOG_ERROR(
log,
"Failed to write {}", productFilename);