Loading...
Searching...
No Matches
AMS.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*/
12
14#include <cstddef>
15#include <format>
16#include <fstream>
17#include <iostream>
18#include <iterator>
19#include <stdexcept>
20#include <vector>
21
22namespace bp = boost::process;
23
25 : Potential(PotType::AMS, p) {
26 // Get the values from the configuration
27 // All the parameter values convert to lowercase in generate_run
28 this->engine = p.ams_options.engine;
30 this->model = p.ams_options.model;
31 this->xc = p.ams_options.xc;
33 this->basis = p.ams_options.basis;
34 this->engine_setup = generate_run(p);
35 // Environment
36 // TODO: Add more checks for how this can be set
37 if (p.ams_options.env.amshome.empty() &&
38 p.ams_options.env.scm_tmpdir.empty() &&
39 p.ams_options.env.scmlicense.empty() &&
40 p.ams_options.env.scm_pythondir.empty() &&
41 p.ams_options.env.amsbin.empty() &&
42 p.ams_options.env.amsresources.empty()) {
43 nativenv = boost::this_process::environment();
44 } else {
45 nativenv = boost::this_process::environment();
46 // Some of these can derive from the others
47 nativenv["AMSHOME"] = p.ams_options.env.amshome;
48 nativenv["SCM_TMPDIR"] = p.ams_options.env.scm_tmpdir;
49 nativenv["SCMLICENSE"] = p.ams_options.env.scmlicense;
50 nativenv["SCM_PYTHONDIR"] = p.ams_options.env.scm_pythondir;
51 nativenv["AMSBIN"] = p.ams_options.env.amsbin;
52 nativenv["AMSRESOURCES"] = p.ams_options.env.amsresources;
53 nativenv["PATH"] += p.ams_options.env.amsbin;
54 }
55 // Do not pass "" in the config files
56 // std::cout<<nativenv["PATH"].to_string()<<std::endl;
57 amsevals = 0;
58 // TODO: Optimize and reuse existing files Currently each Matter will
59 // recreate the folders It should instead figure out if results exist and
60 // use them
61 this->first_run = true;
62 // Determine if the engine supports restarts
63 if (engine == "MOPAC") {
64 this->can_restart = false;
65 this->cjob = "amsResults";
66 this->pjob = "amsResults";
67 } else {
68 this->can_restart = true;
69 this->cjob = "firstRun";
70 this->pjob = "secondRun";
71 }
72 return;
73}
74
75void AMS::cleanMemory(void) { return; }
76
78
79namespace {
80
81// The driver script, written afresh before every AMS invocation.
82constexpr const char *kRunScript = "run_AMS.sh";
83
84const char *elementArray[] = {
85 "Unknown", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na",
86 "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V",
87 "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br",
88 "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag",
89 "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr",
90 "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu",
91 "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi",
92 "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", NULL};
93
94// guess the atom type from the atomic mass,
95std::string mass2atom(double atomicmass) {
96 return elementArray[int(atomicmass + .5)];
97}
98
99int symbol2atomicNumber(char const *symbol) {
100 int i = 0;
101
102 while (elementArray[i] != NULL) {
103 if (strcmp(symbol, elementArray[i]) == 0) {
104 return i;
105 }
106 i++;
107 }
108 // invalid symbol
109 return -1;
110}
111
112char const *atomicNumber2symbol(int n) {
113 // The trailing NULL terminates the table, so it bounds the valid range.
114 if (n < 0 || static_cast<std::size_t>(n) + 1 >= std::size(elementArray)) {
115 throw std::runtime_error(
116 std::format("AMS knows no element symbol for atomic number {}", n));
117 }
118 return elementArray[n];
119}
120} // namespace
121
123 boost::asio::io_context amsRun;
124 std::future<std::string> run_out_future, run_err_future;
125 std::string runout, runerr;
126 if (chmod(kRunScript, S_IRWXU) != 0) {
127 throw std::runtime_error(
128 std::format("Could not make {} executable", kRunScript));
129 }
130 nativenv["AMS_JOBNAME"] = cjob;
131 // assert(validate_order() == true); // TODO: Debug only
132 bp::child c(std::string(kRunScript),
133 nativenv, // set the input
134 bp::std_in.close(), // no input
135 bp::std_out > run_out_future, // STDOUT
136 bp::std_err > run_err_future, // STDERR
137 amsRun);
138 amsRun.run();
139 runout = run_out_future.get();
140 runerr = run_err_future.get();
141 if (runerr.find("ERROR") != std::string::npos) {
142 bp::spawn("cat myrestart.in");
143 bp::spawn("cat run_AMS.sh");
144 throw std::runtime_error("\n AMS error while running");
145 } else {
146 this->amsevals = amsevals + 1;
147 // std::cout << "Run completed normally" << std::endl;
148 // std::cerr << "NORMAL TERMINATION\n";
149 }
150}
151
152double AMS::extract_scalar_rkf(std::string key) {
153 // The logic here handles the extraction asynchronously, as we lack
154 // interest in processing this output line by line, and would prefer to
155 // have it all in one place
156 std::string execString;
157 std::vector<std::string> execDat;
158 boost::asio::io_context rkf;
159 std::future<std::string> rkf_out_future, rkf_err_future;
160 std::string rkfout, rkferr;
161 double xval, x;
162 std::vector<double> extracted;
163 execString = std::format("dmpkf {}.results/{}.rkf AMSResults%{}", this->cjob,
164 this->engine_lower, key);
165 // std::cout << execString << "\n";
166 // Extract
167 bp::child c(execString, nativenv, // execute with the environment
168 bp::std_in.close(), // no input
169 bp::std_out > rkf_out_future, // STDOUT
170 bp::std_err > rkf_err_future, // STDERR
171 rkf);
172
173 rkf.run(); // this blocks until the end of the command
174 // Populate the strings
175 rkfout = rkf_out_future.get();
176 rkferr = rkf_err_future.get();
177 if (rkferr.find("ERROR") != std::string::npos) {
178 throw std::runtime_error(std::format(
179 "\n AMS error while extracting {}, got:\n {}", key, rkferr));
180 } else {
181 // std::cout << "Extracting " << key << std::endl;
182 }
183
184 execDat = absl::StrSplit(rkfout, '\n');
185 // Is energy or some other scalar property
186 // First 3 lines hold the headers
187 // [0] = "AMSResults "
188 // [1] = "Energy "
189 // [2] = " 1 1 2"
190 // [3] = " 0.135012547958282714E+000"
191 // [4] = ""
192 if (execDat.size() < 4) {
193 throw std::runtime_error(
194 std::format("\n AMS returned {} lines for {}, too few to hold a value",
195 execDat.size(), key));
196 }
197 if (absl::SimpleAtod(execDat[3], &x)) {
198 xval = x * this->energyConversion;
199 // std::cout << std::format(
200 // "\n Got {:.4f} Hartree from AMS and converted to {:.4f} eV\n", x,
201 // xval);
202 return xval;
203 } else {
204 throw std::runtime_error(
205 std::format("\n Expected {}, got {} instead", key, execDat[3]));
206 }
207 // Never reach here
208 throw std::runtime_error("Generic AMS dmpkf scalar error \n");
209}
210
211std::vector<double> AMS::extract_cartesian_rkf(std::string key) {
212 std::string execString;
213 std::vector<std::string> execDat, innerDat;
214 boost::asio::io_context rkf;
215 std::future<std::string> rkf_out_future, rkf_err_future;
216 std::string rkfout, rkferr;
217 double felem, x;
218 std::vector<double> extracted;
219 execString = std::format("dmpkf {}.results/{}.rkf AMSResults%{}", this->cjob,
220 this->engine_lower, key);
221 // std::cout << execString << "\n";
222
223 // Extract
224 bp::child c(execString, nativenv, // execute with the environment
225 bp::std_in.close(), // no input
226 bp::std_out > rkf_out_future, // STDOUT
227 bp::std_err > rkf_err_future, // STDERR
228 rkf);
229
230 rkf.run(); // this blocks until the end of the command
231 // Populate the strings
232 rkfout = rkf_out_future.get();
233 rkferr = rkf_err_future.get();
234
235 if (rkferr.find("ERROR") != std::string::npos) {
236 throw std::runtime_error(std::format(
237 "\n AMS error while extracting {}, got:\n {}", key, rkferr));
238 } else {
239 // std::cout << "Extracting " << key << std::endl;
240 }
241
242 execDat = absl::StrSplit(rkfout, '\n');
243 // Assume gradients or some other x y z property
244 for (int i = 3; i < execDat.size(); i++) {
245 // There exist one per atom, the number of which equals N
246 // The first three lines hold the header files as before
247 std::vector<std::string> strrow = absl::StrSplit(execDat[i], ' ');
248 for (auto elem : strrow) {
249 // This loop uses an auto variable since some of the elements of strrow
250 // often evaluate to ' '
251 // TODO: Optimize, perhaps with a regex
252 // [0] = ""
253 // [1] = ""
254 // [2] = ""
255 // [3] = "0.798885933949825835E-004"
256 // [4] = ""
257 // [5] = "-0.755400038198822616E-004"
258 // [6] = ""
259 // [7] = ""
260 // [8] = "0.457005740252359742E-004"
261 if (!elem.empty() && absl::SimpleAtod(elem, &x)) {
262 felem = x * this->forceConversion;
263 // std::cout << std::format(
264 // "\n Gradient element={:.4f} Hartree/Bohr from AMS\n Force "
265 // "element={:.4f} eV/Angstrom\n", x, felem);
266 extracted.emplace_back(felem);
267 }
268 }
269 }
270 // // Debug
271 // int counter = 0;
272 // for (int a = 0; a < N * 3; a++) {
273 // std::cout << std::format("{:.25e} ", forces[a]);
274 // counter++;
275 // if (counter % 3 == 0) {
276 // std::cout << std::endl;
277 // }
278 // }
279 return extracted;
280}
281
282void AMS::updateCoord(long N, const double *R) {
283 // The logic used here requires us to update the PREVIOUS job, since that
284 // functions as the one from which the calculation restarts
285 // Only the third line presents vague complexity
286 // https://www.scm.com/doc/Scripting/Commandline_Tools/KF_command_line_utilities.html
287 std::ofstream updCoord;
288 std::string execString, coordDump, newCoord;
289 std::vector<std::string> execDat;
290 boost::asio::io_context coordio;
291 std::future<std::string> err, rdump;
292 std::vector<double> gradients;
293 // Prep new run
294 // Get the previous run's coordinates
295 execString = std::format("dmpkf {}.results/ams.rkf Molecule%Coords", pjob);
296 // std::cout << execString << "\n";
297 // Store Coordinates
298 // TODO: Simplify this, we only need the first few lines
299 bp::child cprog(execString, nativenv, bp::std_in.close(), bp::std_out > rdump,
300 bp::std_err > err, coordio);
301 coordio.run();
302 execDat = absl::StrSplit(rdump.get(), '\n');
303 // Get the first three lines
304 int counter = 0;
305 for (auto j : execDat) {
306 if (counter >= 3) {
307 break;
308 } else {
309 absl::StrAppend(&newCoord, j, "\n");
310 }
311 counter++;
312 }
313 // Put the rest of the coordinates
314 counter = 0;
315 for (int a = 0; a < N * 3; a++) {
316 absl::StrAppend(&newCoord, (R[a] * lengthConversion), " ");
317 counter++;
318 if (counter % 3 == 0) {
319 absl::StrAppend(&newCoord, "\n");
320 }
321 }
322 coordDump = "#!/bin/sh\n udmpkf ";
323 absl::StrAppend(&coordDump, pjob, ".results/ams.rkf <<EOF\n", newCoord,
324 "EOF");
325 // std::cout << coordDump;
326 updCoord.open("updCoord.sh", std::ios::trunc);
327 if (!updCoord) {
328 throw std::runtime_error("Could not open updCoord.sh for writing");
329 }
330 updCoord << coordDump;
331 updCoord.close();
332 if (!updCoord) {
333 throw std::runtime_error("Could not write the coordinates to updCoord.sh");
334 }
335 // bp::spawn("chmod +x updCoord.sh");
336 if (chmod("updCoord.sh", S_IRWXU) != 0) {
337 throw std::runtime_error("Could not make updCoord.sh executable");
338 }
339 bp::child cuprog("updCoord.sh", nativenv, bp::std_err > bp::null);
340 cuprog.wait();
341 return;
342}
343
345 std::string tmp;
346 // std::cout << std::format("\nEntered Switch:\nCurrent:{}, Previous:{}\n",
347 // cjob, pjob);
348 tmp = this->cjob;
349 this->cjob = this->pjob;
350 this->pjob = tmp;
351 // std::cout << std::format("\nSwitched\n Current:{}, Previous:{}\n", cjob,
352 // pjob);
353}
354
356 std::string restart_formatter;
357 if (can_restart) {
358 restart_formatter = R"(
359 EngineRestart {0}.results/{1}.rkf
360 LoadSystem
361 File {0}.results/ams.rkf
362 Section Molecule
363 End
364 )";
365 } else {
366 restart_formatter = R"(
367 LoadSystem
368 File {0}.results/ams.rkf
369 Section Molecule
370 End
371 )";
372 }
373 std::string restart_data = std::format(restart_formatter, pjob, engine_lower);
374 restartFrom.open("myrestart.in", std::ios::trunc);
375 if (!restartFrom) {
376 throw std::runtime_error("Could not open myrestart.in for writing");
377 }
378 restartFrom << restart_data;
379 restartFrom.close();
380 if (!restartFrom) {
381 throw std::runtime_error("Could not write the restart block to "
382 "myrestart.in");
383 }
384 return;
385}
386
387void AMS::copyForces(long N, const std::vector<double> &frc, double *F) {
388 // A short AMS run yields fewer than 3N values; copying blindly would hand
389 // the optimizer whatever lies past the end of the vector.
390 if (frc.size() < static_cast<std::size_t>(3 * N)) {
391 throw std::runtime_error(
392 std::format("\n AMS returned {} gradient components, expected {}",
393 frc.size(), 3 * N));
394 }
395 for (long i = 0; i < 3 * N; i++) {
396 F[i] = frc[i];
397 }
398}
399
400void AMS::force(long N, const double *R, const int *atomicNrs, double *F,
401 double *U, double *variance, const double *box) {
402 variance = nullptr;
403 if (not can_restart or first_run) {
404 // std::cout << std::format("\nCAN_RESTART:{} FIRST_RUN:{}\n", can_restart,
405 // first_run); This holds true for all engines with no restart Also if an
406 // engine supports being restarted, the first run needs this
407 passToSystem(N, R, atomicNrs, box);
408 runAMS();
409 copyForces(N, extract_cartesian_rkf("Gradients"), F);
410 *U = extract_scalar_rkf("Energy");
411 // Update, will still not matter for those without restarts
412 if (can_restart) {
413 first_run = false;
414 // We need the "previous job" to pre-populate
415 // clang-format off
416 // std::cout<<std::format("\nMoving {} to {} before switching during the first job\n", cjob, pjob);
417 const auto copyOptions = std::filesystem::copy_options::overwrite_existing
418 | std::filesystem::copy_options::recursive
419 ;
420 // clang-format on
421 std::filesystem::copy(std::format("./{}.results/", cjob),
422 std::format("./{}.results/", pjob), copyOptions);
423 }
424 return;
425 } else {
426 // std::cout << std::format("\nCAN_RESTART:{} FIRST_RUN:{}\n", can_restart,
427 // first_run);
428 smallSys(N, R, atomicNrs, box); // writes run_AMS.sh
429 updateCoord(N, R); // updates coordinates in previous job
430 write_restart(); // writes restart file using previous job
431 runAMS();
432 copyForces(N, extract_cartesian_rkf("Gradients"), F);
433 *U = extract_scalar_rkf("Energy");
434 switchjob(); // toggles the jobs
435 return;
436 }
437 // Never reach here
438 throw std::runtime_error("Generic AMS force error \n");
439}
440
441void AMS::passToSystem(long N, const double *R, const int *atomicNrs,
442 const double *box)
443// Creating the standard input file that the AMS driver reads
444{
445 std::ofstream out(kRunScript, std::ios::trunc);
446 if (!out) {
447 throw std::runtime_error(
448 std::format("Could not open {} for writing", kRunScript));
449 }
450
451 out << "#!/bin/sh\n";
452 out << std::format("export AMS_JOBNAME={}\n", cjob);
453 out << "$AMSBIN/ams --delete-old-results <<eor\n";
454 out << "Task SinglePoint\n";
455 out << "System\n";
456 out << " Atoms\n";
457 for (long i = 0; i < N; i++) {
458 out << std::format(" {}\t{:.19f}\t{:.19f}\t{:.19f}\n",
459 atomicNumber2symbol(atomicNrs[i]), R[i * 3 + 0],
460 R[i * 3 + 1], R[i * 3 + 2]);
461 }
462 out << " End\n";
463 if (not model.empty() || not forcefield.empty()) {
464 out << " Lattice\n";
465 for (int i = 0; i < 3; i++) {
466 out << std::format(" {:.19f}\t{:.19f}\t{:.19f}\n", box[i * 3 + 0],
467 box[i * 3 + 1], box[i * 3 + 2]);
468 }
469 out << " End\n";
470 }
471 out << "End\n";
472 out << engine_setup;
473 out << "Properties\n";
474 out << " Gradients\n";
475 out << "End\n";
476 if (can_restart and not first_run) {
477 out << "@include myrestart.in\n";
478 }
479 out << "eor";
480
481 finishRunScript(out);
482 return;
483}
484
485void AMS::smallSys(long N, const double *R, const int *atomicNrs,
486 const double *box)
487// Creating the truncated input file that the AMS driver reads
488{
489 std::ofstream out(kRunScript, std::ios::trunc);
490 if (!out) {
491 throw std::runtime_error(
492 std::format("Could not open {} for writing", kRunScript));
493 }
494
495 out << "#!/bin/sh\n";
496 out << std::format("export AMS_JOBNAME={}\n", cjob);
497 out << "$AMSBIN/ams --delete-old-results <<eor\n";
498 out << "Task SinglePoint\n";
500 out << "Properties\n";
501 out << " Gradients\n";
502 out << "End\n";
503 out << "@include myrestart.in\n";
504 out << "eor";
505
506 finishRunScript(out);
507 return;
508}
509
510void AMS::finishRunScript(std::ofstream &out) {
511 out.close();
512 if (!out) {
513 throw std::runtime_error(
514 std::format("Could not write the AMS input to {}", kRunScript));
515 }
516 if (chmod(kRunScript, S_IRWXU) != 0) {
517 throw std::runtime_error(
518 std::format("Could not make {} executable", kRunScript));
519 }
520}
521
522std::string AMS::generate_run(const Parameters &p) {
523 std::string engine_block; // Shadows the class variable
524 // TODO: Use args everywhere, cleaner logic
525 // Ensure capitals and existence
526 engine.empty()
527 ? throw std::runtime_error("AMS Engine is required \n")
528 : std::transform(engine.begin(), engine.end(), engine.begin(), ::toupper);
529 // engine functions uniquely, it serves as a filename, so we store
530 // engine_lower as a lowercase version too
532 std::transform(engine.begin(), engine.end(), engine_lower.begin(), ::tolower);
533 // Prepare the block
534 if (engine == "MOPAC") {
535 model.empty()
536 ? throw std::runtime_error("MOPAC needs a model\n")
537 : std::transform(model.begin(), model.end(), model.begin(), ::toupper);
538 std::string engine_formatter = R"(
539 Engine {}
540 Model {}
541 EndEngine
542)";
543 engine_block = std::format(engine_formatter, engine, model);
544 return engine_block;
545 } else if (engine == "ADF" || engine == "BAND") {
546 basis.empty()
547 ? throw std::runtime_error("ADF/BAND need a basis\n")
548 : std::transform(basis.begin(), basis.end(), basis.begin(), ::toupper);
549 xc.empty() ? throw std::runtime_error("ADF/BAND need a functional\n")
550 : std::transform(xc.begin(), xc.end(), xc.begin(), ::toupper);
551 std::string engine_formatter = R"(
552 Engine {}
553 Basis
554 Type {}
555 End
556
557 XC
558 {}
559 End
560 EndEngine
561 )";
562 engine_block = std::format(engine_formatter, engine, basis, xc);
563 return engine_block;
564 } else if (engine == "DFTB") {
565 resources.empty() ? throw std::runtime_error("DFTB need resources\n")
566 : std::transform(resources.begin(), resources.end(),
567 resources.begin(), ::toupper);
568 std::string engine_formatter = R"(
569 Engine {}
570 ResourcesDir {}
571 EndEngine
572 )";
573 engine_block = std::format(engine_formatter, engine, resources);
574 return engine_block;
575 } else if (engine == "reaxff") {
576 forcefield.empty() ? throw std::runtime_error("REAXFF needs a forcefield\n")
577 : std::transform(forcefield.begin(), forcefield.end(),
578 forcefield.begin(), ::toupper);
579
580 std::string engine_formatter = R"(
581 Engine {}
582 ForceField {}
583 EndEngine
584 )";
585 engine_block = std::format(engine_formatter, engine, forcefield);
586 return engine_block;
587 } else if (engine == "FORCEFIELD") {
588 std::string engine_formatter = R"(
589 Engine {}
590 EndEngine
591 )";
592 engine_block = std::format(engine_formatter, engine);
593 return engine_block;
594 }
595
596 // Never reach here
597 throw std::runtime_error("Generic AMS engine error \n");
598}
599
600/*
601**
602** DEBUGGER
603**
604** The set of functions below, when activated, include asserts to ensure
605*equality with AMS_IO
606* These work.
607*/
608
609// clang-format off
610
611// void AMS::recieveFromSystem(long N, double *F, double *U) {
612
613// FILE *in;
614// double junkF;
615// char junkChar[256];
616// double forceX;
617// double forceY;
618// double forceZ;
619// double index;
620// char line[256];
621
622// in = fopen("ams_output", "r");
623
624// while (fgets(line, sizeof(line), in)) {
625
626// if (strcmp(line, " CALCULATION RESULTS\n") ==
627// 0) { // Finding the Energy in the output file
628
629// fscanf(in, "%s %s %s %lf", junkChar, junkChar, junkChar, U);
630// *U = *U * 27.2114; // Energy in hartree to eV
631// }
632// if (strcmp(line, " Index Atom d/dx d/dy "
633// " d/dz\n") == 0) { // Finding the forces
634// for (int i = 0; i < N; i++) {
635// fscanf(in, "%lf %s %lf %lf %lf", &index, &junkChar, &forceX, &forceY,
636// &forceZ);
637// F[int(i) * 3 + 0] = -forceX;
638// F[int(i) * 3 + 1] = -forceY;
639// F[int(i) * 3 + 2] = -forceZ;
640// // AMS gives gradients, not forces, hence the change.
641// }
642// }
643// }
644// for (int i = 0; i < 3 * N; i++) {
645// F[i] = F[i] * 51.4220862; // Forces from hartree/bohr to eV/Angstrom
646// }
647
648// fclose(in);
649// return;
650// }
651
652// void AMS::force(long N, const double *R, const int *atomicNrs, double *F,
653// double *U, const double *box, int nImages = 1) {
654// passToSystem(N, R, atomicNrs, box);
655// system("chmod +x run_AMS.sh");
656// system("./run_AMS.sh >> ams_output"); // Run a single point AMS calculation
657// // and write the results into ams_output
658// recieveFromSystem(N, F, U);
659// runAMS();
660// double energ = extract_scalar_rkf("Energy"); // Sets energy
661// // assert(fabs(*U-this->energy)<DBL_EPSILON); // Equality == doesn't work well
662// // for floats
663// assert(fabs(*U - energ) < 1e-5); // Equality == doesn't work well for floats
664// // *U = this->energy;
665// // this->forces.clear(); // TODO: Slow!
666// std::vector<double> frc = extract_cartesian_rkf("Gradients"); // Sets forces
667// double *ftest = frc.data();
668// for (int i = 0; i < forces.size(); i++) {
669// assert(fabs(F[i] - ftest[i]) < 1e-5);
670// }
671// *U = energ;
672// F = ftest;
673// return;
674// }
675
676// Ordering which works, scopes are correct
677// void AMS::force(long N, const double *R, const int *atomicNrs, double *F,
678// double *U, const double *box, int nImages = 1) {
679// // Somehow broken, even though this matches the prior configuration
680// passToSystem(N, R, atomicNrs, box);
681// runAMS();
682// std::vector<double> frc = extract_cartesian_rkf("Gradients");
683// double *ftest = frc.data();
684// for (int i = 0; i < N; i++) {
685// F[3 * i] = ftest[3 * i];
686// F[3 * i + 1] = ftest[3 * i + 1];
687// F[3 * i + 2] = ftest[3 * i + 2];
688// }
689// *U = extract_scalar_rkf("Energy");
690// return;
691// }
692
693// clang-format on
694
695/*
696** Debugging Toggles
697** These functions validate the job ordering when added to
698*runAMS()
699*/
700
701// TODO: Only in debug
702// std::string AMS::readFile(std::filesystem::path path) {
703// // Kanged: https://stackoverflow.com/a/40903508/1895378
704// std::ifstream f(path, std::ios::in | std::ios::binary);
705// const auto sz = std::filesystem::file_size(path);
706// std::string stres(sz, '\0');
707// f.read(stres.data(), sz);
708// return stres;
709// }
710
711// bool AMS::validate_order() {
712// // Validate all inputs
713// if (not first_run) {
714// std::string rfile = readFile("run_AMS.sh");
715// std::string resfile = readFile("myrestart.in");
716// std::string updcoord = readFile("updCoord.sh");
717// // ifstream runfile("run_AMS.sh"), restartfile("myrestart.in"),
718// // updcoord("updCoord.sh"); istream_iterator<string> rfiter(runfile),
719// // refiter(restartfile), uciter(updcoord), eof; vector<string>
720// // rfstore(rfiter, eof), refstore(refiter, eof), ucstore(uciter, eof);
721// // (?<=AMS_JOBNAME=).*$
722// // (?<=udmpkf ).*(?=\.results)
723// // (?<=File ).*(?=\.results)
724// // The logic here enforces that the current job restarts from the
725// previous one, so
726// // the coordinates of the previous job update, the restart
727// references
728// // the previous job, and then finally the current job executes with cjob
729// assert(absl::StrContains(rfile, cjob));
730// assert(absl::StrContains(resfile, pjob));
731// assert(absl::StrContains(updcoord, pjob));
732// }
733// return true;
734// }
std::string pjob
Definition AMS.h:65
void copyForces(long N, const std::vector< double > &frc, double *F)
Definition AMS.cpp:376
const double lengthConversion
Definition AMS.h:71
std::string forcefield
Definition AMS.h:57
std::string generate_run(const Parameters &p)
Definition AMS.cpp:511
std::string engine_setup
Definition AMS.h:58
void switchjob()
Definition AMS.cpp:344
const double energyConversion
Definition AMS.h:70
std::ofstream restartFrom
Definition AMS.h:66
void cleanMemory(void)
Definition AMS.cpp:75
std::string engine
Definition AMS.h:57
std::vector< double > extract_cartesian_rkf(std::string key)
Definition AMS.cpp:211
bool can_restart
Definition AMS.h:64
bool first_run
Definition AMS.h:64
std::string cjob
Definition AMS.h:65
void write_restart()
Definition AMS.cpp:355
boost::process::native_environment nativenv
Definition AMS.h:62
void smallSys(long N, const double *R, const int *atomicNrs, const double *box)
Flush, close and make the run script executable.
Definition AMS.cpp:474
std::string xc
Definition AMS.h:57
std::string engine_lower
Definition AMS.h:58
void runAMS()
Definition AMS.cpp:122
void passToSystem(long N, const double *R, const int *atomicNrs, const double *box)
< Creates a script to run AMS
Definition AMS.cpp:430
void updateCoord(long N, const double *R)
Definition AMS.cpp:282
std::string basis
Definition AMS.h:57
AMS(const Parameters &p)
Definition AMS.cpp:24
~AMS()
Definition AMS.cpp:77
int amsevals
Definition AMS.h:63
void finishRunScript(std::ofstream &out)
Copy 3N gradient components into the caller's force array.
Definition AMS.cpp:499
std::string model
Definition AMS.h:57
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box)
Definition AMS.cpp:389
std::string resources
Definition AMS.h:57
const double forceConversion
Definition AMS.h:67
double extract_scalar_rkf(std::string key)
Definition AMS.cpp:152
struct eonc::Parameters::ams_options_t ams_options
Potential(PotType a_ptype)
Definition Potential.h:35
struct eonc::Parameters::ams_options_t::env_t env