23#include <readcon-core.hpp>
27namespace bp = boost::process;
48 nativenv = boost::this_process::environment();
50 nativenv = boost::this_process::environment();
69 this->
cjob =
"amsResults";
70 this->
pjob =
"amsResults";
73 this->
cjob =
"firstRun";
74 this->
pjob =
"secondRun";
86constexpr const char *kRunScript =
"run_AMS.sh";
88std::string symbol_for_z(
int n) {
90 throw std::runtime_error(
91 std::format(
"AMS knows no element symbol for atomic number {}", n));
93 return readcon::z_to_symbol(
static_cast<uint64_t
>(n));
98 boost::asio::io_context amsRun;
99 std::future<std::string> run_out_future, run_err_future;
100 std::string runout, runerr;
101 if (chmod(kRunScript, S_IRWXU) != 0) {
102 throw std::runtime_error(
103 std::format(
"Could not make {} executable", kRunScript));
106 bp::child c(std::string(kRunScript),
109 bp::std_out > run_out_future,
110 bp::std_err > run_err_future,
113 runout = run_out_future.get();
114 runerr = run_err_future.get();
115 if (runerr.find(
"ERROR") != std::string::npos) {
116 bp::spawn(
"cat myrestart.in");
117 bp::spawn(
"cat run_AMS.sh");
118 throw std::runtime_error(
"\n AMS error while running");
128 std::string execString;
129 std::vector<std::string> execDat;
130 boost::asio::io_context rkf;
131 std::future<std::string> rkf_out_future, rkf_err_future;
132 std::string rkfout, rkferr;
134 std::vector<double> extracted;
135 execString = std::format(
"dmpkf {}.results/{}.rkf AMSResults%{}", this->
cjob,
140 bp::std_out > rkf_out_future,
141 bp::std_err > rkf_err_future,
146 rkfout = rkf_out_future.get();
147 rkferr = rkf_err_future.get();
148 if (rkferr.find(
"ERROR") != std::string::npos) {
149 throw std::runtime_error(std::format(
150 "\n AMS error while extracting {}, got:\n {}", key, rkferr));
153 execDat = absl::StrSplit(rkfout,
'\n');
161 if (execDat.size() < 4) {
162 throw std::runtime_error(
163 std::format(
"\n AMS returned {} lines for {}, too few to hold a value",
164 execDat.size(), key));
166 if (absl::SimpleAtod(execDat[3], &x)) {
170 throw std::runtime_error(
171 std::format(
"\n Expected {}, got {} instead", key, execDat[3]));
174 throw std::runtime_error(
"Generic AMS dmpkf scalar error \n");
178 std::string execString;
179 std::vector<std::string> execDat, innerDat;
180 boost::asio::io_context rkf;
181 std::future<std::string> rkf_out_future, rkf_err_future;
182 std::string rkfout, rkferr;
184 std::vector<double> extracted;
185 execString = std::format(
"dmpkf {}.results/{}.rkf AMSResults%{}", this->
cjob,
191 bp::std_out > rkf_out_future,
192 bp::std_err > rkf_err_future,
197 rkfout = rkf_out_future.get();
198 rkferr = rkf_err_future.get();
200 if (rkferr.find(
"ERROR") != std::string::npos) {
201 throw std::runtime_error(std::format(
202 "\n AMS error while extracting {}, got:\n {}", key, rkferr));
205 execDat = absl::StrSplit(rkfout,
'\n');
207 for (
int i = 3; i < execDat.size(); i++) {
210 std::vector<std::string> strrow = absl::StrSplit(execDat[i],
' ');
211 for (
auto elem : strrow) {
224 if (!elem.empty() && absl::SimpleAtod(elem, &x)) {
226 extracted.emplace_back(felem);
238 std::ofstream updCoord;
239 std::string execString, coordDump, newCoord;
240 std::vector<std::string> execDat;
241 boost::asio::io_context coordio;
242 std::future<std::string> err, rdump;
243 std::vector<double> gradients;
246 execString = std::format(
"dmpkf {}.results/ams.rkf Molecule%Coords",
pjob);
249 bp::child cprog(execString,
nativenv, bp::std_in.close(), bp::std_out > rdump,
250 bp::std_err > err, coordio);
252 execDat = absl::StrSplit(rdump.get(),
'\n');
255 for (
auto j : execDat) {
259 absl::StrAppend(&newCoord, j,
"\n");
265 for (
int a = 0; a < N * 3; a++) {
268 if (counter % 3 == 0) {
269 absl::StrAppend(&newCoord,
"\n");
272 coordDump =
"#!/bin/sh\n udmpkf ";
273 absl::StrAppend(&coordDump,
pjob,
".results/ams.rkf <<EOF\n", newCoord,
275 updCoord.open(
"updCoord.sh", std::ios::trunc);
277 throw std::runtime_error(
"Could not open updCoord.sh for writing");
279 updCoord << coordDump;
282 throw std::runtime_error(
"Could not write the coordinates to updCoord.sh");
285 if (chmod(
"updCoord.sh", S_IRWXU) != 0) {
286 throw std::runtime_error(
"Could not make updCoord.sh executable");
288 bp::child cuprog(
"updCoord.sh",
nativenv, bp::std_err > bp::null);
301 std::string restart_formatter;
303 restart_formatter = R
"(
304 EngineRestart {0}.results/{1}.rkf
306 File {0}.results/ams.rkf
311 restart_formatter = R
"(
313 File {0}.results/ams.rkf
318 std::string restart_data = std::format(restart_formatter, pjob, engine_lower);
321 throw std::runtime_error(
"Could not open myrestart.in for writing");
326 throw std::runtime_error(
"Could not write the restart block to "
332void AMS::copyForces(
long N,
const std::vector<double> &frc,
double *F) {
335 if (frc.size() <
static_cast<std::size_t
>(3 * N)) {
336 throw std::runtime_error(
337 std::format(
"\n AMS returned {} gradient components, expected {}",
340 for (
long i = 0; i < 3 * N; i++) {
345void AMS::force(
long N,
const double *R,
const int *atomicNrs,
double *F,
346 double *U,
double *variance,
const double *box) {
360 const auto copyOptions = std::filesystem::copy_options::overwrite_existing
361 | std::filesystem::copy_options::recursive
364 std::filesystem::copy(std::format(
"./{}.results/",
cjob),
365 std::format(
"./{}.results/",
pjob), copyOptions);
379 throw std::runtime_error(
"Generic AMS force error \n");
386 std::ofstream out(kRunScript, std::ios::trunc);
388 throw std::runtime_error(
389 std::format(
"Could not open {} for writing", kRunScript));
392 out <<
"#!/bin/sh\n";
393 out << std::format(
"export AMS_JOBNAME={}\n",
cjob);
394 out <<
"$AMSBIN/ams --delete-old-results <<eor\n";
395 out <<
"Task SinglePoint\n";
398 for (
long i = 0; i < N; i++) {
399 out << std::format(
" {}\t{:.19f}\t{:.19f}\t{:.19f}\n",
400 symbol_for_z(atomicNrs[i]), R[i * 3 + 0], R[i * 3 + 1],
406 for (
int i = 0; i < 3; i++) {
407 out << std::format(
" {:.19f}\t{:.19f}\t{:.19f}\n", box[i * 3 + 0],
408 box[i * 3 + 1], box[i * 3 + 2]);
414 out <<
"Properties\n";
415 out <<
" Gradients\n";
418 out <<
"@include myrestart.in\n";
426void AMS::smallSys(
long N,
const double *R,
const int *atomicNrs,
430 std::ofstream out(kRunScript, std::ios::trunc);
432 throw std::runtime_error(
433 std::format(
"Could not open {} for writing", kRunScript));
436 out <<
"#!/bin/sh\n";
437 out << std::format(
"export AMS_JOBNAME={}\n",
cjob);
438 out <<
"$AMSBIN/ams --delete-old-results <<eor\n";
439 out <<
"Task SinglePoint\n";
441 out <<
"Properties\n";
442 out <<
" Gradients\n";
444 out <<
"@include myrestart.in\n";
454 throw std::runtime_error(
455 std::format(
"Could not write the AMS input to {}", kRunScript));
457 if (chmod(kRunScript, S_IRWXU) != 0) {
458 throw std::runtime_error(
459 std::format(
"Could not make {} executable", kRunScript));
464 std::string engine_block;
468 throw std::runtime_error(
"AMS Engine is required \n");
470 std::ranges::transform(
engine,
engine.begin(), [](
unsigned char c) {
471 return static_cast<char>(std::toupper(c));
477 return static_cast<char>(std::tolower(c));
482 throw std::runtime_error(
"MOPAC needs a model\n");
484 std::ranges::transform(
model,
model.begin(), [](
unsigned char c) {
485 return static_cast<char>(std::toupper(c));
487 std::string engine_formatter = R
"(
492 engine_block = std::format(engine_formatter, engine, model);
496 throw std::runtime_error(
"ADF/BAND need a basis\n");
498 std::ranges::transform(
basis,
basis.begin(), [](
unsigned char c) {
499 return static_cast<char>(std::toupper(c));
502 throw std::runtime_error(
"ADF/BAND need a functional\n");
504 std::ranges::transform(
xc,
xc.begin(), [](
unsigned char c) {
505 return static_cast<char>(std::toupper(c));
507 std::string engine_formatter = R
"(
518 engine_block = std::format(engine_formatter, engine, basis, xc);
520 }
else if (
engine ==
"DFTB") {
522 throw std::runtime_error(
"DFTB need resources\n");
525 return static_cast<char>(std::toupper(c));
527 std::string engine_formatter = R
"(
534 }
else if (
engine ==
"reaxff") {
536 throw std::runtime_error(
"REAXFF needs a forcefield\n");
539 return static_cast<char>(std::toupper(c));
542 std::string engine_formatter = R
"(
549 }
else if (
engine ==
"FORCEFIELD") {
550 std::string engine_formatter = R
"(
554 engine_block = std::format(engine_formatter, engine);
559 throw std::runtime_error(
"Generic AMS engine error \n");
void copyForces(long N, const std::vector< double > &frc, double *F)
const double lengthConversion
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)
AMS(const eonc::Parameters &p)
std::string generate_run(const eonc::Parameters &p)
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)
const ams_options_t & ams_options() const
Potential(PotType a_ptype)
Production default: construction-scope registry, else PotRegistry::get().
RAII resource manager for the ARTn C library with global synchronization.
std::string scm_pythondir
struct eonc::ams_options_t::env_t env