Loading...
Searching...
No Matches
GlobalOptimizationJob.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/EonLogger.h"
15
16#include <format>
17#include <memory>
18#include <stdexcept>
19// #include "eon/MinimizationJob.h"
20#include "eon/Dynamics.h"
21#include "eon/HelperFunctions.h"
22
23#include <algorithm>
24#include <cmath>
25
26using namespace eonc::helpers;
27
28std::vector<std::string> GlobalOptimizationJob::run() {
29 // int status;
31 std::string reactant_passed =
32 eonc::helpers::getRelevantFile(params.main_options.conFilename);
33 std::vector<std::string> returnFiles;
34 // returnFiles.push_back(reactant_output);
35 auto matter_cur = std::make_unique<Matter>(pot, params);
36 auto matter_hop = std::make_unique<Matter>(pot, params);
37 if (!eonc::io::io_ok(matter_cur->con2matter(reactant_passed))) {
38 QUILL_LOG_CRITICAL(log, "Failed to load {}", reactant_passed);
39 throw std::runtime_error("failed to load " + reactant_passed);
40 }
41 bool converged;
42 long nstep = params.global_optimization_options.steps;
43 AtomMatrix rat_t(matter_cur->numberOfAtoms(), 3);
44 // double epot_hop;
45 // std::vector<double> earr;
46 // std::vector<Matter> allmatter;
47 QUILL_LOG_DEBUG(log, "\nBeginning minima hopping of {}",
48 reactant_passed.c_str());
49 // long fcalls;
50 QUILL_LOG_TRACE_L1(log, "fcalls= {}", matter_cur->getForceCalls());
51 QUILL_LOG_TRACE_L1(log, "epot= {:24.15E}", matter_cur->getPotentialEnergy());
52 converged =
53 matter_cur->relax(false, params.debug_options.write_movies,
54 params.main_options.checkpoint, "min", "matter_cur");
55 QUILL_LOG_DEBUG(log, "converged {}", (converged) ? "TRUE" : "FALSE");
56 // nlmin=0;
57 // if(nlmin==0)
58 earr.push_back(matter_cur->getPotentialEnergy());
59 *matter_hop = *matter_cur;
61 for (long istep = 1; istep <= nstep; istep++) {
62 // hoppingStep attempts to hop into a new state followed by a minimization
63 hoppingStep(istep, *matter_cur, *matter_hop);
64 // decisionStep decides to accept or reject this step if it escaped
65 decisionStep(*matter_cur, *matter_hop);
66 // analyzing what happened in this step if it escaped
67 analyze(*matter_cur, *matter_hop);
68 // reporting useful information about this hop
69 report(*matter_hop);
70 if (matter_cur->getPotentialEnergy() <
71 params.global_optimization_options.target_energy)
72 break;
73 }
74 for (size_t i = 0; i < earr.size(); i++) {
75 double earrim1;
76 if (i == 0) {
77 earrim1 = earr[0];
78 } else {
79 earrim1 = earr[i - 1];
80 }
81 earrfile << std::format("{:5} {:15.5f} {:15.5f} {:15.5f} ", i + 1,
82 earr[i], earr[i] - earr[0], earr[i] - earrim1);
83 }
84 // globopt.run();
85 return returnFiles;
86} // end of GlobalOptimizationJob::run
87
88void GlobalOptimizationJob::analyze(Matter &matter_cur, Matter &matter_hop) {
89 if (escapeResult == "failure") {
90 // matter_hop=matter_cur;
91 hoppingResult = "same";
92 return;
93 }
94 double epot = matter_hop.getPotentialEnergy();
95 size_t jlo = hunt(epot);
96 QUILL_LOG_TRACE_L1(log, "REZA: {}", jlo);
97 if (std::abs(epot - earr[jlo]) <
98 params.structure_comparison_options.energy_difference) {
99 hoppingResult = "already_visited";
100 } else {
101 hoppingResult = "new";
102 }
103 if (decisionResult == "accepted") {
104 double epot_hop = matter_hop.getPotentialEnergy();
105 matter_cur = matter_hop;
106 size_t jlo = hunt(matter_hop.getPotentialEnergy());
107 if (hoppingResult == "new" && jlo == 0 && epot_hop < earr[0]) {
108 QUILL_LOG_DEBUG(log,
109 "new lowest: nlmin, epot_hop, dE {:7d} {:15.5f} {:10.5f}",
110 1, epot_hop, epot_hop - earr[0]);
111 }
112 insert(matter_cur);
113 } else if (decisionResult == "rejected") {
114 // matter_hop=matter_cur;
115 } else {
117 QUILL_LOG_CRITICAL(
118 log,
119 "ERROR: new minimum is neither accepted nor rejected: client stops.");
120 throw std::runtime_error(
121 "[Global Optimization] new minimum is neither accepted nor rejected");
122 }
123}
124
126 Matter &matter_hop) {
127 // fSPDLOG_LOGGER_DEBUG(log, monfile,"%15.5f %15.5f %15.5f
128 // ",matter_hop.getPotentialEnergy(),
129 // matter_cur.getPotentialEnergy(),matter_hop.getPotentialEnergy()-matter_cur.getPotentialEnergy());
130 double epot, epot_hop;
131 epot = matter_cur.getPotentialEnergy();
132 epot_hop = matter_hop.getPotentialEnergy();
133 if (std::abs(epot_hop - epot) <
134 params.structure_comparison_options.energy_difference) {
135 escapeResult = "failure";
136 } else {
137 escapeResult = "success";
138 // fprintf(monfile,"%15.5f %15.5f %15.5f ", epot_hop, ediff,
139 // ekin);
140 }
141}
142
144 if (firstStep) {
145 firstStep = false;
146 return;
147 }
148 if (hoppingResult == "same") {
149 ekin *= beta1;
150 } else if (hoppingResult == "already_visited") {
151 ekin *= beta2;
152 } else if (hoppingResult == "new") {
153 ekin *= beta3;
154 } else {
156 QUILL_LOG_CRITICAL(log,
157 "ERROR: client does not know what to do with ekin.");
158 QUILL_LOG_CRITICAL(log, "ERROR: client stops in applyMoveFeedbackMD.");
159 throw std::runtime_error(std::format(
160 "[Global Optimization] unknown hoppingResult: {}", hoppingResult));
161 }
162}
163
165 if (decisionResult == "accepted") {
166 ediff *= alpha1;
167 } else {
168 ediff *= alpha2;
169 }
170}
171
173 char C1, C2;
174 double ekin_p;
175 if (hoppingResult == "same") {
176 C1 = 'S';
177 C2 = '-';
178 ekin_p = ekin * beta1;
179 } else if (hoppingResult == "already_visited") {
180 C1 = 'O';
181 ekin_p = ekin * beta2;
182 } else if (hoppingResult == "new") {
183 C1 = 'N';
184 ekin_p = ekin * beta3;
185 } else {
186 C1 = '-';
187 ekin_p = ekin;
188 }
189 if (decisionResult == "accepted") {
190 C2 = 'A';
191 } else if (decisionResult == "rejected") {
192 C2 = 'R';
193 } else {
194 C2 = '-';
195 }
196 double epot_hop = matter_hop.getPotentialEnergy();
197 double temp = (2.0 * ekin_p / params.constants.kB);
198 double dt = params.dynamics_options.time_step;
199 monfile << std::format(
200 "{:15.5f} {:15.5f} {:11} {:12.2f} {}{} {:5} {:5}", epot_hop,
201 ediff, static_cast<size_t>(temp), dt, C1, C2, fcallsMove, fcallsRelax);
202}
203
205 Matter &matter_hop) {
206 decisionResult = "unknown";
207 examineEscape(matter_cur, matter_hop);
208 if (escapeResult == "failure") {
209 // matter_hop[0] = matter_cur[0];
210 return;
211 }
212 if (params.global_optimization_options.decision_method == "npew") {
213 acceptRejectNPEW(matter_cur, matter_hop);
214 // GlobalOptimizationJob::update_minhop_param(matter_hop);
215 } else if (params.global_optimization_options.decision_method ==
216 "boltzmann") {
217 acceptRejectBoltzmann(matter_cur, matter_hop);
218 } else {
220 QUILL_LOG_CRITICAL(
221 log, "ERROR: accept/reject method not specified. client stops.");
222 throw std::invalid_argument(
223 std::format("[Global Optimization] unknown decision_method: {}",
224 params.global_optimization_options.decision_method));
225 }
227}
228
229// NPEW: non-probablistic energy window (used in minima hopping method)
231 Matter &matter_hop) {
232 if (matter_hop.getPotentialEnergy() <
233 matter_cur.getPotentialEnergy() + ediff) {
234 decisionResult = "accepted";
235 } else {
236 decisionResult = "rejected";
237 }
238}
239
241 Matter &matter_hop) {
242 double eTrial = matter_hop.getPotentialEnergy();
243 double eCurrent = matter_hop.getPotentialEnergy();
244
245 double deltaE = eTrial - eCurrent;
246 double kB = 8.6173324e-5;
247
248 double p;
249 if (deltaE <= 0.0) {
250 p = 1.0;
251 } else {
252 p = std::exp(-deltaE / params.main_options.temperature * kB);
253 }
254
255 if (randomDouble(1.0) < p) {
256 decisionResult = "accepted";
257 } else {
258 decisionResult = "rejected";
259 }
260}
261
262void GlobalOptimizationJob::hoppingStep(long istep, Matter &matter_cur,
263 Matter &matter_hop) {
264 bool converged;
265 matter_hop = matter_cur;
266 long fcalls1 = matter_hop.getForceCalls();
267 if (params.global_optimization_options.move_method == "md") {
269 mdescape(matter_hop);
270 } else if (params.global_optimization_options.move_method == "random") {
271 randomMove(matter_hop);
272 }
273 long fcalls2 = matter_hop.getForceCalls();
274 hoppingResult = "unknown";
275 converged =
276 matter_hop.relax(true, params.debug_options.write_movies,
277 params.main_options.checkpoint, "min", "matter_hop");
278 QUILL_LOG_DEBUG(log, "converged {}", (converged) ? "TRUE" : "FALSE");
279 long fcalls3 = matter_hop.getForceCalls();
280 fcallsMove = fcalls2 - fcalls1;
281 fcallsRelax = fcalls3 - fcalls2;
282}
283
285 // create a random displacement
286 AtomMatrix displacement;
287 displacement.resize(matter.numberOfAtoms(), 3);
288 displacement.setZero();
289 int num = matter.numberOfAtoms();
290
291 for (int i = 0; i < num; i++) {
292 double disp = params.basin_hopping_options.displacement;
293 if (!matter.getFixed(i)) {
294 for (int j = 0; j < 3; j++) {
295 if (params.basin_hopping_options.displacement_distribution ==
296 "uniform") {
297 displacement(i, j) = randomDouble(2 * disp) - disp;
298 } else if (params.basin_hopping_options.displacement_distribution ==
299 "gaussian") {
300 displacement(i, j) = gaussRandom(0.0, disp);
301 } else {
303 QUILL_LOG_CRITICAL(log, "Unknown displacement_distribution");
304 throw std::invalid_argument(std::format(
305 "[Global Optimization] unknown displacement_distribution: {}",
306 params.basin_hopping_options.displacement_distribution));
307 }
308 }
309 }
310 }
311 matter.setPositions(matter.getPositions() + displacement);
312}
313
315 int nmd;
316 double ekinc, epot, etot, epot0, etot0;
317 auto dyn = std::make_unique<Dynamics>(&matter, params);
318 velopt(matter);
319 epot = matter.getPotentialEnergy();
320 ekinc = matter.getKineticEnergy();
321 etot = ekinc + epot;
322 epot0 = epot;
323 etot0 = etot;
324 size_t nummax = 0, nummin = 0;
325 double enmin1 = 0.0, enmin2 = 0.0, en0000 = 0.0;
326 double econs_max = -1.E100, econs_min = 1.E100, devcon;
327 bool md_presumably_escaped = false;
328 QUILL_LOG_DEBUG(log, "MD {:5d} {:20.10E} {:15.5E} {:15.5E} ", 0,
329 epot - epot0, ekinc, etot - etot0);
330 nmd = 1000;
331 for (int imd = 1; imd <= nmd; imd++) {
332 enmin2 = enmin1;
333 enmin1 = en0000;
334 dyn->velocityVerlet();
335 epot = matter.getPotentialEnergy();
336 ekinc = matter.getKineticEnergy();
337 etot = ekinc + epot;
338 en0000 = epot - epot0;
339 if (enmin1 > enmin2 && enmin1 > en0000)
340 nummax += 1;
341 if (enmin1 < enmin2 && enmin1 < en0000)
342 nummin += 1;
343 QUILL_LOG_TRACE_L1(log,
344 "MD {:5d} {:15.5f} {:15.5f} {:12.2E} {:4} {:4}",
345 imd, epot - epot0, ekinc, etot - etot0, nummax, nummin);
346 econs_max = std::max(econs_max, ekinc + epot);
347 econs_min = std::min(econs_min, ekinc + epot);
348 if (nummin >= static_cast<size_t>(mdmin)) {
349 if (nummax != nummin)
350 QUILL_LOG_WARNING(log, "WARNING: iproc,nummin,nummax {} {}", nummin,
351 nummax);
352 md_presumably_escaped = true;
353 break;
354 }
355 } // end of loop over imd
356 devcon = econs_max - econs_min;
357 if (md_presumably_escaped) {
358 devcon = devcon / static_cast<double>(matter.numberOfFreeAtoms() * 3);
359 if (devcon / ekin < 2.E-3) {
360 params.dynamics_options.time_step *= 1.1;
361 } else {
362 params.dynamics_options.time_step /= 1.1;
363 }
364 } else {
365 QUILL_LOG_DEBUG(log, "TOO MANY MD STEPS ");
366 params.dynamics_options.time_step *= 2.0;
367 }
368}
369
371 AtomMatrix vat(matter.numberOfAtoms(), 3);
372 double tt1, tt2, tt3, vtot[3]; //, ekin_t;
373 int iat;
374 // matter.numberOfAtoms();
375 vtot[0] = 0.0;
376 vtot[1] = 0.0;
377 vtot[2] = 0.0;
378 for (iat = 0; iat < matter.numberOfAtoms(); iat++) {
379 tt1 = randomDouble();
380 tt2 = randomDouble();
381 tt3 = randomDouble();
382 vat(iat, 0) = (tt1 - 0.5) * 2.0;
383 vat(iat, 1) = (tt2 - 0.5) * 2.0;
384 vat(iat, 2) = (tt3 - 0.5) * 2.0;
385 vtot[0] += vat(iat, 0);
386 vtot[1] += vat(iat, 1);
387 vtot[2] += vat(iat, 2);
388 }
389 QUILL_LOG_DEBUG(log, "Linear momentum {:15.5E} {:15.5E} {:15.5E} ",
390 vtot[0], vtot[1], vtot[2]);
391 vtot[0] /= matter.numberOfAtoms();
392 vtot[1] /= matter.numberOfAtoms();
393 vtot[2] /= matter.numberOfAtoms();
394 for (iat = 0; iat < matter.numberOfAtoms(); iat++) {
395 vat(iat, 0) -= vtot[0];
396 vat(iat, 1) -= vtot[1];
397 vat(iat, 2) -= vtot[2];
398 }
399 matter.setVelocities(vat);
400 long nFreeCoords = matter.numberOfFreeAtoms() * 3;
401 double kinE = matter.getKineticEnergy();
402 double kB = params.constants.kB;
403 double kinT = (2.0 * kinE / nFreeCoords / kB);
404 double temperature = (2.0 * ekin / kB);
405 matter.setVelocities(vat * std::sqrt(temperature / kinT));
406}
407
408/*
409void ::rescaleVelocity()
410{
411 AtomMatrix velocity = matter->getVelocities();
412 double kinE = matter->getKineticEnergy();
413 double kinT = (2.0*kinE/nFreeCoords/kb);
414 matter->setVelocities(velocity*sqrt(temperature/kinT));
415}
416*/
417
419 double epot;
420 // vector<double> epot_hop;
421 size_t jlo, jlo_insert;
422 // vector<double>::iterator it;
423 epot = matter.getPotentialEnergy();
424 jlo = hunt(epot);
425 QUILL_LOG_DEBUG(log, "JLO= {} {:10.5f} ", jlo, std::abs(epot - earr[jlo]));
426 // it=earr.begin()+jlo;
427 // epot_hop.push_back(epot);
428 if (!(std::abs(epot - earr[jlo]) <
429 params.structure_comparison_options.energy_difference)) {
430 // earr.insert(it,epot_hop.begin(),epot_hop.end());
431 jlo_insert = jlo;
432 if (epot > earr[jlo])
433 jlo_insert++;
434 earr.insert(earr.begin() + jlo_insert, 1, epot);
435 }
436}
437
438size_t GlobalOptimizationJob::hunt(double epot) {
439 // epot is in interval [earr(jlo),earr(jlo+1)[ ; earr(0)=-Infinity ; earr(n+1)
440 // = Infinity
441 size_t jlo;
442 double de;
443 for (jlo = 0; jlo < earr.size(); jlo++)
444 if (epot < earr[jlo])
445 break;
446 if (jlo == earr.size())
447 jlo--;
448 de = std::abs(epot - earr[jlo]);
449 if (jlo > 0)
450 if (std::abs(epot - earr[jlo - 1]) < de)
451 jlo--; //{jlo--;de=abs(epot-earr[jlo]);}
452 // if(jlo!=earr.size()-1)
453 // if(abs(epot-earr[jlo+1])<params.structure_comparison_options.energy_difference)
454 // jlo++;
455 return jlo;
456}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
std::vector< std::string > run(void)
Virtual run; used solely for dynamic dispatch.
void analyze(Matter &, Matter &)
void decisionStep(Matter &, Matter &)
void examineEscape(Matter &, Matter &)
void hoppingStep(long, Matter &, Matter &)
void acceptRejectNPEW(Matter &, Matter &)
void acceptRejectBoltzmann(Matter &, Matter &)
std::shared_ptr< Potential > pot
Definition Job.h:55
Parameters params
Definition Job.h:54
double getPotentialEnergy() const
Definition Matter.cpp:446
long getForceCalls() const
Definition Matter.cpp:479
void setVelocities(const AtomMatrix &v)
Definition Matter.cpp:604
long int numberOfAtoms() const
Definition Matter.cpp:209
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:257
long int numberOfFreeAtoms() const
Definition Matter.cpp:475
void setPositions(const AtomMatrix &pos)
Definition Matter.cpp:273
double getKineticEnergy() const
Definition Matter.cpp:454
const AtomMatrix & getPositions() const
Definition Matter.cpp:236
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
Definition Matter.cpp:402
double randomDouble()
std::string getRelevantFile(std::string filename)
double gaussRandom(double avg, double std)
constexpr bool io_ok(IoStatus s) noexcept
Definition ConFileIO.h:38
quill::Logger * traceback() noexcept
Get or create the "_traceback" logger for traceback logging.
Definition EonLogger.h:88