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/PyGuard.h"
17#include "eon/fpe_handler.h"
18
19#include <atomic>
20#include <format>
21#include <stdexcept>
22#include <string>
23#include <system_error>
24
25#ifdef _WIN32
26#include <process.h>
27#else
28#include <unistd.h>
29#endif
30
31namespace {
32
33std::filesystem::path makeAseWorkDir(const char *prefix) {
34 static std::atomic<long> instanceCount{0};
35#ifdef _WIN32
36 const long pid = static_cast<long>(_getpid());
37#else
38 const long pid = static_cast<long>(getpid());
39#endif
40 std::error_code ec;
41 auto dir = std::filesystem::temp_directory_path(ec);
42 if (ec) {
43 throw std::runtime_error(std::format(
44 "ASE-ORCA: cannot resolve temp directory: {}", ec.message()));
45 }
46 dir /= std::format("{}_{}_{}", prefix, pid, instanceCount.fetch_add(1));
47 std::filesystem::create_directories(dir, ec);
48 if (ec) {
49 throw std::runtime_error(
50 std::format("ASE-ORCA: cannot create work directory {}: {}",
51 dir.string(), ec.message()));
52 }
53 return dir;
54}
55
56} // namespace
57
58// XXX: This always assumes that charge is 0, mult is 1
59// ASE default ----------------------------^ ---------^
60// See also: https://gitlab.com/ase/ase/-/issues/1357
62 : Potential(PotType::ASE_ORCA, a_params) {
64 counter = 0;
65 py::module_ sys = py::module_::import("sys");
66 // Fix for gh-184, see
67 // https://github.com/numpy/numpy/issues/20504#issuecomment-985542508
69 fpeh.eat_fpe();
70 ase = py::module_::import("ase");
71 fpeh.restore_fpe();
72 py::module_ ase_orca = py::module_::import("ase.calculators.orca");
73 py::module_ psutil = py::module_::import("psutil");
75 "ORCA_COMMAND", a_params.ase_orca_options.path, "", "", true);
76 std::string orca_simpleinput = eonc::helpers::get_value_from_env_or_param(
77 "ORCA_SIMPLEINPUT", a_params.ase_orca_options.simpleinput, "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 variance = nullptr;
123 try {
124 AtomMatrix positions = AtomMatrix::Map(const_cast<double *>(R), nAtoms, 3);
125 RotationMatrix boxx = RotationMatrix::Map(const_cast<double *>(box), 3, 3);
126 Eigen::VectorXi atmnmrs =
127 Eigen::Map<Eigen::VectorXi>(const_cast<int *>(atomicNrs), nAtoms);
128 py::object atoms = this->ase.attr("Atoms")(
129 "symbols"_a = atmnmrs, "positions"_a = positions, "cell"_a = boxx);
130 atoms.attr("set_calculator")(this->calc);
131 atoms.attr("set_pbc")(std::tuple<bool, bool, bool>(true, true, true));
132 double py_e = py::cast<double>(atoms.attr("get_potential_energy")());
133 Eigen::MatrixXd py_force =
134 py::cast<Eigen::MatrixXd>(atoms.attr("get_forces")());
135
136 // Populate the output parameters
137 *U = py_e;
138 for (long i = 0; i < nAtoms; ++i) {
139 F[3 * i] = py_force(i, 0);
140 F[3 * i + 1] = py_force(i, 1);
141 F[3 * i + 2] = py_force(i, 2);
142 }
143 } catch (py::error_already_set &e) {
144 throw std::runtime_error(std::string("ASE-ORCA Python error: ") + e.what());
145 } catch (const std::exception &e) {
146 throw std::runtime_error(std::string("ASE-ORCA C++ exception: ") +
147 e.what());
148 }
149
150 counter++;
151 return;
152}
Eigen::Matrix< double, 3, 3, eOnStorageOrder > RotationMatrix
Definition Eigen.h:38
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
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 Parameters &a_params)
Definition ASE_ORCA.cpp:61
py::object ase
Definition ASE_ORCA.h:30
size_t counter
Definition ASE_ORCA.h:32
py::object calc
Definition ASE_ORCA.h:29
std::filesystem::path workDir
Definition ASE_ORCA.h:31
struct eonc::Parameters::ase_orca_options_t ase_orca_options
Potential(PotType a_ptype)
Definition Potential.h:35
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
void ensure_interpreter()
Definition NbGuard.h:21