Loading...
Searching...
No Matches
RgpotAdapter.h
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** Adapter over rgpot potential kernels. Migrated pair potentials live in
7** librgpot (github.com/OmniPotentRPC/rgpot); eOn's config surface,
8** PotRegistry, force counters, and caching stay on this side.
9*/
10#pragma once
11
12#include <memory>
13#include <stdexcept>
14#include <type_traits>
15#include <vector>
16
17#include "eon/Parameters.h"
18#include "eon/Potential.h"
20
21#include "rgpot/ForceStructs.hpp"
22#include "rgpot/pot_caps.hpp"
23
31template <class RPot> class RgpotAdapter final : public eonc::Potential {
32public:
36 template <class Cfg>
37 requires(!std::is_base_of_v<eonc::IPluginLoader, std::remove_cvref_t<Cfg>>)
39 const Cfg &cfg)
41
42 template <class Cfg>
43 requires(!std::is_base_of_v<eonc::IPluginLoader, std::remove_cvref_t<Cfg>>)
45 const Cfg &cfg, eonc::IPluginLoader &loader)
46 : eonc::Potential(ptype, params), pot_(cfg), loader_(loader) {
47 finishConstruct(params);
48 }
49
52 : eonc::Potential(ptype, params), pot_(),
53 loader_(eonc::PluginLoader::instance()) {
54 finishConstruct(params);
55 }
56
58 eonc::IPluginLoader &loader)
59 : eonc::Potential(ptype, params), pot_(), loader_(loader) {
60 finishConstruct(params);
61 }
62
65 RPot &&kernel)
66 : RgpotAdapter(ptype, params, std::move(kernel),
67 eonc::PluginLoader::instance()) {}
68
70 RPot &&kernel, eonc::IPluginLoader &loader)
71 : eonc::Potential(ptype, params), pot_(std::move(kernel)),
72 loader_(loader) {
73 finishConstruct(params);
74 }
75
76 void force(long N, const double *R, const int *atomicNrs, double *F,
77 double *U, double *variance, const double *box) override {
78 rgpot::ForceInput in{static_cast<size_t>(N), R, atomicNrs, box};
79 rgpot::ForceOut out{F, 0.0, 0.0};
80 pot_.forceImpl(in, &out);
81 *U = out.energy;
82 if (variance != nullptr) {
83 *variance = out.variance;
84 }
85 storeStress(out);
86 }
87
88 [[nodiscard]] bool computesStress() const noexcept override {
89 return haveStress_;
90 }
91
94 [[nodiscard]] Matrix3d cauchyStress() const override {
95 if (!haveStress_) {
96 throw std::logic_error("rgpot kernel did not return a stress tensor");
97 }
98 return stress_;
99 }
100
105 [[nodiscard]] bool supportsBatchEvaluation() const noexcept override {
106 return pot_.caps().batched;
107 }
108
117 void forceBatch(long nSystems, long nAtoms, const double *const *positions,
118 const int *const *atomicNrs, double *const *forces,
119 double *energies, double *variances,
120 const double *const *boxes) override {
121 const auto n = static_cast<size_t>(nSystems);
122 std::vector<rgpot::ForceInput> in;
123 std::vector<rgpot::ForceOut> out;
124 in.reserve(n);
125 out.reserve(n);
126 for (size_t i = 0; i < n; ++i) {
127 in.push_back(rgpot::ForceInput{static_cast<size_t>(nAtoms), positions[i],
128 atomicNrs[i], boxes[i]});
129 out.push_back(rgpot::ForceOut{forces[i], 0.0, 0.0});
130 }
131
132 pot_.forceBatchImpl(
133 rgpot::ForceBatch{.nSystems = n, .in = in.data(), .out = out.data()});
134
135 haveStress_ = false;
136 for (size_t i = 0; i < n; ++i) {
137 energies[i] = out[i].energy;
138 if (variances != nullptr) {
139 variances[i] = out[i].variance;
140 }
141 if (out[i].has_stress) {
142 storeStress(out[i]);
143 }
146 }
147 }
148
149 [[nodiscard]] bool isThreadSafe() const noexcept override {
150 return pot_.caps().reentrancy == rgpot::Reentrancy::SharedInstance;
151 }
152
156 [[nodiscard]] bool requiresIsolatedMoleculeLayout() const noexcept override {
157 return !pot_.caps().periodic;
158 }
159
160 [[nodiscard]] bool needsPerImageInstance() const noexcept override {
161 const auto caps = pot_.caps();
162 return caps.perImageInstances ||
163 caps.reentrancy == rgpot::Reentrancy::PerInstance;
164 }
165
166 [[nodiscard]] const RPot &kernel() const noexcept { return pot_; }
167
170 [[nodiscard]] double finiteCutoff() const noexcept override {
171 if constexpr (requires(const RPot &k) { k.config().cutoff; }) {
172 return pot_.config().cutoff;
173 } else {
174 return 0.0;
175 }
176 }
177
178private:
179 void finishConstruct(const eonc::Parameters &params) {
180 loader_.add_config_paths(params.potential_options().potentialsPath);
181 validateCaps();
182 }
183
184 void validateCaps() const {
185 const auto caps = pot_.caps();
186 using R = rgpot::Reentrancy;
187 if (caps.reentrancy != R::SharedInstance &&
188 caps.reentrancy != R::PerInstance &&
189 caps.reentrancy != R::ProcessSerial) {
190 throw std::runtime_error("rgpot kernel advertised an unknown reentrancy");
191 }
192 }
193
194 void storeStress(const rgpot::ForceOut &out) {
195 haveStress_ = out.has_stress != 0;
196 if (!haveStress_) {
197 return;
198 }
199 for (int row = 0; row < 3; ++row) {
200 for (int col = 0; col < 3; ++col) {
201 stress_(row, col) = out.stress[row * 3 + col];
202 }
203 }
204 }
205
206 RPot pot_;
208 Matrix3d stress_{Matrix3d::Zero()};
209 bool haveStress_{false};
210};
211
214template <class RPot>
215std::shared_ptr<eonc::Potential>
217 return std::make_shared<RgpotAdapter<RPot>>(ptype, params);
218}
219
221template <class RPot, class Cfg>
222std::shared_ptr<eonc::Potential>
223makeRgpot(eonc::PotType ptype, const eonc::Parameters &params, const Cfg &cfg) {
224 return std::make_shared<RgpotAdapter<RPot>>(ptype, params, cfg);
225}
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Definition Eigen.h:35
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.
RgpotAdapter(eonc::PotType ptype, const eonc::Parameters &params, RPot &&kernel)
Take a pre-built kernel (ExprPot and other move-only constructors).
bool isThreadSafe() const noexcept override
Whether this potential's force() can be called from multiple threads on the SAME instance.
bool supportsBatchEvaluation() const noexcept override
Reports whether the kernel serves a batch natively.
bool requiresIsolatedMoleculeLayout() const noexcept override
Kernels that do not implement periodic boundaries must not have their coordinates wrapped.
eonc::IPluginLoader & loader_
RgpotAdapter(eonc::PotType ptype, const eonc::Parameters &params)
Kernels with no configuration surface default-construct in place.
bool computesStress() const noexcept override
True when force() leaves a Cauchy stress that cauchyStress() can read until the next force() on this ...
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box) override
RgpotAdapter(eonc::PotType ptype, const eonc::Parameters &params, eonc::IPluginLoader &loader)
Test seam: injected loader, no process-default search-path mutation.
const RPot & kernel() const noexcept
void validateCaps() const
void storeStress(const rgpot::ForceOut &out)
void finishConstruct(const eonc::Parameters &params)
Matrix3d stress_
RgpotAdapter(eonc::PotType ptype, const eonc::Parameters &params, const Cfg &cfg)
Kernels construct in place from their config: several hold mutexes or other immovable state,...
bool needsPerImageInstance() const noexcept override
Whether NEB should create separate Potential instances per image for true parallel force evaluation.
Matrix3d cauchyStress() const override
Cauchy stress from the last force() that reported one.
double finiteCutoff() const noexcept override
Classical kernels expose config().cutoff.
RgpotAdapter(eonc::PotType ptype, const eonc::Parameters &params, const Cfg &cfg, eonc::IPluginLoader &loader)
Test seam: injected loader, no process-default search-path mutation.
void forceBatch(long nSystems, long nAtoms, const double *const *positions, const int *const *atomicNrs, double *const *forces, double *energies, double *variances, const double *const *boxes) override
Hands eOn's batch straight to the kernel.
RgpotAdapter(eonc::PotType ptype, const eonc::Parameters &params, RPot &&kernel, eonc::IPluginLoader &loader)
Test seam: injected loader, no process-default search-path mutation.
const potential_options_t & potential_options() const
static PluginLoader & instance()
Thread-safe singleton accessor (Meyer's pattern).
void on_force_call(PotType t) noexcept override
static PotRegistry & get() noexcept
Process-lifetime singleton.
std::atomic< size_t > forceCallCounter
Definition Potential.h:53
PotType ptype
Definition Potential.h:44
Potential(PotType a_ptype)
Production default: construction-scope registry, else PotRegistry::get().
RAII resource manager for the ARTn C library with global synchronization.