Loading...
Searching...
No Matches
Potential.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#include "eon/EonLogger.h"
13#include <cctype>
14#include <csignal>
15#include <ctime>
16#include <limits>
17#include <memory>
18#include <span>
19#include <stdexcept>
20#include <string>
21#include <utility>
22#include <vector>
23
24#include "eon/HelperFunctions.h"
25#include "eon/Parameters.h"
26#include "eon/Potential.h"
27#include "eon/Runtime.h"
28
29#ifdef WITH_CATLEARN
31#endif
32
33#ifdef WITH_GPRD
35#endif
36
42#include "rgpot/LennardJones/LJClusterPot.hpp"
43#include "rgpot/LennardJones/LJPot.hpp"
44#include "rgpot/Morse/MorsePot.hpp"
45#include "rgpot/ZBL/ZBLPot.hpp"
46#ifdef RGPOT_HAS_DFTD3
47#include "rgpot/D3Pot/D3Pot.hpp"
48#endif
49#ifdef RGPOT_HAS_DFTD4
50#include "rgpot/D4Pot/D4Pot.hpp"
51#endif
52#ifdef RGPOT_HAS_EXPR
53#include "rgpot/ExprPot/ExprPot.hpp"
54#endif
55#include "rgpot/MOPACPot/MOPACPot.hpp"
56#include "rgpot/fortran/FortranPots.hpp"
57#ifndef IS_WINDOWS
59#ifdef WITH_RGPOT
61#endif
62#endif
63
64// Fortran potentials: always compiled, loaded at runtime via dlopen
65
66#ifdef EMBED_PYTHON
67#ifdef WITH_ASE_POT
69#endif
70#endif
71
72#ifdef EONMPI
74#endif
75
77
78#ifndef _WIN32
79#ifdef WITH_VASP
81#endif
82#endif
83
84#ifdef WITH_AMS
87#endif
88
89#ifdef WITH_ASE_ORCA
91#endif
92
93#ifdef WITH_ASE_NWCHEM
95#endif
96
97#ifdef WITH_METATOMIC
99#endif
100
101#ifdef WITH_WATER
103#ifdef WITH_FORTRAN
104#endif
106#endif
107
108// Should respect Fortran availability
109
110#ifdef WITH_XTB
112#endif
113
114#include <cmath>
115#include <limits>
116#include <stdexcept>
117
118std::tuple<double, AtomMatrix> eonc::Potential::get_ef(const AtomMatrix &pos,
119 const VectorXi &atmnrs,
120 const Matrix3d &box) {
121 double energy{std::numeric_limits<double>::infinity()};
122 long nAtoms = static_cast<long>(pos.rows());
123 AtomMatrix forces{MatrixXd::Zero(nAtoms, 3)};
124 double var{0}; // no variance for true potentials
125 const auto n = static_cast<size_t>(nAtoms);
126 this->force(std::span<const double>(pos.data(), n * 3),
127 std::span<const int>(atmnrs.data(), n),
128 std::span<double>(forces.data(), n * 3), &energy, &var,
129 std::span<const double>(box.data(), 9));
131 registry_.on_force_call(ptype);
132 if (!std::isfinite(energy) || !forces.allFinite()) {
133 throw std::runtime_error("Potential::get_ef: non-finite energy or forces");
134 }
135
136 return std::make_tuple(energy, forces);
137}
138
139namespace eonc::helpers {
140namespace {
141
142std::string lower_copy(std::string s) {
143 for (char &c : s) {
144 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
145 }
146 return s;
147}
148
149#ifdef RGPOT_HAS_EXPR
150#ifdef RGPOT_HAS_DFTD3
151rgpot::D3Damping d3_damping_from_params(const Parameters &params) {
152 rgpot::D3Damping damp = rgpot::D3Damping::BJ;
153 if (lower_copy(params.dftd_options().d3_damping) == "zero") {
154 damp = rgpot::D3Damping::Zero;
155 }
156 return damp;
157}
158#endif
159
160std::unique_ptr<rgpot::PotentialBase> make_expr_term(const std::string &raw,
161 const Parameters &params) {
162 const std::string name = lower_copy(raw);
163 if (name == "lj") {
164 return std::make_unique<rgpot::LJPot>(rgpot::LJConfig{});
165 }
166 if (name == "ljcluster") {
167 return std::make_unique<rgpot::LJClusterPot>(rgpot::LJClusterConfig{});
168 }
169 if (name == "morse" || name == "morse_pt") {
170 return std::make_unique<rgpot::MorsePot>(rgpot::MorseConfig{});
171 }
172 if (name == "zbl") {
173 return std::make_unique<rgpot::ZBLPot>(rgpot::ZBLConfig{
174 .cut_inner = params.zbl_options().cut_inner,
175 .cut_global = params.zbl_options().cut_global,
176 });
177 }
178#ifdef RGPOT_HAS_DFTD3
179 if (name == "d3" || name == "dftd3") {
180 return std::make_unique<rgpot::D3Pot>(rgpot::D3Config{
181 .damping = d3_damping_from_params(params),
182 .functional = params.dftd_options().functional,
183 .atm = params.dftd_options().atm,
184 });
185 }
186#endif
187#ifdef RGPOT_HAS_DFTD4
188 if (name == "d4" || name == "dftd4") {
189 return std::make_unique<rgpot::D4Pot>(rgpot::D4Config{
190 .functional = params.dftd_options().functional,
191 .charge = params.dftd_options().d4_charge,
192 .atm = params.dftd_options().atm,
193 });
194 }
195#endif
196 if (name == "mopac") {
197 return std::make_unique<rgpot::MOPACPot>(rgpot::MOPACPot::Config{
198 .charge = params.mopac_options().charge,
199 .spin = params.mopac_options().spin,
200 .model = params.mopac_options().model,
201 .engine_path = params.mopac_options().engine_path,
202 });
203 }
204 throw std::runtime_error(
205 "ExprPot unknown term '" + raw +
206 "' (lj, ljcluster, morse, zbl, d3/dftd3, d4/dftd4, mopac)");
207}
208
209std::vector<rgpot::ExprPot::Term> parse_expr_terms(const Parameters &params) {
210 std::vector<rgpot::ExprPot::Term> terms;
211 std::string buf = params.expr_options().terms;
212 std::string token;
213 auto flush = [&]() {
214 while (!token.empty() &&
215 std::isspace(static_cast<unsigned char>(token.front()))) {
216 token.erase(token.begin());
217 }
218 while (!token.empty() &&
219 std::isspace(static_cast<unsigned char>(token.back()))) {
220 token.pop_back();
221 }
222 if (!token.empty()) {
223 terms.emplace_back(token, make_expr_term(token, params));
224 token.clear();
225 }
226 };
227 for (char c : buf) {
228 if (c == ',') {
229 flush();
230 } else {
231 token.push_back(c);
232 }
233 }
234 flush();
235 if (terms.empty()) {
236 throw std::runtime_error(
237 "ExprPot needs [ExprPot] terms (comma-separated names used in "
238 "expression)");
239 }
240 return terms;
241}
242#endif
243
244} // namespace
245
246std::shared_ptr<Potential> makePotential(const Parameters &params) {
247 return makePotential(params.potential_options().potential, params);
248}
249std::shared_ptr<Potential>
250makePotential(PotType ptype, const Parameters &params, Runtime &runtime) {
251 PotentialConstructionScope scope(runtime.pots());
253 return makePotential(ptype, params);
254}
255std::shared_ptr<Potential> makePotential(PotType ptype,
256 const Parameters &params) {
257 // Inject config-file path before any potential constructor runs.
258 // Called on every code path including Job::Job which uses this overload.
259 PluginLoader::instance().add_config_paths(
261 switch (ptype) {
262 // TODO: Every potential must know their own type
263 case PotType::EMT: {
264 return (std::make_shared<EffectiveMediumTheory>(params));
265 break;
266 }
267 case PotType::EXT_POT: {
268 return (std::make_shared<ExtPot>(params));
269 break;
270 }
271 case PotType::LJ: {
272 return makeRgpot<rgpot::LJPot>(PotType::LJ, params, rgpot::LJConfig{});
273 break;
274 }
275 case PotType::LJCLUSTER: {
277 rgpot::LJClusterConfig{});
278 break;
279 }
280 case PotType::MORSE_PT: {
282 rgpot::MorseConfig{});
283 break;
284 }
285#ifdef CUH2_POT
286 case PotType::CUH2: {
288 break;
289 }
290#endif
291#ifdef WITH_WATER
292 case PotType::TIP4P: {
293 return (std::make_shared<Tip4p>(params));
294 break;
295 }
296 case PotType::SPCE: {
297 return (std::make_shared<SpceCcl>(params));
298 break;
299 }
300#ifdef WITH_FORTRAN
301 case PotType::TIP4P_PT: {
302 return (std::make_shared<Tip4p_Pt>(params));
303 break;
304 }
305 case PotType::TIP4P_H: {
307 params);
308 break;
309 }
310#endif
311#endif
312 // Fortran potentials: always available, loaded at runtime via dlopen
313 case PotType::EAM_AL: {
315 params);
316 break;
317 }
318 case PotType::EDIP: {
320 break;
321 }
322 case PotType::FEHE: {
324 break;
325 }
326 case PotType::LENOSKY_SI: {
328 params);
329 break;
330 }
331 case PotType::SW_SI: {
333 break;
334 }
335 case PotType::TERSOFF_SI: {
337 params);
338 break;
339 }
340#ifndef _WIN32
341#ifdef WITH_VASP
342 case PotType::VASP: {
343 return (std::make_shared<VASP>(params));
344 break;
345 }
346#endif
347#endif
348 case PotType::LAMMPS: {
349 return std::make_shared<LAMMPSPot>(params);
350 }
351#ifdef EONMPI
352 case PotType::MPI: {
353 return (std::make_shared<MPIPot>(params));
354 break;
355 }
356#endif
357#ifdef EMBED_PYTHON
358#ifdef WITH_ASE_POT
359 case PotType::ASE_POT: {
360 return (std::make_shared<ASE>(params));
361 break;
362 }
363#endif
364#endif
365#ifdef WITH_AMS
366 case PotType::AMS: {
367 return (std::make_shared<AMS>(params));
368 break;
369 }
370 case PotType::AMS_IO: {
371 return (std::make_shared<AMS_IO>(params));
372 break;
373 }
374#endif
375#ifdef WITH_CATLEARN
376 case PotType::CatLearn: {
377 return (std::make_shared<CatLearnPot>(params));
378 break;
379 }
380#endif
381// TODO: Handle Fortran interaction
382#ifdef WITH_XTB
383 case PotType::XTB: {
384 return (std::make_shared<XTBPot>(params));
385 break;
386 }
387#endif
388#ifdef WITH_ASE_ORCA
389 case PotType::ASE_ORCA: {
390 return (std::make_shared<ASEOrcaPot>(params));
391 break;
392 }
393#endif
394#ifdef WITH_ASE_NWCHEM
395 case PotType::ASE_NWCHEM: {
396 return (std::make_shared<ASENwchemPot>(params));
397 break;
398 }
399#endif
400#ifdef WITH_METATOMIC
401 case PotType::METATOMIC: {
402 return (std::make_shared<MetatomicPotential>(params));
403 break;
404 }
405#endif
406#ifdef RGPOT_HAS_DFTD3
407 case PotType::DFTD3: {
408 rgpot::D3Damping damp = rgpot::D3Damping::BJ;
409 std::string dname = params.dftd_options().d3_damping;
410 for (char &c : dname) {
411 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
412 }
413 if (dname == "zero") {
414 damp = rgpot::D3Damping::Zero;
415 }
417 PotType::DFTD3, params,
418 rgpot::D3Config{.damping = damp,
419 .functional = params.dftd_options().functional,
420 .atm = params.dftd_options().atm});
421 break;
422 }
423#endif
424#ifdef RGPOT_HAS_DFTD4
425 case PotType::DFTD4: {
427 PotType::DFTD4, params,
428 rgpot::D4Config{.functional = params.dftd_options().functional,
429 .charge = params.dftd_options().d4_charge,
430 .atm = params.dftd_options().atm});
431 break;
432 }
433#endif
434 case PotType::ZBL: {
436 PotType::ZBL, params,
437 rgpot::ZBLConfig{
438 .cut_inner = params.zbl_options().cut_inner,
439 .cut_global = params.zbl_options().cut_global,
440 });
441 break;
442 }
443#ifndef IS_WINDOWS
445 return (std::make_shared<SocketNWChemPot>(params));
446 break;
447 }
448#endif
449#ifdef WITH_RGPOT
450 case PotType::RGPOT: {
451 return (std::make_shared<RgpotPot>(params));
452 break;
453 }
454#endif
455 case PotType::MOPAC: {
457 PotType::MOPAC, params,
458 rgpot::MOPACPot::Config{
459 .charge = params.mopac_options().charge,
460 .spin = params.mopac_options().spin,
461 .model = params.mopac_options().model,
462 .engine_path = params.mopac_options().engine_path,
463 });
464 break;
465 }
466#ifdef RGPOT_HAS_EXPR
467 case PotType::EXPR: {
468 if (params.expr_options().expression.empty()) {
469 throw std::runtime_error(
470 "ExprPot needs [ExprPot] expression, e.g. 0.5*lj + d3");
471 }
472 rgpot::ExprPot expr(params.expr_options().expression,
473 parse_expr_terms(params));
474 return std::make_shared<RgpotAdapter<rgpot::ExprPot>>(PotType::EXPR, params,
475 std::move(expr));
476 break;
477 }
478#endif
479 default:
480 EONC_LOG_ERROR("No known potential could be constructed from {}",
481 magic_enum::enum_name(ptype));
482 eonc::log::get()->flush_log();
483 throw std::runtime_error("Terminating");
484 break;
485 }
486}
487
488} // namespace eonc::helpers
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Definition Eigen.h:35
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
#define EONC_LOG_ERROR(...)
Definition EonLogger.h:261
std::shared_ptr< eonc::Potential > makeRgpotDefault(eonc::PotType ptype, const eonc::Parameters &params)
Factory arm helper for kernels whose parameters are fixed tabulated data with no eOn-side configurati...
std::shared_ptr< eonc::Potential > makeRgpot(eonc::PotType ptype, const eonc::Parameters &params, const Cfg &cfg)
Factory arm helper: construct the kernel from its config and wrap it.
Wrapper for Eon.
Wrapper for Eon.
const expr_options_t & expr_options() const
const potential_options_t & potential_options() const
const zbl_options_t & zbl_options() const
const mopac_options_t & mopac_options() const
const dftd_options_t & dftd_options() const
void add_config_paths(const std::string &colon_paths) override
Inject search paths from the eOn config file.
static PluginLoader & instance()
Thread-safe singleton accessor (Meyer's pattern).
RAII: Potential default construction records on this registry instead of PotRegistry::get().
Definition Potential.h:30
std::atomic< size_t > forceCallCounter
Definition Potential.h:53
virtual void force(long nAtoms, const double *positions, const int *atomicNrs, double *forces, double *energy, double *variance, const double *box)=0
std::tuple< double, AtomMatrix > get_ef(const AtomMatrix &pos, const VectorXi &atmnrs, const Matrix3d &box)
IPotRegistry & registry_
Definition Potential.h:47
PotType ptype
Definition Potential.h:44
Move-only composition root for process resources (dlopen loaders and the potential registry).
Definition Runtime.h:27
PotRegistry & pots() noexcept
Definition Runtime.cpp:33
PluginLoader & plugins() noexcept
Definition Runtime.cpp:39
std::shared_ptr< Potential > makePotential(const Parameters &params)
quill::Logger * get() noexcept
Get or create the default "combi" logger.
Definition EonLogger.h:44