22namespace bp = boost::process;
43 nativenv = boost::this_process::environment();
45 nativenv = boost::this_process::environment();
65 this->
cjob =
"amsResults";
66 this->
pjob =
"amsResults";
69 this->
cjob =
"firstRun";
70 this->
pjob =
"secondRun";
82constexpr const char *kRunScript =
"run_AMS.sh";
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};
95std::string mass2atom(
double atomicmass) {
96 return elementArray[int(atomicmass + .5)];
99int symbol2atomicNumber(
char const *symbol) {
102 while (elementArray[i] != NULL) {
103 if (strcmp(symbol, elementArray[i]) == 0) {
112char const *atomicNumber2symbol(
int n) {
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));
118 return elementArray[n];
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));
132 bp::child c(std::string(kRunScript),
135 bp::std_out > run_out_future,
136 bp::std_err > run_err_future,
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");
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;
162 std::vector<double> extracted;
163 execString = std::format(
"dmpkf {}.results/{}.rkf AMSResults%{}", this->
cjob,
169 bp::std_out > rkf_out_future,
170 bp::std_err > rkf_err_future,
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));
184 execDat = absl::StrSplit(rkfout,
'\n');
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));
197 if (absl::SimpleAtod(execDat[3], &x)) {
204 throw std::runtime_error(
205 std::format(
"\n Expected {}, got {} instead", key, execDat[3]));
208 throw std::runtime_error(
"Generic AMS dmpkf scalar error \n");
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;
218 std::vector<double> extracted;
219 execString = std::format(
"dmpkf {}.results/{}.rkf AMSResults%{}", this->
cjob,
226 bp::std_out > rkf_out_future,
227 bp::std_err > rkf_err_future,
232 rkfout = rkf_out_future.get();
233 rkferr = rkf_err_future.get();
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));
242 execDat = absl::StrSplit(rkfout,
'\n');
244 for (
int i = 3; i < execDat.size(); i++) {
247 std::vector<std::string> strrow = absl::StrSplit(execDat[i],
' ');
248 for (
auto elem : strrow) {
261 if (!elem.empty() && absl::SimpleAtod(elem, &x)) {
266 extracted.emplace_back(felem);
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;
295 execString = std::format(
"dmpkf {}.results/ams.rkf Molecule%Coords",
pjob);
299 bp::child cprog(execString,
nativenv, bp::std_in.close(), bp::std_out > rdump,
300 bp::std_err > err, coordio);
302 execDat = absl::StrSplit(rdump.get(),
'\n');
305 for (
auto j : execDat) {
309 absl::StrAppend(&newCoord, j,
"\n");
315 for (
int a = 0; a < N * 3; a++) {
318 if (counter % 3 == 0) {
319 absl::StrAppend(&newCoord,
"\n");
322 coordDump =
"#!/bin/sh\n udmpkf ";
323 absl::StrAppend(&coordDump,
pjob,
".results/ams.rkf <<EOF\n", newCoord,
326 updCoord.open(
"updCoord.sh", std::ios::trunc);
328 throw std::runtime_error(
"Could not open updCoord.sh for writing");
330 updCoord << coordDump;
333 throw std::runtime_error(
"Could not write the coordinates to updCoord.sh");
336 if (chmod(
"updCoord.sh", S_IRWXU) != 0) {
337 throw std::runtime_error(
"Could not make updCoord.sh executable");
339 bp::child cuprog(
"updCoord.sh",
nativenv, bp::std_err > bp::null);
356 std::string restart_formatter;
358 restart_formatter = R
"(
359 EngineRestart {0}.results/{1}.rkf
361 File {0}.results/ams.rkf
366 restart_formatter = R
"(
368 File {0}.results/ams.rkf
373 std::string restart_data = std::format(restart_formatter, pjob, engine_lower);
376 throw std::runtime_error(
"Could not open myrestart.in for writing");
381 throw std::runtime_error(
"Could not write the restart block to "
387void AMS::copyForces(
long N,
const std::vector<double> &frc,
double *F) {
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 {}",
395 for (
long i = 0; i < 3 * N; i++) {
400void AMS::force(
long N,
const double *R,
const int *atomicNrs,
double *F,
401 double *U,
double *variance,
const double *box) {
417 const auto copyOptions = std::filesystem::copy_options::overwrite_existing
418 | std::filesystem::copy_options::recursive
421 std::filesystem::copy(std::format(
"./{}.results/",
cjob),
422 std::format(
"./{}.results/",
pjob), copyOptions);
438 throw std::runtime_error(
"Generic AMS force error \n");
445 std::ofstream out(kRunScript, std::ios::trunc);
447 throw std::runtime_error(
448 std::format(
"Could not open {} for writing", kRunScript));
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";
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]);
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]);
473 out <<
"Properties\n";
474 out <<
" Gradients\n";
477 out <<
"@include myrestart.in\n";
485void AMS::smallSys(
long N,
const double *R,
const int *atomicNrs,
489 std::ofstream out(kRunScript, std::ios::trunc);
491 throw std::runtime_error(
492 std::format(
"Could not open {} for writing", kRunScript));
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";
503 out <<
"@include myrestart.in\n";
513 throw std::runtime_error(
514 std::format(
"Could not write the AMS input to {}", kRunScript));
516 if (chmod(kRunScript, S_IRWXU) != 0) {
517 throw std::runtime_error(
518 std::format(
"Could not make {} executable", kRunScript));
523 std::string engine_block;
527 ?
throw std::runtime_error(
"AMS Engine is required \n")
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
"(
543 engine_block = std::format(engine_formatter, engine, model);
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
"(
562 engine_block = std::format(engine_formatter, engine, basis, xc);
564 }
else if (
engine ==
"DFTB") {
565 resources.empty() ?
throw std::runtime_error(
"DFTB need resources\n")
568 std::string engine_formatter = R
"(
575 }
else if (
engine ==
"reaxff") {
576 forcefield.empty() ?
throw std::runtime_error(
"REAXFF needs a forcefield\n")
580 std::string engine_formatter = R
"(
587 }
else if (
engine ==
"FORCEFIELD") {
588 std::string engine_formatter = R
"(
592 engine_block = std::format(engine_formatter, engine);
597 throw std::runtime_error(
"Generic AMS engine error \n");
void copyForces(long N, const std::vector< double > &frc, double *F)
const double lengthConversion
std::string generate_run(const Parameters &p)
const double energyConversion
std::ofstream restartFrom
std::vector< double > extract_cartesian_rkf(std::string key)
boost::process::native_environment nativenv
void smallSys(long N, const double *R, const int *atomicNrs, const double *box)
Flush, close and make the run script executable.
void passToSystem(long N, const double *R, const int *atomicNrs, const double *box)
< Creates a script to run AMS
void updateCoord(long N, const double *R)
void finishRunScript(std::ofstream &out)
Copy 3N gradient components into the caller's force array.
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box)
const double forceConversion
double extract_scalar_rkf(std::string key)
struct eonc::Parameters::ams_options_t ams_options
Potential(PotType a_ptype)
std::string scm_pythondir
struct eonc::Parameters::ams_options_t::env_t env