20#include "eon_relax_engine.capnp.h"
24#include <capnp/serialize.h>
51uint64_t fnv1a64(
const char *s) {
52 uint64_t h = 14695981039346656037ULL;
56 for (
const unsigned char *p =
reinterpret_cast<const unsigned char *
>(s); *p;
58 h ^=
static_cast<uint64_t
>(*p);
59 h *= 1099511628211ULL;
64void set_err(
char *errbuf,
size_t errlen,
const char *msg) {
65 if (!errbuf || errlen == 0) {
68 std::snprintf(errbuf, errlen,
"%s", msg ? msg :
"");
78 Eigen::VectorXi z(band->
n_atoms);
79 for (
long a = 0; a < band->
n_atoms; ++a) {
83 Eigen::VectorXd masses = Eigen::VectorXd::Ones(band->
n_atoms);
85 for (
long a = 0; a < band->
n_atoms; ++a) {
86 masses(a) = band->
masses[a];
93 Eigen::Map<const AtomMatrix> mapped(src, band->
n_atoms, 3);
95 const double *b = band->
boxes + image * 9;
96 const Matrix3d cell = Eigen::Map<const Matrix3d>(b);
104 for (
long a = 0; a < band->
n_atoms; ++a) {
108 for (
long a = 0; a < band->
n_atoms; ++a) {
115void write_image(
double *dst,
const Matter &m,
long n_atoms) {
117 Eigen::Map<AtomMatrix> mapped(dst, n_atoms, 3);
125 for (int64_t a = 0; a < band->
n_atoms; ++a) {
126 const double m = band->
masses[a];
127 if (!(m > 0.0) || !std::isfinite(m)) {
136 std::memset(out, 0,
sizeof(*out));
139 out->
kind =
static_cast<int32_t
>(kind);
144void dirty_keep_frame(
Matter &m) {
152 dirty_keep_frame(*neb.
path[0]);
153 dirty_keep_frame(*neb.
path[
static_cast<size_t>(neb.
numImages + 1)]);
157 uint64_t *epoch,
const void *config,
size_t config_len,
158 char *errbuf,
size_t errlen) {
159 if (config_len %
sizeof(::capnp::word) != 0) {
160 set_err(errbuf, errlen,
"-13 RelaxEngineParams capnp root");
164 const auto nwords = config_len /
sizeof(::capnp::word);
165 std::vector<::capnp::word> words(nwords);
166 std::memcpy(words.data(), config, config_len);
167 ::kj::ArrayPtr<const ::capnp::word> view(words.data(), nwords);
168 ::capnp::FlatArrayMessageReader reader(view);
169 auto root = reader.getRoot<eonc::params_ssot::RelaxEngineParams>();
170 const auto kind_txt = root.getKind();
171 if (kind_txt ==
"neb") {
173 }
else if (kind_txt ==
"saddle") {
176 set_err(errbuf, errlen,
"-11 unknown RelaxEngineParams.kind");
179 auto neb = root.getNeb();
181 neb_o.image_count =
static_cast<long>(neb.getImageCount());
182 neb_o.max_iterations =
static_cast<long>(neb.getMaxIterations());
183 neb_o.force_tolerance = neb.getForceTolerance();
184 neb_o.climbing_image.enabled = neb.getClimbingImage();
185 neb_o.endpoints.minimize = neb.getMinimizeEndpoints();
186 auto saddle = root.getSaddle();
188 saddle_o.max_iterations =
static_cast<long>(
saddle.getMaxIterations());
189 saddle_o.converged_force =
saddle.getConvergedForce();
190 *epoch = root.getSurfaceEpoch();
192 static_cast<long>(root.getRandomSeed());
194 root.getUncertainty();
196 }
catch (
const std::exception &) {
197 set_err(errbuf, errlen,
"-13 RelaxEngineParams capnp root");
200 set_err(errbuf, errlen,
"-13 RelaxEngineParams capnp root");
217 std::shared_ptr<eonc::HostSurfacePotential>
step_pot;
237 stamp_outcome(out, kind, epoch);
272 static const std::string
id =
273 VERSION +
"+git." + GIT_HASH_FULL +
"+feat." +
274 std::to_string(fnv1a64(FEATURES_STRING.c_str()));
283 char *errbuf,
size_t errlen) {
284 auto make_engine = [&]() -> std::unique_ptr<EonRelaxEngine> {
286 return std::make_unique<EonRelaxEngine>();
287 }
catch (
const std::bad_alloc &) {
288 set_err(errbuf, errlen,
"-19 out of memory");
292 if (config ==
nullptr && config_len == 0) {
293 auto eng = make_engine();
298 return eng.release();
300 if (config ==
nullptr || config_len == 0) {
301 set_err(errbuf, errlen,
"-13 RelaxEngineParams capnp root");
304 auto eng = make_engine();
308 const int prc = apply_relax_params(eng->params, &eng->kind, &eng->epoch,
309 config, config_len, errbuf, errlen);
313 return eng.release();
372 if (!known_kind(eng->
kind)) {
375 const int mass_rc = masses_ok(band);
379 stamp_outcome(out, eng->
kind, eng->
epoch);
382 std::make_shared<eonc::HostSurfacePotential>(surface, user, eng->
epoch);
395 std::vector<Matter> path;
396 path.reserve(
static_cast<size_t>(band->
n_images));
397 for (int64_t i = 0; i < band->
n_images; ++i) {
399 fill_matter(img, band,
static_cast<long>(i), eng->
epoch);
400 path.push_back(std::move(img));
402 auto neb = std::make_unique<NudgedElasticBand>(std::move(path),
404 pot->bindPath(neb->path);
405 dirty_endpoints(*neb);
406 const auto st = neb->compute();
407 if (st == NudgedElasticBand::NEBStatus::INIT ||
408 st == NudgedElasticBand::NEBStatus::RUNNING) {
409 out->
status = neb_status(st);
412 out->
status = neb_status(st);
419 out->
max_force = neb->convergenceForce();
421 for (int64_t i = 1; i + 1 < band->
n_images; ++i) {
423 *neb->path[
static_cast<size_t>(i)],
424 static_cast<long>(band->
n_atoms));
437 auto matter = std::make_shared<Matter>(pot, eng->
params);
438 fill_matter(*matter, band, 0, eng->
epoch);
440 Eigen::Map<const AtomMatrix> mmap(band->
mode, band->
n_atoms, 3);
442 const double reactant_e = matter->getPotentialEnergy();
444 const int sst = search.
run();
453 }
catch (
const std::exception &) {
504 const int mass_rc = masses_ok(band);
532 eng->
step_pot = std::make_shared<eonc::HostSurfacePotential>(
533 surface, user, eng->
epoch);
534 std::vector<Matter> path;
535 path.reserve(
static_cast<size_t>(band->
n_images));
536 for (int64_t i = 0; i < band->
n_images; ++i) {
538 fill_matter(img, band,
static_cast<long>(i), eng->
epoch);
539 path.push_back(std::move(img));
541 eng->
step_neb = std::make_unique<NudgedElasticBand>(
546 std::min(eng->
step_neb->path[0]->getPotentialEnergy(),
548 ->getPotentialEnergy());
550 eng->
step_objf = std::make_shared<NEBObjectiveFunction>(
561 for (int64_t i = 0; i < band->
n_images; ++i) {
562 fill_matter(*eng->
step_neb->path[
static_cast<size_t>(i)], band,
563 static_cast<long>(i), eng->
epoch);
565 eng->
step_neb->movedAfterForceCall =
true;
572 const bool ci_active =
575 convForce < ci_opt.trigger_force);
583 }
else if (convForce <= force_tol) {
603 for (int64_t i = 1; i + 1 < band->
n_images; ++i) {
605 *neb.
path[
static_cast<size_t>(i)],
606 static_cast<long>(band->
n_atoms));
618 }
catch (
const std::exception &) {
638 std::unique_ptr<EonRelaxEngine> owned(eng);
649 return "BAD_MAX_ITERATIONS";
653 return "MAX_UNCERTAINTY";
660 if (msg ==
"Unknown status") {
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
The optimizer class is used to serve as an abstract class for all optimizers, as well as to call an o...
int eon_relax_step(EonRelaxEngine *eng, eon_relax_band_t *band, eon_relax_surface_fn surface, void *user, eon_relax_outcome_t *out)
One band optimizer step (kind=NEB only).
int eon_relax_run(EonRelaxEngine *eng, eon_relax_band_t *band, eon_relax_surface_fn surface, void *user, eon_relax_outcome_t *out)
Run the bound engine to completion.
const char * eon_relax_status_name(eon_relax_kind_t kind, int status)
NULL if kind or status is unknown.
static int fail_out(EonRelaxEngine *eng, eon_relax_outcome_t *out, int rc, eon_relax_kind_t kind, uint64_t epoch)
int eon_relax_available(void)
EonRelaxEngine * eon_relax_create(const void *config, size_t config_len, char *errbuf, size_t errlen)
Create.
static int stamp_rc(EonRelaxEngine *eng, int rc)
static void clear_stepper(EonRelaxEngine *eng)
void eon_relax_destroy(EonRelaxEngine *eng)
uint64_t eon_relax_version_hash(void)
64-bit FNV-1a hash of eon_relax_version_hash_str().
int eon_relax_reset(EonRelaxEngine *eng)
Drop the stepper's band state and optimizer history; the next eon_relax_step reinitializes from the c...
int eon_relax_last_error(const EonRelaxEngine *eng)
Last named create/run/step/reset/set_epoch rc on this handle.
int eon_relax_set_surface_epoch(EonRelaxEngine *eng, uint64_t epoch)
const char * eon_relax_version_hash_str(void)
Immortal dest identity string.
int eon_relax_abi_version(void)
(major << 16) | minor.
int eon_relax_abi_stamp(eon_relax_version_t *out)
static constexpr std::string_view statusMessage(int status)
Human-readable message for a status code.
void bindPath(const std::vector< std::shared_ptr< Matter > > &path)
void setEpoch(std::uint64_t epoch)
unsigned long long surfaceEpoch() const noexcept override
Host-visible surface generation.
void setFixed(long int atom, int isFixed)
Broadcast a whole-atom flag onto all three axes.
const AtomMatrix & getPositions() const
void setPositions(const AtomMatrix &pos)
void setCell(const Matrix3d &newCell)
bool getPeriodic() const noexcept
void setPeriodic(bool periodic)
void setAtomicNrs(const VectorXi &atmnrs)
void resize(long int nAtoms)
void setMasses(const VectorXd &massesIn)
void setSurfaceEpoch(unsigned long long epoch)
Host surface generation.
int getIterationCount() const override
void updateForces(bool ci_active)
std::vector< std::shared_ptr< Matter > > path
double convergenceForce(void)
void setCIEnabled(bool enabled)
Abstract base of the structure optimizers.
const neb_options_t & neb_options() const
const optimizer_options_t & optimizer_options() const
Tier-2 C ABI: eOn as a dlopen relaxation engine.
#define EON_RELAX_KIND_IS_KNOWN(k)
#define EON_RELAX_ABI_MAJOR
#define EON_RELAX_ABI_MINOR
@ EON_RELAX_NEB_MAX_UNCERTAINTY
@ EON_RELAX_NEB_BAD_MAX_ITERATIONS
@ EON_RELAX_SURFACE_FATAL
@ EON_RELAX_NULL_ATOMIC_NRS
@ EON_RELAX_NULL_POSITIONS
@ EON_RELAX_UNKNOWN_STATUS
@ EON_RELAX_SADDLE_NIMAGES
@ EON_RELAX_BAND_TOO_SHORT
@ EON_RELAX_INVALID_PARAMETER
int(* eon_relax_surface_fn)(void *user, eon_relax_surface_request_t *req)
Return 0 on success, positive recoverable, negative fatal.
std::unique_ptr< Optimizer > mkOptim(std::shared_ptr< ObjectiveFunction > a_objf, OptType a_otype, const Parameters &a_params)
std::shared_ptr< eonc::HostSurfacePotential > step_pot
std::shared_ptr< NEBObjectiveFunction > step_objf
eon_relax_surface_fn step_surface
std::unique_ptr< NudgedElasticBand > step_neb
std::unique_ptr< Optimizer > step_opt
static gp_surrogate_options_t & gp_surrogate_options(Parameters &p)
static saddle_search_options_t & saddle_search_options(Parameters &p)
static main_options_t & main_options(Parameters &p)
static neb_options_t & neb_options(Parameters &p)
eon_relax_version_t version
const int32_t * atomic_nrs
eon_relax_version_t version
Version head carried by every wire struct (dlpack DLPackVersion).
Write hole for INI/JSON loaders and nanobind property setters.
struct eonc::neb_options_t::climbing_image_options_t climbing_image