Loading...
Searching...
No Matches
eon_relax_engine.h File Reference

Tier-2 C ABI: eOn as a dlopen relaxation engine. More...

#include <stddef.h>
#include <stdint.h>

Go to the source code of this file.

Classes

struct  eon_relax_version_t
 Version head carried by every wire struct (dlpack DLPackVersion). More...
struct  eon_relax_band_t
 Caller-owned band. More...
struct  eon_relax_outcome_t
 Engine-written result. More...
struct  eon_relax_surface_request_t
 Host-surface request: one struct, engine-stamped version, so the callback contract grows behind the version head instead of by new symbols. More...

Macros

#define EON_RELAX_API   __attribute__((visibility("default")))
#define EON_RELAX_ABI_MAJOR   2u
#define EON_RELAX_ABI_MINOR   1u
#define EON_RELAX_VERSION_INIT   {EON_RELAX_ABI_MAJOR, EON_RELAX_ABI_MINOR}
#define EON_RELAX_KIND_IS_KNOWN(k)
#define EON_RELAX_IMAGE_NONE   INT64_C(-1)

Typedefs

typedef struct EonRelaxEngine EonRelaxEngine
typedef int(* eon_relax_surface_fn) (void *user, eon_relax_surface_request_t *req)
 Return 0 on success, positive recoverable, negative fatal.

Enumerations

enum  eon_relax_kind_t { EON_RELAX_KIND_INVALID = 0 , EON_RELAX_KIND_NEB = 1 , EON_RELAX_KIND_SADDLE = 2 }
enum  eon_relax_rc_t {
  EON_RELAX_OK = 0 , EON_RELAX_NULL_ENGINE = -1 , EON_RELAX_NULL_BAND = -2 , EON_RELAX_NULL_SURFACE = -3 ,
  EON_RELAX_NULL_OUTCOME = -4 , EON_RELAX_NULL_POSITIONS = -5 , EON_RELAX_NULL_ATOMIC_NRS = -6 , EON_RELAX_NULL_BOXES = -7 ,
  EON_RELAX_NULL_MODE = -8 , EON_RELAX_BAND_TOO_SHORT = -9 , EON_RELAX_NATOMS = -10 , EON_RELAX_UNKNOWN_KIND = -11 ,
  EON_RELAX_UNKNOWN_STATUS = -12 , EON_RELAX_CAPNP_ROOT = -13 , EON_RELAX_SURFACE_FATAL = -15 , EON_RELAX_SADDLE_NIMAGES = -17 ,
  EON_RELAX_BAND_SIZE = -18 , EON_RELAX_ALLOC = -19 , EON_RELAX_UNAVAILABLE = -21 , EON_RELAX_INVALID_PARAMETER = -22 ,
  EON_RELAX_ABI_MISMATCH = -23
}
 Call rc: 0 success (read outcome), positive recoverable, negative a named fail. More...
enum  eon_relax_neb_status_t {
  EON_RELAX_NEB_GOOD = 0 , EON_RELAX_NEB_INIT = 1 , EON_RELAX_NEB_BAD_MAX_ITERATIONS = 2 , EON_RELAX_NEB_RUNNING = 3 ,
  EON_RELAX_NEB_MAX_UNCERTAINTY = 4
}
 NudgedElasticBand::NEBStatus integers. More...

Functions

EON_RELAX_API int eon_relax_abi_version (void)
 (major << 16) | minor.
EON_RELAX_API int eon_relax_available (void)
EON_RELAX_API int eon_relax_abi_stamp (eon_relax_version_t *out)
EON_RELAX_API const char * eon_relax_version_hash_str (void)
 Immortal dest identity string.
EON_RELAX_API uint64_t eon_relax_version_hash (void)
 64-bit FNV-1a hash of eon_relax_version_hash_str().
EON_RELAX_API EonRelaxEngine * eon_relax_create (const void *config, size_t config_len, char *errbuf, size_t errlen)
 Create.
