Loading...
Searching...
No Matches
GenericEngineLoader.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*/
13#include <cstdlib>
14#include <stdexcept>
15#include <vector>
16#ifndef _WIN32
17#include <dlfcn.h>
18#endif
19
20namespace {
21void *open_lib(const char *path) {
22#ifndef _WIN32
23 return dlopen(path, RTLD_NOW | RTLD_GLOBAL);
24#else
25 return nullptr;
26#endif
27}
28void close_lib(void *h) {
29#ifndef _WIN32
30 if (h)
31 dlclose(h);
32#endif
33}
34void *load_sym(void *h, const char *n) {
35#ifndef _WIN32
36 return dlsym(h, n);
37#else
38 return nullptr;
39#endif
40}
41} // namespace
42
44 : m_tag(opt.tag.empty() ? opt.library : opt.tag) {
45 std::vector<std::string> paths;
46 if (!opt.engine_path.empty())
47 paths.push_back(opt.engine_path);
48 if (!opt.env_var.empty())
49 if (const char *e = std::getenv(opt.env_var.c_str()))
50 if (e && *e)
51 paths.emplace_back(e);
52 paths.push_back(opt.library);
53 auto add_dirs = [&](const char *env) {
54 if (!env)
55 return;
56 std::string s(env);
57 size_t i = 0;
58 while (i < s.size()) {
59 const auto j = s.find(':', i);
60 const auto dir = s.substr(i, j == std::string::npos ? j : j - i);
61 if (!dir.empty())
62 paths.push_back(dir + "/" + opt.library);
63 if (j == std::string::npos)
64 break;
65 i = j + 1;
66 }
67 };
68 add_dirs(std::getenv("EON_POTENTIALS_PATH"));
69
70 std::string last_dlerr;
71 for (const auto &p : paths) {
72 m_lib = open_lib(p.c_str());
73 if (m_lib)
74 break;
75#ifndef _WIN32
76 if (const char *e = dlerror())
77 last_dlerr = e;
78#endif
79 }
80 if (!m_lib) {
81 std::string msg =
82 "RGPOT(" + m_tag + "): " + opt.library + " not found (set " +
83 (opt.env_var.empty() ? std::string("engine_path") : opt.env_var) +
84 " or EON_POTENTIALS_PATH)";
85 if (!last_dlerr.empty())
86 msg += std::string("; last dlerror: ") + last_dlerr;
87 throw std::runtime_error(msg);
88 }
89
90 auto abi =
91 reinterpret_cast<int (*)()>(load_sym(m_lib, "rgpot_engine_abi_version"));
92 m_create =
93 reinterpret_cast<create_fn>(load_sym(m_lib, "rgpot_engine_create"));
94 m_destroy =
95 reinterpret_cast<destroy_fn>(load_sym(m_lib, "rgpot_engine_destroy"));
96 m_force = reinterpret_cast<force_fn>(load_sym(m_lib, "rgpot_engine_force"));
97 if (!abi || !m_create || !m_destroy || !m_force ||
98 abi() != RGPOT_ENGINE_ABI_VERSION) {
99 close_lib(m_lib);
100 m_lib = nullptr;
101 throw std::runtime_error("RGPOT(" + m_tag +
102 "): engine C ABI missing/mismatch");
103 }
104 char err[1024]{};
105 m_pot = m_create(opt.config.data(), opt.config.size(), err, sizeof err);
106 if (!m_pot) {
107 close_lib(m_lib);
108 m_lib = nullptr;
109 throw std::runtime_error("RGPOT(" + m_tag +
110 "): create failed: " + std::string(err));
111 }
112}
113
115 // Destroy the pot while the engine is still mapped. Do not dlclose the
116 // engine: torch static teardown after dlclose routinely SEGV on exit.
117 if (m_pot && m_destroy)
119 m_pot = nullptr;
120 m_lib = nullptr;
121}
122
123void GenericEngineLoader::force(long N, const double *R, const int *atomicNrs,
124 double *F, double *U, double *variance,
125 const double *box) const {
126 if (!m_pot || !m_force)
127 throw std::runtime_error("RGPOT(" + m_tag + "): engine not available");
128 double var = 0.0;
129 const int rc = m_force(m_pot, N, R, atomicNrs, F, U,
130 variance ? variance : &var, box, nullptr, nullptr);
131 if (rc != 0)
132 throw std::runtime_error("RGPOT(" + m_tag + "): force failed");
133}
GenericEngineLoader(const GenericEngineOptions &opt)
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box) const
#define RGPOT_ENGINE_ABI_VERSION
std::string env_var
Optional env var naming an explicit .so path (checked first).
std::string engine_path
Optional explicit .so path from config.
std::string library
Library basename to search for, e.g. "libuma_engine.so".