Loading...
Searching...
No Matches
ASE_ORCA.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 "eon/EnvHelpers.hpp"
15#include "eon/EonLogger.h"
16#include "eon/Parameters.h"
17#include "eon/PyGuard.h"
18#include "eon/fpe_handler.h"
19
20#include <atomic>
21#include <format>
22#include <stdexcept>
23#include <string>
24#include <system_error>
25
26#ifdef _WIN32
27#include <process.h>
28#else
29#include <unistd.h>
30#endif
31
32namespace {
33
34std::filesystem::path makeAseWorkDir(const char *prefix) {
35 static std::atomic<long> instanceCount{0};
36#ifdef _WIN32
37 const long pid = static_cast<long>(_getpid());
38#else
39 const long pid = static_cast<long>(getpid());
40#endif
41 std::error_code ec;
42 auto dir = std::filesystem::temp_directory_path(ec);
43 if (ec) {
44 throw std::runtime_error(std::format(
45 "ASE-ORCA: cannot resolve temp directory: {}", ec.message()));
46 }
47 dir /= std::format("{}_{}_{}", prefix, pid, instanceCount.fetch_add(1));
48 std::filesystem::create_directories(dir, ec);
49 if (ec) {
50 throw std::runtime_error(
51 std::format("ASE-ORCA: cannot create work directory {}: {}",
52 dir.string(), ec.message()));
53 }
54 return dir;
55}
56
57} // namespace
58
60 : eonc::Potential(eonc::PotType::ASE_ORCA, a_params) {
61 using namespace pybind11::literals;
63 counter = 0;
64 py::module_ sys = py::module_::import("sys");
65 // Fix for gh-184, see
66 // https://github.com/numpy/numpy/issues/20504#issuecomment-985542508
68 fpeh.eat_fpe();
69 ase = py::module_::import("ase");
70 fpeh.restore_fpe();
71 py::module_ ase_orca = py::module_::import("ase.calculators.orca");
72 py::module_ psutil = py::module_::import("psutil");
74 "ORCA_COMMAND", a_params.ase_orca_options().path, "", "", true);
75 std::string orca_simpleinput = eonc::helpers::get_value_from_env_or_param(
76 "ORCA_SIMPLEINPUT", a_params.ase_orca_options().simpleinput,
77 "ENGRAD HF-3c",
78 "Using ENGRAD HF-3c as a default input, set simpleinput or the "
79 "environment variable ORCA_SIMPLEINPUT.\n");
80
81 // Set up ORCA profile and calculator
82 py::object OrcaProfile = ase_orca.attr("OrcaProfile");
83 py::object ORCA = ase_orca.attr("ORCA");
84 size_t nproc{0};
85
86 if (a_params.ase_orca_options().nproc == "auto") {
87 nproc = py::cast<int>(psutil.attr("cpu_count")(false));
88 } else {
89 nproc = std::stoi(a_params.ase_orca_options().nproc);
90 }
91
92 // One directory per calculator instance so two LocalInProcess jobs in
93 // the same cwd do not share ORCA scratch and output files.
94 workDir = makeAseWorkDir("eon_ase_orca");
95
96 try {
97 this->calc =
98 ORCA("profile"_a = OrcaProfile(py::str(orcpth)),
99 "orcasimpleinput"_a = orca_simpleinput,
100 "orcablocks"_a = py::str(std::format("%pal nprocs {} end", nproc)),
101 "directory"_a = workDir.string());
102 } catch (...) {
103 std::error_code ec;
104 std::filesystem::remove_all(workDir, ec);
105 workDir.clear();
106 throw;
107 }
108};
109
111 QUILL_LOG_INFO(eonc::log::get(), "[ASEOrca] called potential {} times",
112 counter);
113 if (!workDir.empty()) {
114 std::error_code ec;
115 std::filesystem::remove_all(workDir, ec);
116 }
117}
118
119void ASEOrcaPot::force(long nAtoms, const double *R, const int *atomicNrs,
120 double *F, double *U, double *variance,
121 const double *box) {
122 using namespace pybind11::literals;
123 if (variance != nullptr) {
124 *variance = 0.0;
125 }
126 try {
127 const Eigen::Map<const AtomMatrix> positions(R, nAtoms, 3);
128 const Eigen::Map<const RotationMatrix> boxx(box);
129 const Eigen::Map<const Eigen::VectorXi> atmnmrs(atomicNrs, nAtoms);
130 py::object atoms = this->ase.attr("Atoms")(
131 "symbols"_a = atmnmrs, "positions"_a = positions, "cell"_a = boxx,
132 "charge"_a = params.ase_orca_options().charge);
133 atoms.attr("set_calculator")(this->calc);
134 atoms.attr("set_pbc")(std::tuple<bool, bool, bool>(true, true, true));
135 double py_e = py::cast<double>(atoms.attr("get_potential_energy")());
136 Eigen::MatrixXd py_force =
137 py::cast<Eigen::MatrixXd>(atoms.attr("get_forces")());
138
139 // Populate the output parameters
140 *U = py_e;
141 for (long i = 0; i < nAtoms; ++i) {
142 F[3 * i] = py_force(i, 0);
143 F[3 * i + 1] = py_force(i, 1);
144 F[3 * i + 2] = py_force(i, 2);
145 }
146 } catch (py::error_already_set &e) {
147 throw std::runtime_error(std::string("ASE-ORCA Python error: ") + e.what());
148 } catch (const std::exception &e) {
149 throw std::runtime_error(std::string("ASE-ORCA C++ exception: ") +
150 e.what());
151 }
152
153 counter++;
154 return;
155}
void force(long nAtoms, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box) override
Definition ASE_ORCA.cpp:119
virtual ~ASEOrcaPot()
Definition ASE_ORCA.cpp:110
ASEOrcaPot(const eonc::Parameters &a_params)
Definition ASE_ORCA.cpp:59
py::object ase
Definition ASE_ORCA.h:29
size_t counter
Definition ASE_ORCA.h:31
py::object calc
Definition ASE_ORCA.h:28
std::filesystem::path workDir
Definition ASE_ORCA.h:30
const ase_orca_options_t & ase_orca_options() const
Potential(PotType a_ptype)
Production default: construction-scope registry, else PotRegistry::get().
std::string get_value_from_env_or_param(const char *env_variable, const std::string &param_value, const std::string &default_value, const std::string &warning_message, const bool is_mandatory)
Definition EnvHelpers.cc:7
quill::Logger * get() noexcept
Get or create the default "combi" logger.
Definition EonLogger.h:44
RAII resource manager for the ARTn C library with global synchronization.
void ensure_interpreter()
Definition NbGuard.h:21