EON_RELAX_API int eon_relax_set_surface_epoch (EonRelaxEngine *eng, uint64_t epoch)
EON_RELAX_API int eon_relax_last_error (const EonRelaxEngine *eng)
 Last named create/run/step/reset/set_epoch rc on this handle.
EON_RELAX_API 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.
EON_RELAX_API 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).
EON_RELAX_API int eon_relax_reset (EonRelaxEngine *eng)
 Drop the stepper's band state and optimizer history; the next eon_relax_step reinitializes from the caller's band.
EON_RELAX_API void eon_relax_destroy (EonRelaxEngine *eng)
EON_RELAX_API const char * eon_relax_status_name (eon_relax_kind_t kind, int status)
 NULL if kind or status is unknown.

Detailed Description

Tier-2 C ABI: eOn as a dlopen relaxation engine.

Reverse of include/eon/potentials/Rgpot/engine_c_abi.h: the host supplies the surface (energy/forces/variance for a band), eOn owns NudgedElasticBand / MinModeSaddleSearch. Configuration is a Cap'n Proto flat-array whose root is RelaxEngineParams in schema/eon_relax_engine.capnp (NebParams / SaddleParams arms). NULL config selects Parameters NSDMI defaults and kind=NEB.

Units: positions Angstrom, energy eV, forces eV/Angstrom.

Plugin Matters use the Matter default (PBC on for a non-isolated pot). fill and endpoint dirty write positions with PBC off so the caller frame is kept (a 10 A box must not map x=-0.3 to 9.7).

Layout discipline (dlpack.h is the model):

  • Every wire struct opens with an eon_relax_version_t and a uint64 flags word. The writer stamps the version it was built against; the reader refuses a major it does not know (EON_RELAX_ABI_MISMATCH). Growth is append-only behind the version head, gated on minor.
  • Fixed-width integer types only. Never long in a wire struct.
  • The surface callback takes ONE request struct, so the callback contract can grow without a new function symbol.
  • Return values are three-valued: 0 success (read the outcome), positive recoverable, negative a named fail. SURFACE_FATAL makes the instance unusable. Every other named fail leaves the handle reusable.
  • Closed enums. Unknown kind is EON_RELAX_UNKNOWN_KIND; other unknown enumerants are EON_RELAX_INVALID_PARAMETER. Never a silent fallback.
  • Optional symbols are discovered by dlsym; absence means the operation is unsupported.

Definition in file eon_relax_engine.h.

Macro Definition Documentation

◆ EON_RELAX_ABI_MAJOR

#define EON_RELAX_ABI_MAJOR   2u

Definition at line 72 of file eon_relax_engine.h.

◆ EON_RELAX_ABI_MINOR

#define EON_RELAX_ABI_MINOR   1u

Definition at line 73 of file eon_relax_engine.h.

◆ EON_RELAX_API

#define EON_RELAX_API   __attribute__((visibility("default")))

Definition at line 67 of file eon_relax_engine.h.

◆ EON_RELAX_IMAGE_NONE

#define EON_RELAX_IMAGE_NONE   INT64_C(-1)

Definition at line 128 of file eon_relax_engine.h.

◆ EON_RELAX_KIND_IS_KNOWN

#define EON_RELAX_KIND_IS_KNOWN ( k)
Value:
@ EON_RELAX_KIND_SADDLE
@ EON_RELAX_KIND_NEB

Definition at line 89 of file eon_relax_engine.h.

89#define EON_RELAX_KIND_IS_KNOWN(k) \
90 ((k) == EON_RELAX_KIND_NEB || (k) == EON_RELAX_KIND_SADDLE)

◆ EON_RELAX_VERSION_INIT

#define EON_RELAX_VERSION_INIT   {EON_RELAX_ABI_MAJOR, EON_RELAX_ABI_MINOR}

Definition at line 81 of file eon_relax_engine.h.

Typedef Documentation

◆ eon_relax_surface_fn

typedef int(* eon_relax_surface_fn) (void *user, eon_relax_surface_request_t *req)

