33#if RGSADDLE_ABI_MINOR >= 17
35rgsaddle_status_t solidSurfaceCallback(
void *user,
36 rgsaddle_solid_surface_request_t *req) {
37 auto *self =
static_cast<XtsciBand *
>(user);
38 return static_cast<rgsaddle_status_t
>(self ==
nullptr
39 ? RGSADDLE_SURFACE_FAILED
45void cellToRowMajor(
const Matrix3d &cell,
double *out) {
46 for (
int r = 0; r < 3; ++r) {
47 for (
int c = 0; c < 3; ++c) {
48 out[3 * r + c] = cell(r, c);
53Matrix3d cellFromRowMajor(
const double *in) {
55 for (
int r = 0; r < 3; ++r) {
56 for (
int c = 0; c < 3; ++c) {
57 cell(r, c) = in[3 * r + c];
64rgsaddle_status_t surfaceCallback(
void *user, rgsaddle_surface_request_t *req) {
65 auto *self =
static_cast<XtsciBand *
>(user);
66 return static_cast<rgsaddle_status_t
>(
70void checkStatus(
int rc,
const char *what) {
71 if (rc == RGSADDLE_OK) {
74 throw std::runtime_error(
75 std::string(what) +
": " +
76 rgsaddle_status_name(
static_cast<rgsaddle_status_t
>(rc)));
80 return neb.climbing_image.use_old_tangent ? RGSADDLE_TANGENT_SIMPLE
81 : RGSADDLE_TANGENT_IMPROVED;
85 if (
neb.spring.om.enabled) {
86 return RGSADDLE_SPRING_ONSAGER_MACHLUP;
88 if (
neb.spring.weighting.enabled) {
89 return RGSADDLE_SPRING_WEIGHTED;
91 return RGSADDLE_SPRING_UNIFORM;
95 if (
neb.spring.use_elastic_band) {
96 return RGSADDLE_PROJECTION_PLAIN_EB;
98 if (
neb.spring.doubly_nudged) {
99 return RGSADDLE_PROJECTION_DNEB;
101 return RGSADDLE_PROJECTION_NEB;
107 return RGSADDLE_METHOD_FIRE;
111 return RGSADDLE_METHOD_FIRE;
113 return RGSADDLE_METHOD_LBFGS;
120 pos->row(j) = ref.row(j);
136 auto *self =
static_cast<XtsciBand *
>(user);
137 auto *req =
static_cast<rgsaddle_surface_request_t *
>(request);
138 if (self ==
nullptr || req ==
nullptr || req->positions ==
nullptr ||
139 req->energies ==
nullptr || req->gradients ==
nullptr) {
140 return RGSADDLE_SURFACE_FAILED;
143 auto *
neb = self->m_neb;
144 const auto band =
static_cast<int64_t
>(
neb->numImages + 2);
145 const auto nAtoms =
static_cast<int64_t
>(
neb->atoms);
146 if (req->version.major != RGSADDLE_ABI_MAJOR) {
147 return RGSADDLE_ABI_MISMATCH;
149 if (req->n_atoms != nAtoms) {
150 return RGSADDLE_SHAPE;
158 if ((req->flags & RGSADDLE_REQ_ONE_IMAGE) != 0) {
159 if (req->n_images != band || req->image < 0 || req->image >= band) {
160 return RGSADDLE_SHAPE;
164 }
else if (req->n_images == band) {
166 }
else if (req->n_images == band - 2) {
170 return RGSADDLE_SHAPE;
172 const auto dof =
static_cast<Eigen::Index
>(3 * nAtoms);
173 std::vector<Matter *> carried;
174 std::vector<long> owners;
175 carried.reserve(
static_cast<size_t>(rows));
176 owners.reserve(
static_cast<size_t>(rows));
177 for (int64_t r = 0; r < rows; ++r) {
178 const auto i =
static_cast<size_t>(first + r);
179 carried.push_back(
neb->path[i].get());
180 owners.push_back(
neb->imageOwner(i));
185 auto place = [&](int64_t r) {
186 const auto i =
static_cast<size_t>(first + r);
187 AtomMatrix pos = AtomMatrix::Map(req->positions + r * dof, nAtoms, 3);
188 if (i < self->
m_fixed.size()) {
189 pinRows(self->m_fixed[i], *
neb->path[i], &pos);
191 moveTo(*
neb->path[i], pos);
193 auto report = [&](int64_t r) {
194 Matter &image = *carried[
static_cast<size_t>(r)];
196 const double *src = image.
getForces().data();
197 double *grad = req->gradients + r * dof;
198 for (Eigen::Index k = 0; k < dof; ++k) {
203 if (self->m_threads && rows > 1 && !
pot.supportsBatchEvaluation() &&
208 const auto r =
static_cast<int64_t
>(k - 1);
210 Matter &image = *carried[
static_cast<size_t>(r)];
217 for (int64_t r = 0; r < rows; ++r) {
224 for (int64_t r = 0; r < rows; ++r) {
230 return RGSADDLE_SURFACE_FAILED;
235 [[maybe_unused]]
void *request) {
236#if RGSADDLE_ABI_MINOR >= 17
237 auto *self =
static_cast<XtsciBand *
>(user);
238 auto *req =
static_cast<rgsaddle_solid_surface_request_t *
>(request);
239 if (self ==
nullptr || req ==
nullptr || req->positions ==
nullptr ||
240 req->cells ==
nullptr || req->energies ==
nullptr ||
241 req->gradients ==
nullptr || req->stresses ==
nullptr) {
242 return RGSADDLE_SURFACE_FAILED;
245 auto *
neb = self->m_neb;
246 const auto band =
static_cast<int64_t
>(
neb->numImages + 2);
247 const auto nAtoms =
static_cast<int64_t
>(
neb->atoms);
248 if (req->version.major != RGSADDLE_ABI_MAJOR) {
249 return RGSADDLE_ABI_MISMATCH;
251 if (req->n_atoms != nAtoms) {
252 return RGSADDLE_SHAPE;
256 if ((req->flags & RGSADDLE_REQ_ONE_IMAGE) != 0) {
257 if (req->n_images != band || req->image < 0 || req->image >= band) {
258 return RGSADDLE_SHAPE;
262 }
else if (req->n_images == band) {
264 }
else if (req->n_images == band - 2) {
268 return RGSADDLE_SHAPE;
270 const auto dof =
static_cast<Eigen::Index
>(3 * nAtoms);
271 for (int64_t r = 0; r < rows; ++r) {
272 const auto i =
static_cast<size_t>(first + r);
274 const Matrix3d cell = cellFromRowMajor(req->cells + 9 * r);
275 AtomMatrix pos = AtomMatrix::Map(req->positions + r * dof, nAtoms, 3);
276 if (i < self->
m_fixed.size()) {
277 pinRows(self->m_fixed[i], image, &pos);
287 double *grad = req->gradients + r * dof;
288 const double *src = force.data();
289 for (Eigen::Index k = 0; k < dof; ++k) {
292 cellToRowMajor(image.
cauchyStress(), req->stresses + 9 * r);
296 return RGSADDLE_SURFACE_FAILED;
299 return RGSADDLE_SURFACE_FAILED;
305 m_maxMove{params.optimizer_options().max_move},
306 m_threads{params.main_options().parallel} {
307 rgsaddle_version_t stamp{};
308 checkStatus(rgsaddle_abi_stamp(&stamp),
"rgsaddle_abi_stamp");
309 if (stamp.major != RGSADDLE_ABI_MAJOR) {
310 throw std::runtime_error(
"incompatible rgsaddle ABI");
313 const auto nImages =
static_cast<int64_t
>(
neb.numImages + 2);
314 const auto nAtoms =
static_cast<int64_t
>(
neb.atoms);
315 if (nImages < 3 || nAtoms < 1) {
316 throw std::runtime_error(
"rgsaddle band needs at least 3 images");
318 if (springKind(nebOpt) == RGSADDLE_SPRING_WEIGHTED) {
319 m_springKs.assign(
static_cast<size_t>(nImages - 1), nebOpt.spring.constant);
321 rgsaddle_band_config_t
config{};
322 config.version = RGSADDLE_VERSION_INIT;
327 if (!
neb.path.empty() &&
328 neb.path[0]->numberOfFreeAtoms() <
neb.path[0]->numberOfAtoms()) {
329 config.flags |= RGSADDLE_BAND_KEEP_TRANSLATION;
331 config.tangent = tangentKind(nebOpt);
332 config.spring = springKind(nebOpt);
333 config.projection = projectionKind(nebOpt);
334 config.method = methodKind(params);
335 config.spring_k = nebOpt.spring.om.enabled
336 ? nebOpt.spring.constant * nebOpt.spring.om.k_scale
337 : nebOpt.spring.constant;
339 if (nebOpt.climbing_image.enabled) {
340#if RGSADDLE_ABI_MINOR >= 17
342 config.flags |= RGSADDLE_BAND_CI_AFTER;
343 config.ci_trigger_factor = nebOpt.climbing_image.trigger_factor;
345 config.ci_trigger_factor = nebOpt.climbing_image.trigger_factor > 0.0
346 ? nebOpt.climbing_image.trigger_factor
349 config.ci_trigger_force = nebOpt.climbing_image.trigger_force;
351 config.ci_trigger_factor = 0.0;
352 config.ci_trigger_force = 0.0;
355 if (!
m_solid && !
neb.path.empty() &&
neb.path[0]->getPeriodic()) {
357 m_cell.assign(cell.data(), cell.data() + 9);
360 config.force_tol = nebOpt.force_tolerance;
364 std::vector<double> positions(
static_cast<size_t>(nImages * 3 * nAtoms), 0.0);
365 for (int64_t i = 0; i < nImages; ++i) {
367 std::copy(pos.data(), pos.data() + pos.size(),
368 positions.begin() +
static_cast<std::ptrdiff_t
>(i * pos.size()));
371#if RGSADDLE_ABI_MINOR >= 17
374 std::vector<double> cells(
static_cast<size_t>(nImages * 9), 0.0);
375 for (int64_t i = 0; i < nImages; ++i) {
376 cellToRowMajor(
neb.path[i]->getCell(), cells.data() + 9 * i);
378 const auto &solid = nebOpt.solid_state;
380 rgsaddle_band_create_solid(&
config, nImages, nAtoms, positions.data(),
381 cells.data(), solid.pressure, solid.weight);
383 throw std::runtime_error(
"rgsaddle_band_create_solid failed");
387 throw std::runtime_error(
388 "solid_state NEB on the rgsaddle band needs rgsaddle ABI minor 17 "
389 "or newer (built against minor " +
390 std::to_string(RGSADDLE_ABI_MINOR) +
391 "); the older band keeps every cell fixed. Use a newer rgsaddle or "
392 "an eOn optimizer for opt_method");
395 m_band = rgsaddle_band_create(&
config, nImages, nAtoms, positions.data());
397 throw std::runtime_error(
"rgsaddle_band_create failed");
406 [&](
long k) { work(
static_cast<int64_t
>(k - 1)); });
409 for (int64_t i = 0; i < n; ++i) {
415 const auto nImages =
static_cast<int64_t
>(
m_neb->numImages + 2);
416 const auto dof =
static_cast<std::ptrdiff_t
>(3 *
m_neb->atoms);
417 std::vector<double> positions(
static_cast<size_t>(nImages * dof), 0.0);
419 const AtomMatrix &pos =
m_neb->path[
static_cast<size_t>(i)]->getPositions();
420 std::copy(pos.data(), pos.data() + dof, positions.begin() + i * dof);
422 checkStatus(rgsaddle_band_set_positions(
m_band, positions.data()),
423 "rgsaddle_band_set_positions");
427#if RGSADDLE_ABI_MINOR >= 5
430 checkStatus(rgsaddle_band_restart(
m_band),
"rgsaddle_band_restart");
432 checkStatus(rgsaddle_band_reset(
m_band),
"rgsaddle_band_reset");
444 const auto nImages =
static_cast<int64_t
>(
m_neb->numImages + 2);
445 m_fixed.resize(
static_cast<size_t>(nImages));
447 m_fixed[
static_cast<size_t>(i)] =
448 m_neb->path[
static_cast<size_t>(i)]->getPositions();
453#if RGSADDLE_ABI_MINOR >= 5
461 rgsaddle_report_t report{};
462 const int rc = xts_band_step(
m_band, surfaceCallback,
this, &report);
463#if RGSADDLE_ABI_MINOR >= 17
466 if (rc == RGSADDLE_STALLED) {
470 checkStatus(rc,
"xts_band_step");
471 const auto dof =
static_cast<std::ptrdiff_t
>(
m_fixed[0].size());
472 std::vector<double> positions(
m_fixed.size() *
static_cast<size_t>(dof), 0.0);
473 checkStatus(rgsaddle_band_positions(
m_band, positions.data()),
474 "rgsaddle_band_positions");
477 m_neb->climbingImage = report.ci_index > 0 ? report.ci_index : 0;
478 const int64_t interior = nImages - 2;
479#if RGSADDLE_ABI_MINOR >= 5
484 std::vector<double> energies(
m_fixed.size());
485 std::vector<double> gradients(
m_fixed.size() *
static_cast<size_t>(dof));
486 const bool evaluated =
487 rgsaddle_band_evaluation(
m_band, energies.data(), gradients.data(),
488 nullptr) == RGSADDLE_OK;
489 std::vector<double> projected(
static_cast<size_t>(interior * dof));
490 const bool haveProjection =
491 rgsaddle_band_evaluation(
m_band,
nullptr,
nullptr, projected.data()) ==
498 const auto i =
static_cast<size_t>(k + 1);
501 AtomMatrix::Map(positions.data() +
static_cast<std::ptrdiff_t
>(i) * dof,
503 pinRows(
m_fixed[i], image, &pos);
505#if RGSADDLE_ABI_MINOR >= 5
508 gradients.data() +
static_cast<std::ptrdiff_t
>(i) * dof,
m_neb->atoms,
512 if (haveProjection) {
513 *
m_neb->projectedForce[i] = AtomMatrix::Map(
514 projected.data() +
static_cast<std::ptrdiff_t
>(k) * dof,
m_neb->atoms,
519#if RGSADDLE_ABI_MINOR >= 5
520 if (haveProjection) {
525 m_neb->adoptSessionProjection(report.ci_index);
526 m_neb->movedAfterForceCall =
false;
530 m_neb->movedAfterForceCall =
true;
534#if RGSADDLE_ABI_MINOR >= 17
535 const auto nImages =
static_cast<int64_t
>(
m_neb->numImages + 2);
536 const auto nAtoms =
static_cast<int64_t
>(
m_neb->atoms);
537 m_fixed.resize(
static_cast<size_t>(nImages));
538 for (int64_t i = 0; i < nImages; ++i) {
539 m_fixed[
static_cast<size_t>(i)] =
m_neb->path[i]->getPositions();
541 rgsaddle_report_t report{};
543 rgsaddle_band_step_solid(
m_band, solidSurfaceCallback,
this, &report);
544 if (rc == RGSADDLE_STALLED) {
547 checkStatus(rc,
"rgsaddle_band_step_solid");
548 const auto dof =
static_cast<std::ptrdiff_t
>(3 * nAtoms);
549 std::vector<double> positions(
static_cast<size_t>(nImages * dof));
550 std::vector<double> cells(
static_cast<size_t>(nImages * 9));
551 checkStatus(rgsaddle_band_positions(
m_band, positions.data()),
552 "rgsaddle_band_positions");
553 checkStatus(rgsaddle_band_cells(
m_band, cells.data()),
"rgsaddle_band_cells");
554 for (int64_t i = 1; i + 1 < nImages; ++i) {
555 Matter &image = *
m_neb->path[
static_cast<size_t>(i)];
557 AtomMatrix::Map(positions.data() + i * dof,
m_neb->atoms, 3);
558 pinRows(
m_fixed[
static_cast<size_t>(i)], image, &pos);
559 const Matrix3d cell = cellFromRowMajor(cells.data() + 9 * i);
565 if (report.ci_index >= 0) {
566 m_neb->climbingImage = report.ci_index;
570 m_neb->movedAfterForceCall =
true;
575 const auto nImages =
static_cast<int64_t
>(
m_neb->numImages + 2);
576 const auto nAtoms =
static_cast<int64_t
>(
m_neb->atoms);
577 std::vector<double> positions(
static_cast<size_t>(nImages * 3 * nAtoms));
578 if (rgsaddle_band_positions(
m_band, positions.data()) != RGSADDLE_OK) {
581 const auto dof =
static_cast<std::ptrdiff_t
>(3 * nAtoms);
582 for (int64_t i = 0; i < nImages; ++i) {
583 const AtomMatrix &pos =
m_neb->path[
static_cast<size_t>(i)]->getPositions();
584 if (!std::equal(pos.data(), pos.data() + dof,
585 positions.begin() + i * dof)) {
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
const AtomMatrix & getPositions() const
void setPositions(const AtomMatrix &pos)
long int numberOfAtoms() const
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
const neb_options_t & neb_options() const
const optimizer_options_t & optimizer_options() const
void setCell(const Matrix3d &newCell)
Matrix3d cauchyStress()
Cauchy stress in eV/Angstrom^3.
const AtomMatrix & getForcesRaw() const
double getPotentialEnergy() const
bool needsForceUpdate() const
Whether forces need recomputation (positions or surface epoch changed).
const AtomMatrix & getForces() const
void setEvaluation(const AtomMatrix &forces, double energy)
Puts a known evaluation back after a position reset: the forces and energy of a geometry this object ...
const neb_options_t & neb_options() const
const optimizer_options_t & optimizer_options() const
bool sessionMatchesPath() const
NudgedElasticBand * m_neb
void reset()
Drop stepper history after a host reparameterization.
void forImages(int64_t n, Work &&work)
std::vector< double > m_cell
XtsciBand(NudgedElasticBand &neb, const Parameters ¶ms)
std::vector< AtomMatrix > m_fixed
void step(double maxMove)
Assemble the band force and take one stepper step.
void syncFromPath()
Copy the current path into the session.
std::vector< double > m_springKs
RAII resource manager for the ARTn C library with global synchronization.
int xtsciSolidBandSurface(void *user, void *request)
void forEachImage(long n, Work &&work)
int xtsciBandSurface(void *user, void *request)
void evaluateTogether(Potential &pot, std::span< Matter *const > systems, bool threads, std::span< const long > owners)
Evaluates every system that needs a force update.
bool canComputeConcurrently(std::span< Matter *const > systems)
True when the systems can compute on separate threads at once: every potential instance that two of t...
struct eonc::optimizer_options_t::xtsci_t xtsci
struct eonc::optimizer_options_t::lbfgs_t lbfgs