Loading...
Searching...
No Matches
LammpsLoader.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
14#include <cstdlib>
15#include <filesystem>
16#include <iostream>
17#include <string>
18#include <system_error>
19
20namespace eonc {
21
22namespace {
23
24const char *const *lammps_lib_names() noexcept {
25#ifdef _WIN32
26 static const char *names[] = {"lammps.dll", "liblammps.dll", nullptr};
27#elif defined(__APPLE__)
28 static const char *names[] = {"liblammps.dylib", "liblammps.0.dylib",
29 nullptr};
30#else
31 static const char *names[] = {"liblammps.so", "liblammps.so.0", nullptr};
32#endif
33 return names;
34}
35
36bool path_exists(const std::filesystem::path &p) {
37 std::error_code ec;
38 return std::filesystem::exists(p, ec);
39}
40
41bool lib_file_visible(const char *const *names) {
42 for (const char *const *n = names; *n; ++n) {
43 if (path_exists(*n))
44 return true;
45 }
46#ifdef _WIN32
47 const char *path = std::getenv("PATH");
48 const char sep = ';';
49#else
50 const char *path = std::getenv("LD_LIBRARY_PATH");
51#ifdef __APPLE__
52 if (!path || !*path)
53 path = std::getenv("DYLD_LIBRARY_PATH");
54#endif
55 const char sep = ':';
56#endif
57 if (!path || !*path)
58 return false;
59 const std::string paths(path);
60 std::string::size_type start = 0;
61 while (start < paths.size()) {
62 auto pos = paths.find(sep, start);
63 if (pos == std::string::npos)
64 pos = paths.size();
65 if (pos > start) {
66 const std::filesystem::path dir(paths.substr(start, pos - start));
67 for (const char *const *n = names; *n; ++n) {
68 if (path_exists(dir / *n))
69 return true;
70 }
71 }
72 start = pos + 1;
73 }
74 return false;
75}
76
77} // namespace
78
80 static LammpsLoader loader;
81 return loader;
82}
83
85 if (m_tried)
86 return;
87 m_tried = true;
88 m_last_error.clear();
89
90 // Always dlopen the soname. lib_file_visible only walks cwd and
91 // LD_LIBRARY_PATH; the dynamic linker also searches ld.so.cache and
92 // the default lib dirs. Gating on visibility skipped a distro so.
93 m_handle = dynlib::openFirst(lammps_lib_names());
94 if (!m_handle) {
95 const std::string dle = dynlib::error();
96 if (!lib_file_visible(lammps_lib_names())) {
97 m_last_error = "liblammps.so not visible on LD_LIBRARY_PATH / cwd "
98 "and dlopen of the soname failed "
99 "(liblammps_pot.so is the eOn plugin, not LAMMPS)";
100 } else {
101 m_last_error = "liblammps.so is visible but dlopen failed";
102 }
103 if (!dle.empty()) {
104 m_last_error += ": " + dle;
105 }
106 return;
107 }
108
110 close = dynlib::loadSym<close_fn>(m_handle, "lammps_close");
111 command = dynlib::loadSym<command_fn>(m_handle, "lammps_command");
112 file = dynlib::loadSym<file_fn>(m_handle, "lammps_file");
114 dynlib::loadSym<scatter_atoms_fn>(m_handle, "lammps_scatter_atoms");
116 dynlib::loadSym<extract_var_fn>(m_handle, "lammps_extract_variable");
117
118#ifdef EONMPI
119 open_mpi = dynlib::loadSym<open_mpi_fn>(m_handle, "lammps_open");
120#endif
121
122 if (!open_no_mpi || !close || !command || !file || !scatter_atoms ||
124 std::cerr << "[LAMMPS] Library loaded but missing required symbols\n";
125 m_last_error = "opened a liblammps* but lammps_open_no_mpi is missing "
126 "(plugin or ABI mismatch, not the LAMMPS C library)";
128 m_handle = {};
129 return;
130 }
131
132 m_loaded = true;
133}
134
136
138 return lib_file_visible(lammps_lib_names());
139}
140
143 if (!m_loaded) {
144 std::string msg =
145 "LAMMPS potential requested but liblammps is not usable.\n";
146 if (!m_last_error.empty()) {
147 msg += m_last_error;
148 msg += "\n";
149 }
150 msg += "Need a real liblammps.so with lammps_open_no_mpi, built for "
151 "this glibc. conda-forge latest may need a newer libc than "
152 "the host. liblammps_pot.so is the eOn plugin, not LAMMPS.";
153 throw std::runtime_error(msg);
154 }
155}
156
157} // namespace eonc
open_no_mpi_fn open_no_mpi
extract_var_fn extract_variable
scatter_atoms_fn scatter_atoms
LammpsLoader(const LammpsLoader &)=delete
void require_loaded() override
Load on first use, then throw if liblammps is not available.
std::string m_last_error
static LammpsLoader & instance()
Thread-safe singleton accessor (Meyer's pattern). Does not dlopen.
dynlib::Handle m_handle
bool available() const override
Filesystem probe: a candidate liblammps file is visible without dlopen, so LAMMPS static initializers...
std::string error()
Definition DynLib.h:82
void close(Handle h) noexcept
Definition DynLib.h:77
Fn loadSym(Handle h, const char *name) noexcept
Load a symbol and cast to a function pointer type.
Definition DynLib.h:99
Handle openFirst(const char *const names[]) noexcept
Try a list of library names in order, return first successful handle.
Definition DynLib.h:89
RAII resource manager for the ARTn C library with global synchronization.