Return 0 on success, positive recoverable, negative fatal.

Definition at line 198 of file eon_relax_engine.h.

◆ EonRelaxEngine

typedef struct EonRelaxEngine EonRelaxEngine

Definition at line 70 of file eon_relax_engine.h.

Enumeration Type Documentation

◆ eon_relax_kind_t

Enumerator
EON_RELAX_KIND_INVALID 
EON_RELAX_KIND_NEB 
EON_RELAX_KIND_SADDLE 

Definition at line 83 of file eon_relax_engine.h.

◆ eon_relax_neb_status_t

NudgedElasticBand::NEBStatus integers.

Written to outcome.status for NEB.

Enumerator
EON_RELAX_NEB_GOOD 
EON_RELAX_NEB_INIT 
EON_RELAX_NEB_BAD_MAX_ITERATIONS 
EON_RELAX_NEB_RUNNING 
EON_RELAX_NEB_MAX_UNCERTAINTY 

Definition at line 120 of file eon_relax_engine.h.

120 {
eon_relax_neb_status_t
NudgedElasticBand::NEBStatus integers.
@ EON_RELAX_NEB_GOOD
@ EON_RELAX_NEB_MAX_UNCERTAINTY
@ EON_RELAX_NEB_BAD_MAX_ITERATIONS
@ EON_RELAX_NEB_RUNNING
@ EON_RELAX_NEB_INIT

◆ eon_relax_rc_t

Call rc: 0 success (read outcome), positive recoverable, negative a named fail.

SURFACE_FATAL makes the instance unusable. Every other named fail leaves the handle reusable.

Enumerator
EON_RELAX_OK 
EON_RELAX_NULL_ENGINE 
EON_RELAX_NULL_BAND 
EON_RELAX_NULL_SURFACE 
EON_RELAX_NULL_OUTCOME 
EON_RELAX_NULL_POSITIONS 
EON_RELAX_NULL_ATOMIC_NRS 
EON_RELAX_NULL_BOXES 
EON_RELAX_NULL_MODE 
EON_RELAX_BAND_TOO_SHORT 
EON_RELAX_NATOMS 
EON_RELAX_UNKNOWN_KIND 
EON_RELAX_UNKNOWN_STATUS 
EON_RELAX_CAPNP_ROOT 
EON_RELAX_SURFACE_FATAL 
EON_RELAX_SADDLE_NIMAGES 
EON_RELAX_BAND_SIZE 
EON_RELAX_ALLOC 
EON_RELAX_UNAVAILABLE 
EON_RELAX_INVALID_PARAMETER 
EON_RELAX_ABI_MISMATCH 

Definition at line 95 of file eon_relax_engine.h.

95 {
96 EON_RELAX_OK = 0,
106 EON_RELAX_NATOMS = -10,
113 EON_RELAX_ALLOC = -19,
eon_relax_rc_t
Call rc: 0 success (read outcome), positive recoverable, negative a named fail.
@ EON_RELAX_NULL_BAND
@ EON_RELAX_ALLOC
@ EON_RELAX_OK
@ EON_RELAX_SURFACE_FATAL
@ EON_RELAX_NULL_ENGINE
@ EON_RELAX_BAND_SIZE
@ EON_RELAX_NULL_ATOMIC_NRS
@ EON_RELAX_NULL_POSITIONS
@ EON_RELAX_NULL_BOXES
@ EON_RELAX_NULL_SURFACE
@ EON_RELAX_UNKNOWN_STATUS
@ EON_RELAX_CAPNP_ROOT
@ EON_RELAX_ABI_MISMATCH
@ EON_RELAX_UNKNOWN_KIND
@ EON_RELAX_SADDLE_NIMAGES
@ EON_RELAX_BAND_TOO_SHORT
@ EON_RELAX_NULL_OUTCOME
@ EON_RELAX_INVALID_PARAMETER
@ EON_RELAX_NATOMS
@ EON_RELAX_UNAVAILABLE
@ EON_RELAX_NULL_MODE

Function Documentation

◆ eon_relax_abi_stamp()

EON_RELAX_API int eon_relax_abi_stamp ( eon_relax_version_t * out)

Definition at line 262 of file RelaxEngineAbi.cpp.

262 {
263 if (!out) {
265 }
268 return EON_RELAX_OK;
269}
#define EON_RELAX_ABI_MAJOR
#define EON_RELAX_ABI_MINOR

◆ eon_relax_abi_version()

EON_RELAX_API int eon_relax_abi_version ( void )

(major << 16) | minor.

Definition at line 256 of file RelaxEngineAbi.cpp.

256 {
257 return static_cast<int>((EON_RELAX_ABI_MAJOR << 16) | EON_RELAX_ABI_MINOR);
258}

◆ eon_relax_available()

EON_RELAX_API int eon_relax_available ( void )

Definition at line 260 of file RelaxEngineAbi.cpp.

260{ return 1; }

◆ eon_relax_create()

EON_RELAX_API EonRelaxEngine * eon_relax_create ( const void * config,
size_t config_len,
char * errbuf,
size_t errlen )

Create.

NULL config && config_len==0 selects Parameters NSDMI defaults and kind=NEB. A non-empty buffer is a Cap'n Proto flat-array whose root is RelaxEngineParams. kind tokens are "neb" and "saddle"; any other token fails closed (EON_RELAX_UNKNOWN_KIND). An unparseable buffer is EON_RELAX_CAPNP_ROOT. config!=NULL && config_len==0 is refused.

Definition at line 282 of file RelaxEngineAbi.cpp.

283 {
284 auto make_engine = [&]() -> std::unique_ptr<EonRelaxEngine> {
285 try {
286 return std::make_unique<EonRelaxEngine>();
287 } catch (const std::bad_alloc &) {
288 set_err(errbuf, errlen, "-19 out of memory");
289 return nullptr;
290 }
291 };
292 if (config == nullptr && config_len == 0) {
293 auto eng = make_engine();
294 if (!eng) {
295 return nullptr;
296 }
297 eng->kind = EON_RELAX_KIND_NEB;
298 return eng.release();
299 }
300 if (config == nullptr || config_len == 0) {
301 set_err(errbuf, errlen, "-13 RelaxEngineParams capnp root");
302 return nullptr;
303 }
304 auto eng = make_engine();
305 if (!eng) {
306 return nullptr;
307 }
308 const int prc = apply_relax_params(eng->params, &eng->kind, &eng->epoch,
309 config, config_len, errbuf, errlen);
310 if (prc != EON_RELAX_OK) {
311 return nullptr;
312 }
313 return eng.release();
314}
static void set_err(char *errbuf, size_t errlen, const char *msg)

◆ eon_relax_destroy()

EON_RELAX_API void eon_relax_destroy ( EonRelaxEngine * eng)

Definition at line 637 of file RelaxEngineAbi.cpp.

637 {
638 std::unique_ptr<EonRelaxEngine> owned(eng);
639}

◆ eon_relax_last_error()

EON_RELAX_API int eon_relax_last_error ( const EonRelaxEngine * eng)

Last named create/run/step/reset/set_epoch rc on this handle.

0 if none.

Definition at line 331 of file RelaxEngineAbi.cpp.

331 {
332 if (!eng) {
334 }
335 return eng->last_rc;
336}

◆ eon_relax_reset()

EON_RELAX_API int eon_relax_reset ( EonRelaxEngine * eng)

Drop the stepper's band state and optimizer history; the next eon_relax_step reinitializes from the caller's band.

This is the host's model-update hook: quasi-Newton memory taken on one surface epoch must not survive onto the next.

Definition at line 625 of file RelaxEngineAbi.cpp.

625 {
626 if (!eng) {
628 }
629 if (eng->unusable) {
631 }
632 clear_stepper(eng);
633 eng->last_rc = EON_RELAX_OK;
634 return EON_RELAX_OK;
635}
static int stamp_rc(EonRelaxEngine *eng, int rc)
static void clear_stepper(EonRelaxEngine *eng)

◆ eon_relax_run()

EON_RELAX_API 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.

Writes updated interior coordinates into band->positions (caller-owned); endpoints stay in the caller frame. Endpoint minimization stays with the host (NudgedElasticBand::compute does not minimize endpoints). surface may not be NULL. Kind is bound at create (NULL config => NEB). Returns eon_relax_rc_t; on 0, out->status is the dest NEB or saddle table.

Definition at line 338 of file RelaxEngineAbi.cpp.

340 {
341 if (!eng) {
343 }
344 if (eng->unusable) {
345 return fail_out(eng, out, EON_RELAX_SURFACE_FATAL, eng->kind, eng->epoch);
346 }
347 if (!band) {
348 return fail_out(eng, out, EON_RELAX_NULL_BAND, eng->kind, eng->epoch);
349 }
350 if (!surface) {
351 return fail_out(eng, out, EON_RELAX_NULL_SURFACE, eng->kind, eng->epoch);
352 }
353 if (!out) {
354 return stamp_rc(eng, EON_RELAX_NULL_OUTCOME);
355 }
356 clear_stepper(eng);
357 if (band->n_atoms <= 0) {
358 return fail_out(eng, out, EON_RELAX_NATOMS, eng->kind, eng->epoch);
359 }
360 if (!band->positions) {
361 return fail_out(eng, out, EON_RELAX_NULL_POSITIONS, eng->kind, eng->epoch);
362 }
363 if (!band->atomic_nrs) {
364 return fail_out(eng, out, EON_RELAX_NULL_ATOMIC_NRS, eng->kind, eng->epoch);
365 }
366 if (!band->boxes) {
367 return fail_out(eng, out, EON_RELAX_NULL_BOXES, eng->kind, eng->epoch);
368 }
369 if (band->version.major != EON_RELAX_ABI_MAJOR) {
370 return fail_out(eng, out, EON_RELAX_ABI_MISMATCH, eng->kind, eng->epoch);
371 }
372 if (!known_kind(eng->kind)) {
373 return fail_out(eng, out, EON_RELAX_UNKNOWN_KIND, eng->kind, eng->epoch);
374 }
375 const int mass_rc = masses_ok(band);
376 if (mass_rc != EON_RELAX_OK) {
377 return fail_out(eng, out, mass_rc, eng->kind, eng->epoch);
378 }
379 stamp_outcome(out, eng->kind, eng->epoch);
380
381 auto pot =
382 std::make_shared<eonc::HostSurfacePotential>(surface, user, eng->epoch);
383 try {
384 if (eng->kind == EON_RELAX_KIND_NEB) {
385 if (band->n_images < 3) {
386 out->status = -1;
388 }
389 const int64_t want =
390 static_cast<int64_t>(eng->params.neb_options().image_count) + 2;
391 if (band->n_images != want) {
392 out->status = -1;
393 return stamp_rc(eng, EON_RELAX_BAND_SIZE);
394 }
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) {
398 Matter img(pot, eng->params);
399 fill_matter(img, band, static_cast<long>(i), eng->epoch);
400 path.push_back(std::move(img));
401 }
402 auto neb = std::make_unique<NudgedElasticBand>(std::move(path),
403 eng->params, pot);
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);
411 }
412 out->status = neb_status(st);
413 NEBObjectiveFunction ranked(neb.get(), eng->params);
414 if (ranked.isUncertain()) {
416 }
417 out->iterations = neb->lastIteration();
418 out->climbing_image = neb->climbingImage;
419 out->max_force = neb->convergenceForce();
420 out->surface_epoch = pot->surfaceEpoch();
421 for (int64_t i = 1; i + 1 < band->n_images; ++i) {
422 write_image(band->positions + i * 3 * band->n_atoms,
423 *neb->path[static_cast<size_t>(i)],
424 static_cast<long>(band->n_atoms));
425 }
426 return stamp_rc(eng, EON_RELAX_OK);
427 }
428
429 if (band->n_images != 1) {
430 out->status = -1;
432 }
433 if (!band->mode) {
434 out->status = -1;
435 return stamp_rc(eng, EON_RELAX_NULL_MODE);
436 }
437 auto matter = std::make_shared<Matter>(pot, eng->params);
438 fill_matter(*matter, band, 0, eng->epoch);
439 AtomMatrix mode(band->n_atoms, 3);
440 Eigen::Map<const AtomMatrix> mmap(band->mode, band->n_atoms, 3);
441 mode = mmap;
442 const double reactant_e = matter->getPotentialEnergy();
443 MinModeSaddleSearch search(matter, mode, reactant_e, eng->params, pot);
444 const int sst = search.run();
445 write_image(band->positions, *matter, static_cast<long>(band->n_atoms));
446 out->iterations = search.getIterationCount();
447 out->surface_epoch = pot->surfaceEpoch();
448 out->status = sst;
449 return stamp_rc(eng, EON_RELAX_OK);
450 } catch (const eonc::SurfaceRecoverable &rec) {
451 out->status = -1;
452 return stamp_rc(eng, rec.rc);
453 } catch (const std::exception &) {
454 out->status = -1;
455 eng->unusable = true;
457 }
458}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
static int fail_out(EonRelaxEngine *eng, eon_relax_outcome_t *out, int rc, eon_relax_kind_t kind, uint64_t epoch)
const neb_options_t & neb_options() const
eon_relax_kind_t kind
eon_relax_version_t version
const int32_t * atomic_nrs
const double * boxes
const double * mode

