Loading...
Searching...
No Matches
XtsciBand.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/XtsciBand.h"
13
15
16#include "ForEachImage.h"
17
18#include <rgsaddle.h>
19
20#include <algorithm>
21#include <cstddef>
22#include <stdexcept>
23#include <string>
24#include <vector>
25
26namespace eonc {
27
28int xtsciBandSurface(void *user, void *request);
29int xtsciSolidBandSurface(void *user, void *request);
30
31namespace {
32
33#if RGSADDLE_ABI_MINOR >= 17
34// Forward from the C callback to the friend that reads the band.
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
40 : xtsciSolidBandSurface(self, req));
41}
42
43// rgsaddle cells are row-major with rows the lattice vectors, as eOn's
44// cell rows are; Eigen stores a Matrix3d column-major.
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);
49 }
50 }
51}
52
53Matrix3d cellFromRowMajor(const double *in) {
54 Matrix3d cell;
55 for (int r = 0; r < 3; ++r) {
56 for (int c = 0; c < 3; ++c) {
57 cell(r, c) = in[3 * r + c];
58 }
59 }
60 return cell;
61}
62#endif
63
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>(
67 self == nullptr ? RGSADDLE_SURFACE_FAILED : xtsciBandSurface(self, req));
68}
69
70void checkStatus(int rc, const char *what) {
71 if (rc == RGSADDLE_OK) {
72 return;
73 }
74 throw std::runtime_error(
75 std::string(what) + ": " +
76 rgsaddle_status_name(static_cast<rgsaddle_status_t>(rc)));
77}
78
79int32_t tangentKind(const neb_options_t &neb) {
80 return neb.climbing_image.use_old_tangent ? RGSADDLE_TANGENT_SIMPLE
81 : RGSADDLE_TANGENT_IMPROVED;
82}
83
84int32_t springKind(const neb_options_t &neb) {
85 if (neb.spring.om.enabled) {
86 return RGSADDLE_SPRING_ONSAGER_MACHLUP;
87 }
88 if (neb.spring.weighting.enabled) {
89 return RGSADDLE_SPRING_WEIGHTED;
90 }
91 return RGSADDLE_SPRING_UNIFORM;
92}
93
94int32_t projectionKind(const neb_options_t &neb) {
95 if (neb.spring.use_elastic_band) {
96 return RGSADDLE_PROJECTION_PLAIN_EB;
97 }
98 if (neb.spring.doubly_nudged) {
99 return RGSADDLE_PROJECTION_DNEB;
100 }
101 return RGSADDLE_PROJECTION_NEB;
102}
103
104int32_t methodKind(const Parameters &params) {
105 const auto nebMethod = params.neb_options().opt_method;
106 if (nebMethod == OptType::FIRE) {
107 return RGSADDLE_METHOD_FIRE;
108 }
109 if (nebMethod == OptType::XTSCI &&
110 params.optimizer_options().xtsci.method != "lbfgs") {
111 return RGSADDLE_METHOD_FIRE;
112 }
113 return RGSADDLE_METHOD_LBFGS;
114}
115
116// Fixed atoms keep the coordinates they had before the step.
117void pinRows(const AtomMatrix &ref, const Matter &image, AtomMatrix *pos) {
118 for (int j = 0; j < image.numberOfAtoms(); ++j) {
119 if (image.getFixed(j)) {
120 pos->row(j) = ref.row(j);
121 }
122 }
123}
124
125// Moves an image only when the coordinates differ, so an image that
126// already holds this geometry keeps its evaluation.
127void moveTo(Matter &image, const AtomMatrix &pos) {
128 if (image.getPositions() != pos) {
129 image.setPositions(pos);
130 }
131}
132
133} // namespace
134
135int xtsciBandSurface(void *user, void *request) {
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;
141 }
142 try {
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;
148 }
149 if (req->n_atoms != nAtoms) {
150 return RGSADDLE_SHAPE;
151 }
152 // Rows carried by this request and the band image of row 0. The
153 // session sends the whole band on its first evaluation, the interior
154 // images 1 .. band - 2 afterwards, or one image with
155 // RGSADDLE_REQ_ONE_IMAGE.
156 int64_t rows = 0;
157 int64_t first = 0;
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;
161 }
162 rows = 1;
163 first = req->image;
164 } else if (req->n_images == band) {
165 rows = band;
166 } else if (req->n_images == band - 2) {
167 rows = band - 2;
168 first = 1;
169 } else {
170 return RGSADDLE_SHAPE;
171 }
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));
181 }
182 // Row r: the session's coordinates onto its image. The fixed endpoints
183 // and a start point the band already evaluated come back from the
184 // image's own cache.
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);
190 }
191 moveTo(*neb->path[i], pos);
192 };
193 auto report = [&](int64_t r) {
194 Matter &image = *carried[static_cast<size_t>(r)];
195 req->energies[r] = image.getPotentialEnergy();
196 const double *src = image.getForces().data();
197 double *grad = req->gradients + r * dof;
198 for (Eigen::Index k = 0; k < dof; ++k) {
199 grad[k] = -src[k];
200 }
201 };
202 Potential &pot = *carried.front()->getPotential();
203 if (self->m_threads && rows > 1 && !pot.supportsBatchEvaluation() &&
205 // One pass on the image pool: each row is placed, evaluated and
206 // read back on the thread that took it.
207 eonc::forEachImage(static_cast<long>(rows), [&](long k) {
208 const auto r = static_cast<int64_t>(k - 1);
209 place(r);
210 Matter &image = *carried[static_cast<size_t>(r)];
211 if (image.needsForceUpdate()) {
212 image.getForcesRaw();
213 }
214 report(r);
215 });
216 } else {
217 for (int64_t r = 0; r < rows; ++r) {
218 place(r);
219 }
220 // One batch for every carried image, so calculator groups or a
221 // batched model take them together; otherwise in turn. Each image
222 // carries its band identity, as in the classic update.
223 eonc::evaluateTogether(pot, carried, false, owners);
224 for (int64_t r = 0; r < rows; ++r) {
225 report(r);
226 }
227 }
228 return RGSADDLE_OK;
229 } catch (...) {
230 return RGSADDLE_SURFACE_FAILED;
231 }
232}
233
234int xtsciSolidBandSurface([[maybe_unused]] void *user,
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;
243 }
244 try {
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;
250 }
251 if (req->n_atoms != nAtoms) {
252 return RGSADDLE_SHAPE;
253 }
254 int64_t rows = 0;
255 int64_t first = 0;
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;
259 }
260 rows = 1;
261 first = req->image;
262 } else if (req->n_images == band) {
263 rows = band;
264 } else if (req->n_images == band - 2) {
265 rows = band - 2;
266 first = 1;
267 } else {
268 return RGSADDLE_SHAPE;
269 }
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);
273 Matter &image = *neb->path[i];
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);
278 }
279 if (image.getCell() != cell) {
280 image.setCell(cell);
281 }
282 moveTo(image, pos);
283 // One image at a time: Matter keeps the stress that came with its
284 // own force call, so the stress read below costs no second call.
285 const AtomMatrix &force = image.getForces();
286 req->energies[r] = image.getPotentialEnergy();
287 double *grad = req->gradients + r * dof;
288 const double *src = force.data();
289 for (Eigen::Index k = 0; k < dof; ++k) {
290 grad[k] = -src[k];
291 }
292 cellToRowMajor(image.cauchyStress(), req->stresses + 9 * r);
293 }
294 return RGSADDLE_OK;
295 } catch (...) {
296 return RGSADDLE_SURFACE_FAILED;
297 }
298#else
299 return RGSADDLE_SURFACE_FAILED;
300#endif
301}
302
304 : m_neb{&neb},
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");
311 }
312 const auto &nebOpt = params.neb_options();
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");
317 }
318 if (springKind(nebOpt) == RGSADDLE_SPRING_WEIGHTED) {
319 m_springKs.assign(static_cast<size_t>(nImages - 1), nebOpt.spring.constant);
320 }
321 rgsaddle_band_config_t config{};
322 config.version = RGSADDLE_VERSION_INIT;
323 // eOn removes the mean force of an image only when every atom is free
324 // (eonc::neb::zeroTranslation). With a fixed atom the mean stays, so the
325 // session never pushes fixed atoms that the host pins back after the step.
326 config.flags = 0;
327 if (!neb.path.empty() &&
328 neb.path[0]->numberOfFreeAtoms() < neb.path[0]->numberOfAtoms()) {
329 config.flags |= RGSADDLE_BAND_KEEP_TRANSLATION;
330 }
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;
338 config.spring_ks = m_springKs.empty() ? nullptr : m_springKs.data();
339 if (nebOpt.climbing_image.enabled) {
340#if RGSADDLE_ABI_MINOR >= 17
341 // eOn's ci_after and ci_after_rel as given; either may be 0.
342 config.flags |= RGSADDLE_BAND_CI_AFTER;
343 config.ci_trigger_factor = nebOpt.climbing_image.trigger_factor;
344#else
345 config.ci_trigger_factor = nebOpt.climbing_image.trigger_factor > 0.0
346 ? nebOpt.climbing_image.trigger_factor
347 : 1.0e-300;
348#endif
349 config.ci_trigger_force = nebOpt.climbing_image.trigger_force;
350 } else {
351 config.ci_trigger_factor = 0.0;
352 config.ci_trigger_force = 0.0;
353 }
354 m_solid = neb.solidState();
355 if (!m_solid && !neb.path.empty() && neb.path[0]->getPeriodic()) {
356 const Matrix3d cell = neb.path[0]->getCell();
357 m_cell.assign(cell.data(), cell.data() + 9);
358 config.cell = m_cell.data();
359 }
360 config.force_tol = nebOpt.force_tolerance;
361 config.max_move = m_maxMove > 0.0 ? m_maxMove : 0.2;
362 config.memory = std::max<long>(1, params.optimizer_options().lbfgs.memory);
363
364 std::vector<double> positions(static_cast<size_t>(nImages * 3 * nAtoms), 0.0);
365 for (int64_t i = 0; i < nImages; ++i) {
366 const AtomMatrix &pos = neb.path[i]->getPositions();
367 std::copy(pos.data(), pos.data() + pos.size(),
368 positions.begin() + static_cast<std::ptrdiff_t>(i * pos.size()));
369 }
370 if (m_solid) {
371#if RGSADDLE_ABI_MINOR >= 17
372 // Each image keeps its cell; prepareSolidState already put every cell
373 // in lower-triangular form, which rgsaddle requires.
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);
377 }
378 const auto &solid = nebOpt.solid_state;
379 m_band =
380 rgsaddle_band_create_solid(&config, nImages, nAtoms, positions.data(),
381 cells.data(), solid.pressure, solid.weight);
382 if (m_band == nullptr) {
383 throw std::runtime_error("rgsaddle_band_create_solid failed");
384 }
385 return;
386#else
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");
393#endif
394 }
395 m_band = rgsaddle_band_create(&config, nImages, nAtoms, positions.data());
396 if (m_band == nullptr) {
397 throw std::runtime_error("rgsaddle_band_create failed");
398 }
399}
400
401XtsciBand::~XtsciBand() { rgsaddle_band_free(m_band); }
402
403template <typename Work> void XtsciBand::forImages(int64_t n, Work &&work) {
404 if (m_threads && n > 1) {
405 eonc::forEachImage(static_cast<long>(n),
406 [&](long k) { work(static_cast<int64_t>(k - 1)); });
407 return;
408 }
409 for (int64_t i = 0; i < n; ++i) {
410 work(i);
411 }
412}
413
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);
418 forImages(nImages, [&](int64_t i) {
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);
421 });
422 checkStatus(rgsaddle_band_set_positions(m_band, positions.data()),
423 "rgsaddle_band_set_positions");
424}
425
427#if RGSADDLE_ABI_MINOR >= 5
428 // Same surface: drop the optimizer history and climbing state, keep the
429 // endpoint energies and the last evaluation.
430 checkStatus(rgsaddle_band_restart(m_band), "rgsaddle_band_restart");
431#else
432 checkStatus(rgsaddle_band_reset(m_band), "rgsaddle_band_reset");
433#endif
434}
435
436void XtsciBand::step(double maxMove) {
437 if (maxMove > 0.0) {
438 m_maxMove = maxMove;
439 }
440 if (m_solid) {
441 stepSolid();
442 return;
443 }
444 const auto nImages = static_cast<int64_t>(m_neb->numImages + 2);
445 m_fixed.resize(static_cast<size_t>(nImages));
446 forImages(nImages, [&](int64_t i) {
447 m_fixed[static_cast<size_t>(i)] =
448 m_neb->path[static_cast<size_t>(i)]->getPositions();
449 });
450 // The session holds the band of the last step. Resending it drops the
451 // session's cached endpoint and start-point evaluations, so only a band
452 // moved outside the session (a reparameterization) is resent.
453#if RGSADDLE_ABI_MINOR >= 5
454 // Unchanged rows cost nothing: the session keeps its cached values.
455 syncFromPath();
456#else
457 if (!sessionMatchesPath()) {
458 syncFromPath();
459 }
460#endif
461 rgsaddle_report_t report{};
462 const int rc = xts_band_step(m_band, surfaceCallback, this, &report);
463#if RGSADDLE_ABI_MINOR >= 17
464 // A stalled step leaves the band as it was. The NEB loop then runs into
465 // max_iterations, as it does when a host optimizer stops making progress.
466 if (rc == RGSADDLE_STALLED) {
467 return;
468 }
469#endif
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");
475 // The session reports -1 until an image climbs; the host then holds no
476 // climbing image either, so its convergence test reads the whole band.
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
480 // The session's evaluation of the accepted band and its projected force.
481 // An image whose last evaluation was elsewhere (the solver backed off to
482 // a point it evaluated earlier) takes that evaluation instead of a fresh
483 // force call, and the host convergence check reads the session's force.
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()) ==
492 RGSADDLE_OK;
493#endif
494 // Endpoints are fixed by the session. Each interior image takes the
495 // accepted point; interior fixed atoms stay put. The accepted point is
496 // usually the band's last evaluation, which the image still holds.
497 forImages(interior, [&](int64_t k) {
498 const auto i = static_cast<size_t>(k + 1);
499 Matter &image = *m_neb->path[i];
500 AtomMatrix pos =
501 AtomMatrix::Map(positions.data() + static_cast<std::ptrdiff_t>(i) * dof,
502 m_neb->atoms, 3);
503 pinRows(m_fixed[i], image, &pos);
504 moveTo(image, pos);
505#if RGSADDLE_ABI_MINOR >= 5
506 if (evaluated && image.needsForceUpdate()) {
507 const AtomMatrix grad = AtomMatrix::Map(
508 gradients.data() + static_cast<std::ptrdiff_t>(i) * dof, m_neb->atoms,
509 3);
510 image.setEvaluation(-grad, energies[i]);
511 }
512 if (haveProjection) {
513 *m_neb->projectedForce[i] = AtomMatrix::Map(
514 projected.data() + static_cast<std::ptrdiff_t>(k) * dof, m_neb->atoms,
515 3);
516 }
517#endif
518 });
519#if RGSADDLE_ABI_MINOR >= 5
520 if (haveProjection) {
521 // The session already projected the force. The host convergence
522 // check reads that force and does not rebuild it, so the highest
523 // image (sp.con), the climbing image and the tangents (neb.dat) are
524 // refreshed here from the accepted band.
525 m_neb->adoptSessionProjection(report.ci_index);
526 m_neb->movedAfterForceCall = false;
527 return;
528 }
529#endif
530 m_neb->movedAfterForceCall = true;
531}
532
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();
540 }
541 rgsaddle_report_t report{};
542 const int rc =
543 rgsaddle_band_step_solid(m_band, solidSurfaceCallback, this, &report);
544 if (rc == RGSADDLE_STALLED) {
545 return;
546 }
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)];
556 AtomMatrix pos =
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);
560 if (image.getCell() != cell) {
561 image.setCell(cell);
562 }
563 moveTo(image, pos);
564 }
565 if (report.ci_index >= 0) {
566 m_neb->climbingImage = report.ci_index;
567 }
568 // eOn's convergence force reads its own joint projection of the moved
569 // band (projectedForce and projectedCellForce); rebuild it there.
570 m_neb->movedAfterForceCall = true;
571#endif
572}
573
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) {
579 return false;
580 }
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)) {
586 return false;
587 }
588 }
589 return true;
590}
591
592} // namespace eonc
Eigen::Matrix< double, 3, 3, eOnStorageOrder > Matrix3d
Definition Eigen.h:35
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
const AtomMatrix & getPositions() const
Definition Matter.cpp:375
void setPositions(const AtomMatrix &pos)
Definition Matter.cpp:417
long int numberOfAtoms() const
Definition Matter.cpp:340
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
Definition Matter.cpp:572
const neb_options_t & neb_options() const
const optimizer_options_t & optimizer_options() const
void setCell(const Matrix3d &newCell)
Definition Matter.cpp:344
Matrix3d getCell() const
Definition Matter.cpp:342
Matrix3d cauchyStress()
Cauchy stress in eV/Angstrom^3.
Definition Matter.cpp:917
const AtomMatrix & getForcesRaw() const
Definition Matter.cpp:490
double getPotentialEnergy() const
Definition Matter.cpp:621
bool needsForceUpdate() const
Whether forces need recomputation (positions or surface epoch changed).
Definition Matter.h:220
const AtomMatrix & getForces() const
Definition Matter.cpp:479
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 ...
Definition Matter.h:238
const neb_options_t & neb_options() const
const optimizer_options_t & optimizer_options() const
One rgsaddle band step.
Definition XtsciBand.h:28
bool sessionMatchesPath() const
NudgedElasticBand * m_neb
Definition XtsciBand.h:53
void reset()
Drop stepper history after a host reparameterization.
RgsaddleBand * m_band
Definition XtsciBand.h:54
void forImages(int64_t n, Work &&work)
std::vector< double > m_cell
Definition XtsciBand.h:56
XtsciBand(NudgedElasticBand &neb, const Parameters &params)
std::vector< AtomMatrix > m_fixed
Definition XtsciBand.h:57
double m_maxMove
Definition XtsciBand.h:58
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
Definition XtsciBand.h:55
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.
Definition Matter.cpp:995
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...
Definition Matter.cpp:978
struct eonc::optimizer_options_t::xtsci_t xtsci
struct eonc::optimizer_options_t::lbfgs_t lbfgs