Loading...
Searching...
No Matches
RGPotEngine.cpp
Go to the documentation of this file.
1// Isolated TU: rgpot only (no eOn Potential.h) — avoids Cap'n Proto Potential
2// clash.
3// on_exit is a glibc extension. -std=c++20 hides it unless this is set
4// before the libc headers.
5#ifndef _GNU_SOURCE
6#define _GNU_SOURCE
7#endif
10
11#include <algorithm>
12#include <array>
13#include <cctype>
14#include <cstdint>
15#include <cstdio>
16#include <cstdlib>
17#include <cstring>
18#include <fstream>
19#include <mutex>
20#include <stdexcept>
21#include <string>
22#include <tuple>
23#include <vector>
24
25#if !defined(_WIN32)
26#include <dlfcn.h>
27#endif
28#if defined(__linux__) && defined(EON_RGPOT_MPI)
29#include <mpi.h>
30#include <stdlib.h>
31#endif
32
33#include <capnp/message.h>
34#include <capnp/serialize.h>
35
38#include "rgpot/CPMDPot/CPMDPot.hpp"
39#include "rgpot/CalculatorGroup.hpp"
40#include "rgpot/NWChemPot/NWChemPot.hpp"
41#include "rgpot/rpc/Potentials.capnp.h"
42
43using rgpot::types::AtomMatrix;
44
45#if defined(__linux__) && defined(EON_RGPOT_MPI)
46// Runs before rgpot's atexit handler and ends in _Exit, so rgpot's handler
47// never runs. It honours rgpot's abort request itself: after a failed
48// engine call the peers can sit in a collective, and MPI_Finalize would
49// wait for them until the walltime kill.
50extern "C" void eon_rgpot_hard_exit(int status, void *) {
51 int inited = 0;
52 int finalized = 0;
53 MPI_Initialized(&inited);
54 MPI_Finalized(&finalized);
55 if (inited && !finalized) {
56 if (::rgpot::mpiAbortRequested()) {
57 std::fflush(nullptr);
58 MPI_Abort(MPI_COMM_WORLD, status != 0 ? status : 1);
59 }
60 MPI_Finalize();
61 }
62 std::fflush(nullptr);
63 std::_Exit(status);
64}
65#endif
66
67namespace {
68
69// Serialized Cap'n Proto message (standard segment framing, word-aligned) as
70// written by `capnp encode` or messageToFlatArray.
71std::vector<::capnp::word> read_params_file(const std::string &path) {
72 std::ifstream in(path, std::ios::binary | std::ios::ate);
73 if (!in)
74 throw std::runtime_error("RGPOT: cannot open params_path: " + path);
75 const std::streamsize bytes = in.tellg();
76 if (bytes <= 0 || (static_cast<size_t>(bytes) % sizeof(::capnp::word)) != 0)
77 throw std::runtime_error(
78 "RGPOT: params_path is not a capnp flat message: " + path);
79 std::vector<::capnp::word> words(static_cast<size_t>(bytes) /
80 sizeof(::capnp::word));
81 in.seekg(0);
82 in.read(reinterpret_cast<char *>(words.data()), bytes);
83 if (!in)
84 throw std::runtime_error("RGPOT: short read on params_path: " + path);
85 return words;
86}
87
88std::string to_lower(std::string s) {
89 for (char &c : s)
90 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
91 return s;
92}
93
94std::array<std::array<double, 3>, 3> box_from_row_major(const double *box) {
95 std::array<std::array<double, 3>, 3> out{};
96 for (int i = 0; i < 3; ++i)
97 for (int j = 0; j < 3; ++j)
98 out[static_cast<size_t>(i)][static_cast<size_t>(j)] = box[i * 3 + j];
99 return out;
100}
101
102bool looks_like_dft_xc(const std::string &s) {
103 if (s.empty())
104 return false;
105 static const char *k[] = {"b3lyp", "blyp", "pbe", "pw91", "bp86",
106 "hcth", "ft97", "hfexch", "xperpbe", nullptr};
107 for (int i = 0; k[i]; ++i) {
108 if (s.size() >= std::char_traits<char>::length(k[i]) &&
109 s.compare(0, std::char_traits<char>::length(k[i]), k[i]) == 0)
110 return true;
111 }
112 return false;
113}
114
116int xtb_method_from_paramset(const std::string &paramset) {
117 const std::string p = to_lower(paramset);
118 if (p == "gfnff" || p == "gfn-ff")
120 if (p == "gfn0xtb" || p == "gfn0" || p == "gfn0-xtb")
122 if (p == "gfn1xtb" || p == "gfn1" || p == "gfn1-xtb")
124 if (p == "gfn2xtb" || p == "gfn2" || p == "gfn2-xtb" || p.empty())
126 throw std::runtime_error(
127 "RGPOT(xtb): paramset must be GFNFF, GFN0xTB, GFN1xTB, or GFN2xTB "
128 "(got '" +
129 paramset + "')");
130}
131
132// Ranks mpirun assigned. 1 when this process was not started under mpirun,
133// so a one-process test does not call MPI_Init.
134int mpi_world_hint() {
135 const char *s = std::getenv("OMPI_COMM_WORLD_SIZE");
136 if (s == nullptr || s[0] == '\0')
137 s = std::getenv("PMI_SIZE");
138 if (s == nullptr || s[0] == '\0')
139 return 1;
140 return std::atoi(s);
141}
142
143void pin_cpmd_library(const std::string &path) {
144#if !defined(_WIN32)
145 // A later dlclose must not run Fortran destructors. The mapping stays
146 // until process exit, and the grouped exit path does not run them.
147 const char *name = path.empty() ? "libcpmdc.so" : path.c_str();
148 dlopen(name, RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL | RTLD_NODELETE);
149#else
150 (void)path;
151#endif
152}
153
154#if defined(__linux__) && defined(EON_RGPOT_MPI)
155// Every rank calls this. A rank that failed locally still enters, so nobody
156// is left in MPI_Comm_split. The shared message is the lowest failing rank's
157// text. Returns false when any rank failed.
158bool agree_construction(std::string &message) {
159 int inited = 0;
160 MPI_Initialized(&inited);
161 if (!inited)
162 MPI_Init(nullptr, nullptr);
163 ::rgpot::finalizeMpiAtExit();
164 const int ok = message.empty() ? 1 : 0;
165 int all_ok = 0;
166 MPI_Allreduce(&ok, &all_ok, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
167 if (all_ok)
168 return true;
169 int rank = 0;
170 int size = 1;
171 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
172 MPI_Comm_size(MPI_COMM_WORLD, &size);
173 const int mine = ok ? size : rank;
174 int owner = 0;
175 MPI_Allreduce(&mine, &owner, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
176 int len = 0;
177 if (rank == owner)
178 len = static_cast<int>(
179 std::min(message.size(), static_cast<std::size_t>(4095)));
180 MPI_Bcast(&len, 1, MPI_INT, owner, MPI_COMM_WORLD);
181 std::vector<char> buf(static_cast<std::size_t>(len) + 1, '\0');
182 if (rank == owner && len > 0)
183 std::memcpy(buf.data(), message.data(), static_cast<std::size_t>(len));
184 if (len > 0)
185 MPI_Bcast(buf.data(), len, MPI_CHAR, owner, MPI_COMM_WORLD);
186 message.assign(buf.data(), static_cast<std::size_t>(len));
187 return false;
188}
189#endif
190
191} // namespace
192
193namespace eon {
194::CPMDParams::Builder fillCpmdParams(::capnp::MallocMessageBuilder &msg,
195 const RGPotEngineOptions &opt) {
196 ::CPMDParams::Builder params = msg.initRoot<::CPMDParams>();
197 if (!opt.params_path.empty()) {
198 const auto words = read_params_file(opt.params_path);
199 ::capnp::FlatArrayMessageReader reader(
200 kj::arrayPtr(words.data(), words.size()));
201 msg.setRoot(reader.getRoot<::CPMDParams>());
202 params = msg.getRoot<::CPMDParams>();
203 } else {
204 params.setFunctional(opt.functional);
205 params.setCutOffRy(opt.cutoff_ry);
206 params.setCharge(opt.charge);
207 params.setMultiplicity(opt.multiplicity);
208 if (!opt.title.empty())
209 params.setTitle(opt.title);
210 if (opt.memory_mb > 0)
211 params.setMemoryMb(static_cast<uint32_t>(opt.memory_mb));
212 }
213 if (!opt.engine_path.empty())
214 params.setEnginePath(opt.engine_path);
215 else if (!opt.engine_library.empty())
216 params.setEnginePath(opt.engine_library);
217 if (!opt.engine_root.empty())
218 params.setCpmdRoot(opt.engine_root);
219 if (!opt.scratch_dir.empty())
220 params.setScratchDir(opt.scratch_dir);
221 if (!opt.permanent_dir.empty())
222 params.setPermanentDir(opt.permanent_dir);
223
224 std::string block = opt.input_block;
225 if (block.empty()) {
226 if (const char *env = std::getenv("RGPOT_CPMD_INPUT_BLOCK"))
227 block = env;
228 }
229 if (!block.empty()) {
230 std::vector<std::string> kept;
231 {
232 auto existing = params.getInputBlocks();
233 kept.reserve(existing.size());
234 for (auto text : existing)
235 kept.emplace_back(text.cStr());
236 }
237 auto blocks = params.initInputBlocks(kept.size() + 1);
238 for (std::size_t i = 0; i < kept.size(); ++i)
239 blocks.set(i, kept[i]);
240 blocks.set(kept.size(), block);
241 }
242 return params;
243}
244} // namespace eon
245
247 enum class Backend { Nwchemc, Cpmdc, Metatomic, Xtb };
249 std::unique_ptr<rgpot::NWChemPot> nwchem;
250 std::unique_ptr<rgpot::CPMDPot> cpmd;
251 std::unique_ptr<MetatomicEngineLoader> metatomic;
252 std::unique_ptr<XTBEngineLoader> xtb;
253 // Calculator groups (cpmdc, ranks_per_image > 0). One group when off.
254 int groups{1};
255 int group{0};
256 int world{1};
257 bool module_down{false};
258};
259
261 : impl_(std::make_unique<Impl>()) {
262 backend_ = to_lower(opt.backend);
263 if (backend_ == "nwchem" || backend_ == "nwchemc" ||
264 backend_ == "nwchempot") {
265 backend_ = "nwchemc";
266 impl_->backend = Impl::Backend::Nwchemc;
267 ::capnp::MallocMessageBuilder msg;
268 auto params = msg.initRoot<::NWChemParams>();
269 params.setBasis(opt.basis);
270 params.setTheory(opt.theory);
271 params.setScfType(opt.scf_type);
272 params.setCharge(opt.charge);
273 params.setMultiplicity(opt.multiplicity);
274 if (!opt.engine_path.empty())
275 params.setEnginePath(opt.engine_path);
276 else if (!opt.engine_library.empty())
277 params.setEnginePath(opt.engine_library);
278 if (!opt.engine_root.empty())
279 params.setNwchemRoot(opt.engine_root);
280 if (!opt.title.empty())
281 params.setTitle(opt.title);
282 if (opt.memory_mb > 0)
283 params.setMemoryMb(static_cast<uint32_t>(opt.memory_mb));
284 if (!opt.scratch_dir.empty())
285 params.setScratchDir(opt.scratch_dir);
286 // DFT XC as inputBlocks when theory=dft and scfType is an XC label
287 std::string block = opt.input_block;
288 if (block.empty()) {
289 if (const char *env = std::getenv("RGPOT_NWCHEM_INPUT_BLOCK"))
290 block = env;
291 }
292 if (block.empty() && (opt.theory == "dft" || opt.theory == "DFT") &&
293 looks_like_dft_xc(opt.scf_type)) {
294 block = "dft\n xc " + opt.scf_type + "\n mult " +
295 std::to_string(opt.multiplicity) + "\nend";
296 } else if (block.empty() && looks_like_dft_xc(opt.theory)) {
297 block = "dft\n xc " + opt.theory + "\n mult " +
298 std::to_string(opt.multiplicity) + "\nend";
299 }
300 if (!block.empty()) {
301 auto blocks = params.initInputBlocks(1);
302 blocks.set(0, block);
303 }
304 impl_->nwchem = std::make_unique<rgpot::NWChemPot>(params.asReader());
305 if (!impl_->nwchem->available())
306 throw std::runtime_error(
307 "RGPOT(nwchemc): engine not available (set NWCHEMC_LIBRARY / "
308 "RGPOT_NWCHEMC_ENGINE or [RgpotPot] engine_path)");
309 } else if (backend_ == "cpmd" || backend_ == "cpmdc" ||
310 backend_ == "cpmdpot") {
311 backend_ = "cpmdc";
312 impl_->backend = Impl::Backend::Cpmdc;
313 std::string local_error;
314 try {
315 ::capnp::MallocMessageBuilder msg;
316 ::CPMDParams::Builder params = eon::fillCpmdParams(msg, opt);
317 impl_->cpmd = std::make_unique<rgpot::CPMDPot>(params.asReader());
318 pin_cpmd_library(opt.engine_path.empty() ? opt.engine_library
319 : opt.engine_path);
320 if (!impl_->cpmd->available())
321 throw std::runtime_error(
322 "RGPOT(cpmdc): engine not available (set CPMDC_LIBRARY / "
323 "RGPOT_CPMDC_ENGINE or [RgpotPot] engine_path)");
324 } catch (const std::exception &ex) {
325 local_error = ex.what();
326 }
327 // Several ranks: agree before MPI_Comm_split. One rank that cannot
328 // read params_path must not leave the others inside the split.
329 if (mpi_world_hint() <= 1 && !local_error.empty())
330 throw std::runtime_error(local_error);
331#if defined(__linux__) && defined(EON_RGPOT_MPI)
332 // rgpot reports MPI only once MPI_Init has run, and agree_construction
333 // is what initialises it, so the agreement keys on the launch size.
334 if (mpi_world_hint() > 1 && !agree_construction(local_error)) {
336 throw std::runtime_error(
337 local_error.empty()
338 ? std::string(
339 "RGPOT(cpmdc): a rank failed before the calculator split")
340 : local_error);
341 }
342#endif
343 if (!local_error.empty())
344 throw std::runtime_error(local_error);
345 if (::rgpot::calculatorsUseMpi()) {
346 // Collective on MPI_COMM_WORLD, before the first force: the engine
347 // installs the group communicator ahead of CPMD's mp_start.
348 // ranks_per_image = 0 is one calculator on the whole world.
349 const rgpot::CalculatorGroup g =
350 ::rgpot::bindCalculators(opt.ranks_per_image);
351 ::rgpot::finalizeMpiAtExit();
352 if (mpi_world_hint() > 1)
354 if (g.index < 0)
355 throw std::runtime_error(
356 "RGPOT(cpmdc): ranks_per_image=" +
357 std::to_string(opt.ranks_per_image) +
358 " does not divide the MPI world into calculator groups");
359 impl_->groups = ::rgpot::calculatorCount();
360 impl_->group = g.index;
361 impl_->world = ::rgpot::calculatorWorldSize();
362 } else if (opt.ranks_per_image > 0) {
363 throw std::runtime_error(
364 "RGPOT(cpmdc): ranks_per_image needs rgpot built with MPI "
365 "(-Drgpot:with_mpi=enabled)");
366 }
367 } else if (backend_ == "metatomic" || backend_ == "mta" ||
368 backend_ == "metatomicpot") {
369 backend_ = "metatomic";
372 mopt.model_path = opt.model_path;
373 mopt.device = opt.device;
374 mopt.length_unit = opt.length_unit;
378 mopt.engine_path =
379 !opt.engine_path.empty() ? opt.engine_path : opt.engine_library;
381 impl_->metatomic = std::make_unique<MetatomicEngineLoader>(mopt);
382 if (!impl_->metatomic->available())
383 throw std::runtime_error(
384 "RGPOT(metatomic): engine not available (set RGPOT_METATOMIC_ENGINE "
385 "or [RgpotPot] engine_path to libmetatomic_engine.so)");
386 } else if (backend_ == "xtb" || backend_ == "xtbpot" || backend_ == "gfn" ||
387 backend_ == "gfnxtb") {
388 backend_ = "xtb";
389 impl_->backend = Impl::Backend::Xtb;
390 XTBEngineOptions xopt;
391 xopt.method = xtb_method_from_paramset(opt.xtb_paramset);
392 xopt.accuracy = opt.xtb_accuracy;
395 xopt.charge = opt.xtb_charge;
396 xopt.uhf = opt.xtb_uhf;
397 xopt.engine_path =
398 !opt.engine_path.empty() ? opt.engine_path : opt.engine_library;
399 impl_->xtb = std::make_unique<XTBEngineLoader>(xopt);
400 if (!impl_->xtb->available())
401 throw std::runtime_error(
402 "RGPOT(xtb): engine not available (set RGPOT_XTB_ENGINE or "
403 "[RgpotPot] engine_path to libxtb_engine.so)");
404 } else {
405 throw std::runtime_error("RGPOT: unknown backend '" + opt.backend +
406 "' (expected nwchemc, cpmdc, metatomic, or xtb)");
407 }
408}
409
410RGPotEngine::~RGPotEngine() = default;
411
412int RGPotEngine::calculatorGroups() const noexcept {
413 return impl_ ? impl_->groups : 1;
414}
415
416int RGPotEngine::calculatorIndex() const noexcept {
417 return impl_ ? impl_->group : 0;
418}
419
420int RGPotEngine::calculatorWorld() const noexcept {
421 return impl_ ? impl_->world : 1;
422}
423
424int RGPotEngine::worldRank() const noexcept {
425 if (!impl_ || impl_->world <= 1)
426 return 0;
427 const rgpot::CalculatorGroup &g = ::rgpot::thisCalculator();
428 return g.index * g.ranks + g.rank_in_group;
429}
430
432 if (impl_ && impl_->world > 1)
433 ::rgpot::finalizeMpiAtExit();
434}
435
437 return ::rgpot::mpiAbortRequested();
438}
439
441#if defined(__linux__) && defined(EON_RGPOT_MPI)
442 if (mpi_world_hint() <= 1)
443 return;
444 static std::once_flag once;
445 std::call_once(once, [] { ::on_exit(eon_rgpot_hard_exit, nullptr); });
446#else
447 (void)this;
448#endif
449}
450
452 if (!impl_ || impl_->module_down)
453 return;
454 impl_->module_down = true;
455#if !defined(_WIN32)
456 if (auto *fin =
457 reinterpret_cast<void (*)()>(dlsym(RTLD_DEFAULT, "cpmdc_finalize")))
458 fin();
459 pin_cpmd_library({});
460#endif
461}
462
463void RGPotEngine::broadcastFromDriver(void *data, std::size_t bytes) const {
464 if (!impl_ || impl_->world <= 1 || bytes == 0)
465 return;
466 // World rank 0 is the first rank of calculator 0.
467 if (::rgpot::shareFromCalculator(0, data, bytes) == 0)
468 throw std::runtime_error("RGPOT: could not broadcast from the driver rank");
469}
470
471bool RGPotEngine::shareResult(int owner, long N, double *F, double *U, bool ok,
472 std::string &error) const {
473 if (!impl_ || impl_->world <= 1) {
474 (void)error;
475 return ok;
476 }
477 // Slot 0 carries the owner's status, so a failed evaluation reaches
478 // every rank through the same broadcast as a good one.
479 std::vector<double> buf(static_cast<size_t>(3 * N + 2));
480 if (owner == impl_->group) {
481 buf[0] = ok ? 1.0 : 0.0;
482 buf[1] = *U;
483 std::copy(F, F + 3 * N, buf.begin() + 2);
484 }
485 if (::rgpot::shareFromCalculator(owner, buf.data(),
486 buf.size() * sizeof(double)) == 0)
487 throw std::runtime_error("RGPOT: could not share a calculator result");
488 *U = buf[1];
489 std::copy(buf.begin() + 2, buf.end(), F);
490 // The owner's engine text travels with the status. Other ranks hold an
491 // empty string until this broadcast.
492 std::array<char, 512> msg{};
493 if (owner == impl_->group && !error.empty()) {
494 const auto n = std::min(error.size(), msg.size() - 1);
495 std::memcpy(msg.data(), error.data(), n);
496 }
497 if (::rgpot::shareFromCalculator(owner, msg.data(), msg.size()) == 0)
498 throw std::runtime_error("RGPOT: could not share a calculator error");
499 if (msg[0] != '\0')
500 error.assign(msg.data());
501 return buf[0] == 1.0;
502}
503
505 if (!impl_)
506 return false;
507 if (impl_->backend == Impl::Backend::Nwchemc && impl_->nwchem)
508 return impl_->nwchem->available();
509 if (impl_->backend == Impl::Backend::Cpmdc && impl_->cpmd)
510 return impl_->cpmd->available();
511 if (impl_->backend == Impl::Backend::Metatomic && impl_->metatomic)
512 return impl_->metatomic->available();
513 if (impl_->backend == Impl::Backend::Xtb && impl_->xtb)
514 return impl_->xtb->available();
515 return false;
516}
517
518void RGPotEngine::force(long N, const double *R, const int *atomicNrs,
519 double *F, double *U, const double *box) const {
520 // Set on one rank by the multi-rank failure test. The text is the
521 // engine error that rank has to deliver to rank 0.
522 if (const char *fail = std::getenv("RGPOT_FORCE_FAIL")) {
523 if (fail[0] != '\0')
524 throw std::runtime_error(fail);
525 }
526 if (N <= 0)
527 throw std::runtime_error("RGPotEngine::force called with N <= 0");
528
529 AtomMatrix positions(static_cast<int>(N), 3);
530 for (long i = 0; i < N; ++i) {
531 const int ii = static_cast<int>(i);
532 positions(ii, 0) = R[3 * i + 0];
533 positions(ii, 1) = R[3 * i + 1];
534 positions(ii, 2) = R[3 * i + 2];
535 }
536 std::vector<int> atmtypes(atomicNrs, atomicNrs + N);
537 const auto cell = box_from_row_major(box);
538
539 if (impl_->backend == Impl::Backend::Metatomic) {
540 impl_->metatomic->force(N, R, atomicNrs, F, U, nullptr, box);
541 return;
542 }
543 if (impl_->backend == Impl::Backend::Xtb) {
544 impl_->xtb->force(N, R, atomicNrs, F, U, nullptr, box);
545 return;
546 }
547
548 // rgpot >= 2.5.0: operator() returns (energy, forces, variance).
549 std::tuple<double, AtomMatrix, double> result;
550 if (impl_->backend == Impl::Backend::Nwchemc)
551 result = (*impl_->nwchem)(positions, atmtypes, cell);
552 else
553 result = (*impl_->cpmd)(positions, atmtypes, cell);
554
555 *U = std::get<0>(result);
556 const auto &forces = std::get<1>(result);
557 for (long i = 0; i < N; ++i) {
558 const int ii = static_cast<int>(i);
559 F[3 * i + 0] = forces(ii, 0);
560 F[3 * i + 1] = forces(ii, 1);
561 F[3 * i + 2] = forces(ii, 2);
562 }
563}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
bool available() const
RGPotEngine(const RGPotEngineOptions &opt)
void broadcastFromDriver(void *data, std::size_t bytes) const
Collective on MPI_COMM_WORLD: every rank leaves with world rank 0's bytes.
static bool mpiAbortRequested() noexcept
True once rgpot asked for MPI_Abort at exit in this process.
void armGroupedExit() const
After MPI_Finalize, _Exit.
int calculatorGroups() const noexcept
Number of calculator groups the MPI world is split into (1 when ranks_per_image is off) and the group...
void shutdownModule() noexcept
Calls cpmdc_finalize while the engine is still mapped. MPI stays up.
int calculatorWorld() const noexcept
Ranks in the MPI world the groups were bound on (1 without MPI).
int calculatorIndex() const noexcept
int worldRank() const noexcept
This process's rank in that world (0 without MPI).
void finalizeMpiAtExit() const
Registers MPI_Finalize at exit (once per process) when the world has more than one rank.
std::unique_ptr< Impl > impl_
Definition RGPotEngine.h:92
bool shareResult(int owner, long N, double *F, double *U, bool ok, std::string &error) const
Collective on MPI_COMM_WORLD: every rank leaves with the energy and the 3N forces computed by the fir...
std::string backend_
Definition RGPotEngine.h:93
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, const double *box) const
::CPMDParams::Builder fillCpmdParams(::capnp::MallocMessageBuilder &msg, const RGPotEngineOptions &opt)
std::string scf_type
Definition RGPotEngine.h:11
std::string title
Definition RGPotEngine.h:19
std::string xtb_paramset
Definition RGPotEngine.h:37
std::string basis
Definition RGPotEngine.h:9
std::string scratch_dir
Definition RGPotEngine.h:21
std::string engine_path
Definition RGPotEngine.h:16
std::string params_path
Definition RGPotEngine.h:26
std::string backend
Definition RGPotEngine.h:8
std::string extensions_directory
Definition RGPotEngine.h:32
std::string engine_library
Definition RGPotEngine.h:17
double xtb_electronic_temperature
Definition RGPotEngine.h:39
std::string permanent_dir
Definition RGPotEngine.h:24
std::string functional
Definition RGPotEngine.h:12
std::string length_unit
Definition RGPotEngine.h:31
bool torch_determinism_strict
Definition RGPotEngine.h:35
std::string device
Definition RGPotEngine.h:30
std::string model_path
Definition RGPotEngine.h:29
std::string engine_root
Definition RGPotEngine.h:18
double uncertainty_threshold
Definition RGPotEngine.h:34
std::string theory
Definition RGPotEngine.h:10
std::string input_block
Definition RGPotEngine.h:23
std::unique_ptr< rgpot::NWChemPot > nwchem
std::unique_ptr< rgpot::CPMDPot > cpmd
std::unique_ptr< MetatomicEngineLoader > metatomic
std::unique_ptr< XTBEngineLoader > xtb
std::string engine_path
double electronic_temperature
@ RGPOT_XTB_METHOD_GFNFF
Definition xtb_c_abi.h:32
@ RGPOT_XTB_METHOD_GFN1
Definition xtb_c_abi.h:34
@ RGPOT_XTB_METHOD_GFN2
Definition xtb_c_abi.h:35
@ RGPOT_XTB_METHOD_GFN0
Definition xtb_c_abi.h:33