◆ eon_relax_set_surface_epoch()

EON_RELAX_API int eon_relax_set_surface_epoch ( EonRelaxEngine * eng,
uint64_t epoch )

Definition at line 316 of file RelaxEngineAbi.cpp.

316 {
317 if (!eng) {
319 }
320 if (eng->unusable) {
322 }
323 eng->epoch = epoch;
324 if (eng->step_pot) {
325 eng->step_pot->setEpoch(epoch);
326 }
327 eng->last_rc = EON_RELAX_OK;
328 return EON_RELAX_OK;
329}
void setEpoch(std::uint64_t epoch)
std::shared_ptr< eonc::HostSurfacePotential > step_pot

◆ eon_relax_status_name()

EON_RELAX_API const char * eon_relax_status_name ( eon_relax_kind_t kind,
int status )

NULL if kind or status is unknown.

Definition at line 641 of file RelaxEngineAbi.cpp.

641 {
642 if (kind == EON_RELAX_KIND_NEB) {
643 switch (status) {
645 return "GOOD";
647 return "INIT";
649 return "BAD_MAX_ITERATIONS";
651 return "RUNNING";
653 return "MAX_UNCERTAINTY";
654 default:
655 return nullptr;
656 }
657 }
658 if (kind == EON_RELAX_KIND_SADDLE) {
659 const auto msg = MinModeSaddleSearch::statusMessage(status);
660 if (msg == "Unknown status") {
661 return nullptr;
662 }
663 return msg.data();
664 }
665 return nullptr;
666}
static constexpr std::string_view statusMessage(int status)
Human-readable message for a status code.

