Loading...
Searching...
No Matches
ASE.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/PyGuard.h"
15#include "eon/fpe_handler.h"
16#include <pybind11/embed.h>
17#include <pybind11/numpy.h> // for py::array_t
18#include <pybind11/pybind11.h>
19#include <stdexcept>
20#include <string>
21#include <tuple>
22#include <vector>
23
24namespace py = pybind11;
25
26ASE::ASE(const Parameters &a_params)
27 : Potential(PotType::ASE_POT, a_params) {
29 counter = 1;
30 std::string py_file = a_params.potential_options.extPotPath;
31
32 // import
33 try {
34 // must briefly disable FPE because Python packages like Numpy causes it
35 // during import
37 fpeh.eat_fpe();
38
39 // Create a Python script to use importlib.util to load the module
40 py::exec(R"(
41 import sys
42 import importlib.util
43
44 def load_module_from_path(module_name, file_path):
45 spec = importlib.util.spec_from_file_location(module_name, file_path)
46 module = importlib.util.module_from_spec(spec)
47 sys.modules[module_name] = module
48 spec.loader.exec_module(module)
49 return module
50 )");
51
52 // Prepare the module name and file path
53 std::string module_name = "ase_eon";
54 py::object load_module = py::globals()["load_module_from_path"];
55 py_module = load_module(module_name, py_file);
56
57 fpeh.restore_fpe();
58
59 calculator = py_module.attr("ase_calc")();
60 _calculate = py_module.attr("_calculate");
61
62 } catch (const std::exception &e) {
63 fprintf(stderr,
64 "ASE Calculator: Exception during Python module import: %s\n",
65 e.what());
66 fprintf(stderr, "%s should exist and have no errors on the Python side.\n",
67 py_file.c_str());
68 throw std::runtime_error(std::string("ASE calculator import failed: ") +
69 e.what());
70 }
71 return;
72}
73
74void ASE::force(long nAtoms, const double *R, const int *atomicNrs, double *F,
75 double *U, double *variance, const double *box) {
76 variance = nullptr;
77 try {
78 // TODO(rg): This is easier on the type system if Eigen::Map is used like in
79 // ASE_ORCA convert arrays to Numpy arrays
80 std::vector<size_t> R_shape = {static_cast<size_t>(nAtoms), 3};
81 py::array_t<double> R_np(R_shape, R);
82 py::array_t<int> atomicNrs_np(nAtoms, atomicNrs);
83 py::array_t<double> box_np({3, 3}, box);
84
85 // get energy and forces (in this order) from Python
86 std::tuple<double, py::array_t<double>> py_result =
87 _calculate(R_np, atomicNrs_np, box_np, calculator)
88 .cast<std::tuple<double, py::array_t<double>>>();
89
90 // copy the results to the output arrays
91 *U = std::get<0>(py_result);
92 py::array_t<double> forces = std::get<1>(py_result);
93 auto buffer = forces.request();
94 double *ptr = static_cast<double *>(buffer.ptr);
95 std::copy(ptr, ptr + buffer.size, F);
96
97 } catch (py::error_already_set &e) {
98 fprintf(stderr, "ASE calculator: Python error: %s\n", e.what());
99 throw std::runtime_error(std::string("ASE calculator Python error: ") +
100 e.what());
101 } catch (const std::exception &e) {
102 fprintf(stderr, "ASE calculator: C++ exception: %s\n", e.what());
103 throw std::runtime_error(std::string("ASE calculator C++ exception: ") +
104 e.what());
105 }
106
107 counter++;
108 return;
109}
py::object calculator
Definition ASE.h:24
void force(long nAtoms, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box) override
Definition ASE.cpp:64
py::module_ py_module
Definition ASE.h:23
ASE(const Parameters &a_params)
Definition ASE.cpp:26
size_t counter
Definition ASE.h:22
py::object _calculate
Definition ASE.h:25
struct eonc::Parameters::potential_options_t potential_options
Potential(PotType a_ptype)
Definition Potential.h:35
void ensure_interpreter()
Definition NbGuard.h:21