◆ eon_relax_step()

EON_RELAX_API 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).

The first call on a fresh or reset engine initializes the band state from the caller's positions and captures the baseline force; every call syncs band->positions in (the host may have moved images between steps), assembles NEB forces on the host surface, takes ONE optimizer step, and writes the stepped INTERIOR positions back (endpoints are the caller's and are never rewritten, so the caller's frame survives eOn's own cell wrapping). Climbing-image activation follows the engine's own trigger rule; saddle POLICY (MMF bursts, resampling, acquisition) stays with the host. outcome.status is RUNNING until convergenceForce() reaches the configured tolerance (GOOD), host variance exceeds the GP-surrogate threshold (MAX_UNCERTAINTY), or neb.max_iterations is hit (BAD_MAX_ITERATIONS). surface and user must not change across steps without an eon_relax_reset.

Definition at line 460 of file RelaxEngineAbi.cpp.

462 {
463 if (!eng) {
465 }
466 if (eng->unusable) {
467 return fail_out(eng, out, EON_RELAX_SURFACE_FATAL, eng->kind, eng->epoch);
468 }
469 if (!band) {
470 return fail_out(eng, out, EON_RELAX_NULL_BAND, eng->kind, eng->epoch);
471 }
472 if (!surface) {
473 return fail_out(eng, out, EON_RELAX_NULL_SURFACE, eng->kind, eng->epoch);
474 }
475 if (!out) {
476 return stamp_rc(eng, EON_RELAX_NULL_OUTCOME);
477 }
478 if (eng->kind == EON_RELAX_KIND_SADDLE) {
479 return fail_out(eng, out, EON_RELAX_INVALID_PARAMETER, eng->kind,
480 eng->epoch);
481 }
482 if (eng->kind != EON_RELAX_KIND_NEB) {
483 return fail_out(eng, out, EON_RELAX_UNKNOWN_KIND, eng->kind, eng->epoch);
484 }
485 if (band->n_atoms <= 0) {
486 return fail_out(eng, out, EON_RELAX_NATOMS, EON_RELAX_KIND_NEB, eng->epoch);
487 }
488 if (!band->positions) {
490 eng->epoch);
491 }
492 if (!band->atomic_nrs) {
494 eng->epoch);
495 }
496 if (!band->boxes) {
498 eng->epoch);
499 }
500 if (band->version.major != EON_RELAX_ABI_MAJOR) {
502 eng->epoch);
503 }
504 const int mass_rc = masses_ok(band);
505 if (mass_rc != EON_RELAX_OK) {
506 return fail_out(eng, out, mass_rc, EON_RELAX_KIND_NEB, eng->epoch);
507 }
508 stamp_outcome(out, EON_RELAX_KIND_NEB, eng->epoch);
509 if (eng->step_neb && (band->n_atoms != eng->step_neb->atoms ||
510 band->n_images != eng->step_neb->numImages + 2)) {
511 out->status = -1;
513 }
514 if (band->n_images < 3) {
515 out->status = -1;
517 }
518 const int64_t want =
519 static_cast<int64_t>(eng->params.neb_options().image_count) + 2;
520 if (band->n_images != want) {
521 out->status = -1;
522 return stamp_rc(eng, EON_RELAX_BAND_SIZE);
523 }
524 if (eng->step_neb &&
525 (eng->step_surface != surface || eng->step_user != user)) {
526 out->status = -1;
528 }
529
530 try {
531 if (!eng->step_neb) {
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) {
537 Matter img(eng->step_pot, eng->params);
538 fill_matter(img, band, static_cast<long>(i), eng->epoch);
539 path.push_back(std::move(img));
540 }
541 eng->step_neb = std::make_unique<NudgedElasticBand>(
542 std::move(path), eng->params, eng->step_pot);
543 eng->step_pot->bindPath(eng->step_neb->path);
544 dirty_endpoints(*eng->step_neb);
545 eng->step_neb->E_ref =
546 std::min(eng->step_neb->path[0]->getPotentialEnergy(),
547 eng->step_neb->path[eng->step_neb->numImages + 1]
548 ->getPotentialEnergy());
549 eng->step_neb->updateForces();
550 eng->step_objf = std::make_shared<NEBObjectiveFunction>(
551 eng->step_neb.get(), eng->params);
553 eng->step_objf, eng->params.neb_options().opt_method, eng->params);
554 eng->step_surface = surface;
555 eng->step_user = user;
556 eng->step_baseline = eng->step_neb->convergenceForce();
557 eng->step_iteration = 0;
558 } else {
559 // The host may have moved images between steps (acquisition,
560 // reparameterization); resync and mark the band dirty.
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);
564 }
565 eng->step_neb->movedAfterForceCall = true;
566 eng->step_neb->updateForces();
567 }
568
569 NudgedElasticBand &neb = *eng->step_neb;
570 const double convForce = neb.convergenceForce();
571 const auto &ci_opt = eng->params.neb_options().climbing_image;
572 const bool ci_active =
573 ci_opt.enabled &&
574 (convForce < eng->step_baseline * ci_opt.trigger_factor ||
575 convForce < ci_opt.trigger_force);
576 neb.setCIEnabled(ci_active);
577
578 const double force_tol = eng->params.neb_options().force_tolerance;
579 const long max_iter = eng->params.neb_options().max_iterations;
580 int status = EON_RELAX_NEB_RUNNING;
581 if (eng->step_objf && eng->step_objf->isUncertain()) {
583 } else if (convForce <= force_tol) {
584 status = EON_RELAX_NEB_GOOD;
585 } else if (eng->step_iteration >= max_iter) {
587 } else {
588 eng->step_opt->step(eng->params.optimizer_options().max_move);
589 ++eng->step_iteration;
590 neb.updateForces();
591 if (eng->step_objf && eng->step_objf->isUncertain()) {
593 } else if (neb.convergenceForce() <= force_tol) {
594 status = EON_RELAX_NEB_GOOD;
595 } else if (eng->step_iteration >= max_iter) {
597 }
598 }
599
600 // Interior images only: eOn never moves the endpoints, and Matter
601 // wraps positions into the cell, so writing them back would hand
602 // the caller a different frame than the one it supplied.
603 for (int64_t i = 1; i + 1 < band->n_images; ++i) {
604 write_image(band->positions + i * 3 * band->n_atoms,
605 *neb.path[static_cast<size_t>(i)],
606 static_cast<long>(band->n_atoms));
607 }
608 out->status = status;
609 out->iterations = eng->step_iteration;
610 out->climbing_image = neb.climbingImage;
611 out->max_force = neb.convergenceForce();
612 out->surface_epoch = eng->step_pot->surfaceEpoch();
613 return stamp_rc(eng, EON_RELAX_OK);
614 } catch (const eonc::SurfaceRecoverable &rec) {
615 out->status = -1;
616 clear_stepper(eng);
617 return stamp_rc(eng, rec.rc);
618 } catch (const std::exception &) {
619 out->status = -1;
620 eng->unusable = true;
622 }
623}
void bindPath(const std::vector< std::shared_ptr< Matter > > &path)
unsigned long long surfaceEpoch() const noexcept override
Host-visible surface generation.
void updateForces(bool ci_active)
std::vector< std::shared_ptr< Matter > > path
void setCIEnabled(bool enabled)
const optimizer_options_t & optimizer_options() const
std::unique_ptr< Optimizer > mkOptim(std::shared_ptr< ObjectiveFunction > a_objf, OptType a_otype, const Parameters &a_params)
Definition Optimizer.cpp:55
std::shared_ptr< NEBObjectiveFunction > step_objf
eon_relax_surface_fn step_surface
std::unique_ptr< NudgedElasticBand > step_neb
std::unique_ptr< Optimizer > step_opt
struct eonc::neb_options_t::climbing_image_options_t climbing_image

◆ eon_relax_version_hash()

EON_RELAX_API uint64_t eon_relax_version_hash ( void )

64-bit FNV-1a hash of eon_relax_version_hash_str().

Copied to outcome.version_hash.

Definition at line 278 of file RelaxEngineAbi.cpp.

278 {
279 return fnv1a64(eon_relax_version_hash_str());
280}
const char * eon_relax_version_hash_str(void)
Immortal dest identity string.

◆ eon_relax_version_hash_str()

EON_RELAX_API const char * eon_relax_version_hash_str ( void )

Immortal dest identity string.

Never NULL.

Definition at line 271 of file RelaxEngineAbi.cpp.

271 {
272 static const std::string id =
273 VERSION + "+git." + GIT_HASH_FULL + "+feat." +
274 std::to_string(fnv1a64(FEATURES_STRING.c_str()));
275 return id.c_str();
276}