Loading...
Searching...
No Matches
XtsciMinMode.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/XtsciMinMode.h"
13
15#include "eon/HelperFunctions.h"
16
17#include <rgsaddle.h>
18
19#include <stdexcept>
20#include <string>
21#include <vector>
22
23namespace eonc {
24namespace {
25
26struct MinModeUser {
27 // The host centre. It keeps its evaluation: a request at its own
28 // coordinates reads the cache, and displaced probes go to `probe`.
29 Matter *matter;
30 Matter *probe;
31 const AtomMatrix *fixed;
32};
33
34rgsaddle_status_t surfaceCallback(void *user, rgsaddle_surface_request_t *req) {
35 auto *ctx = static_cast<MinModeUser *>(user);
36 if (ctx == nullptr || ctx->matter == nullptr || ctx->probe == nullptr ||
37 req == nullptr || req->positions == nullptr || req->energies == nullptr ||
38 req->gradients == nullptr || req->n_images != 1) {
39 return RGSADDLE_SURFACE_FAILED;
40 }
41 try {
42 const auto nAtoms = ctx->matter->numberOfAtoms();
43 if (req->n_atoms != nAtoms) {
44 return RGSADDLE_SHAPE;
45 }
46 AtomMatrix pos = AtomMatrix::Map(req->positions, nAtoms, 3);
47 if (ctx->fixed != nullptr) {
48 for (int j = 0; j < nAtoms; ++j) {
49 if (ctx->matter->getFixed(j)) {
50 pos.row(j) = ctx->fixed->row(j);
51 }
52 }
53 }
54 Matter *at = ctx->matter;
55 if (at->getPositions() != pos) {
56 at = ctx->probe;
57 if (at->getPositions() != pos) {
58 at->setPositions(pos);
59 }
60 }
61 req->energies[0] = at->getPotentialEnergy();
62 const AtomMatrix &force = at->getForces();
63 const auto dof = static_cast<Eigen::Index>(force.size());
64 for (Eigen::Index k = 0; k < dof; ++k) {
65 req->gradients[k] = -force.data()[k];
66 }
67 return RGSADDLE_OK;
68 } catch (...) {
69 return RGSADDLE_SURFACE_FAILED;
70 }
71}
72
73void checkStatus(int rc, const char *what) {
74 if (rc == RGSADDLE_OK) {
75 return;
76 }
77 throw std::runtime_error(
78 std::string(what) + ": " +
79 rgsaddle_status_name(static_cast<rgsaddle_status_t>(rc)));
80}
81
82} // namespace
83
84XtsciMinMode::XtsciMinMode(std::shared_ptr<Matter> matter,
85 const Parameters &params,
86 std::shared_ptr<Potential> pot)
88 if (matter) {
89 m_eigenvector = AtomMatrix::Zero(matter->numberOfAtoms(), 3);
90 }
91}
92
93void XtsciMinMode::compute(std::shared_ptr<Matter> matter,
94 AtomMatrix initialDirection) {
95 if (!matter) {
96 throw std::runtime_error("xtsci min-mode requires a geometry");
97 }
98 rgsaddle_version_t stamp{};
99 checkStatus(rgsaddle_abi_stamp(&stamp), "rgsaddle_abi_stamp");
100 if (stamp.major != RGSADDLE_ABI_MAJOR) {
101 throw std::runtime_error("incompatible rgsaddle ABI");
102 }
103 const auto nAtoms = matter->numberOfAtoms();
104 if (initialDirection.rows() != nAtoms || initialDirection.cols() != 3) {
105 throw std::runtime_error("xtsci min-mode direction shape");
106 }
107 // A direction without a norm names no mode. The host dimer normalizes it
108 // to zero and reports a zero curvature along it; rgsaddle refuses such a
109 // mode, so the estimate is the same here without a session call.
110 if (!(initialDirection.norm() > 1.0e-14)) {
111 m_eigenvector = AtomMatrix::Zero(nAtoms, 3);
112 m_eigenvalue = 0.0;
113 statsCurvature = 0.0;
114 statsRotations = 0;
115 totalIterations += 1;
116 return;
117 }
118 const AtomMatrix saved = matter->getPositions();
119 m_fixed = saved;
120 const auto &dim = params.dimer_options();
121 rgsaddle_minmode_config_t config{};
122 config.version = RGSADDLE_VERSION_INIT;
123 config.flags = 0;
124 // Dimer rotation. Lanczos is the same session with kind Lanczos.
125 const bool lanczos = params.saddle_search_options().minmode_method ==
127 config.kind = lanczos ? RGSADDLE_MINMODE_LANCZOS : RGSADDLE_MINMODE_DIMER;
128 config.method = params.optimizer_options().xtsci.method == "lbfgs"
129 ? RGSADDLE_METHOD_LBFGS
130 : RGSADDLE_METHOD_FIRE;
131 // dr is the finite-difference length in Angstrom, the separation the
132 // host dimer and Lanczos use; rotation_angle is a rotation trial angle
133 // in radians and does not belong here.
134 const double fd = params.main_options().finiteDifference;
135 config.dr = fd > 0.0 ? fd : 1.0e-3;
136 config.rotation_tol = dim.torque_min;
137 config.max_rotations = std::max<long>(1, dim.rotations_max);
138 config.krylov_dim = 12;
139 config.force_tol = 0.0;
140 // Zero cap: rotation only. The climb uses the host optimizer.
141 config.max_move = 0.0;
142#if RGSADDLE_ABI_MINOR >= 5
143 config.rotation_angle_tol = eonc::helpers::pi * dim.converged_angle / 180.0;
144 config.difference =
145 parseFdScheme(params.hessian_options().fd_scheme) == FdScheme::OneSided
146 ? RGSADDLE_DIFFERENCE_FORWARD
147 : RGSADDLE_DIFFERENCE_CENTRAL;
148#endif
149
150 Matter probe(*matter);
151 MinModeUser ctx{matter.get(), &probe, &m_fixed};
152 const size_t callsBefore = matter->getPotentialCalls();
153 rgsaddle_report_t report{};
154 std::vector<double> mode(static_cast<size_t>(3 * nAtoms), 0.0);
155#if RGSADDLE_ABI_MINOR >= 5
156 // One session for the whole search: the refreshed mode seeds the next
157 // estimate, and the host's gradient at the centre is handed over so the
158 // session does not evaluate the centre again.
159 if (m_session != nullptr && m_sessionAtoms != nAtoms) {
160 rgsaddle_minmode_free(m_session);
161 m_session = nullptr;
162 }
163 if (m_session == nullptr) {
164 m_session = rgsaddle_minmode_create(&config, nAtoms, saved.data(),
165 initialDirection.data());
166 if (m_session == nullptr) {
167 throw std::runtime_error("rgsaddle_minmode_create failed");
168 }
169 m_sessionAtoms = nAtoms;
170 } else if (initialDirection != m_eigenvector) {
171 checkStatus(rgsaddle_minmode_set_mode(m_session, initialDirection.data()),
172 "rgsaddle_minmode_set_mode");
173 }
174 const AtomMatrix gradient = -matter->getForces();
175 checkStatus(
176 rgsaddle_minmode_set_position(m_session, saved.data(), gradient.data()),
177 "rgsaddle_minmode_set_position");
178 const int rc =
179 xts_minmode_estimate(m_session, surfaceCallback, &ctx, &report);
180 checkStatus(rc, "rgsaddle_minmode_estimate");
181 checkStatus(rgsaddle_minmode_mode(m_session, mode.data()),
182 "rgsaddle_minmode_mode");
183#else
184 RgsaddleMinMode *session = rgsaddle_minmode_create(
185 &config, nAtoms, saved.data(), initialDirection.data());
186 if (session == nullptr) {
187 throw std::runtime_error("rgsaddle_minmode_create failed");
188 }
189 const int rc = rgsaddle_minmode_step(session, surfaceCallback, &ctx, &report);
190 const int modeRc = rgsaddle_minmode_mode(session, mode.data());
191 rgsaddle_minmode_free(session);
192 checkStatus(rc, "rgsaddle_minmode_step");
193 checkStatus(modeRc, "rgsaddle_minmode_mode");
194#endif
195 m_eigenvector = AtomMatrix::Map(mode.data(), nAtoms, 3);
196 m_eigenvalue = report.curvature;
197 statsCurvature = report.curvature;
198 statsRotations = report.rotations;
199 totalIterations += 1;
201 static_cast<long>(matter->getPotentialCalls() - callsBefore);
202}
203
205 if (m_session != nullptr) {
206 rgsaddle_minmode_free(m_session);
207 }
208}
209
211
213
214} // namespace eonc
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
double getPotentialEnergy() const
Definition Matter.cpp:621
const AtomMatrix & getForces() const
Definition Matter.cpp:479
const Parameters & params
std::shared_ptr< Potential > pot
static const char MINMODE_LANCZOS[]
LowestEigenmode(std::shared_ptr< Potential > potPassed, const Parameters &parameters)
XtsciMinMode(std::shared_ptr< Matter > matter, const Parameters &params, std::shared_ptr< Potential > pot)
AtomMatrix getEigenvector() override
double getEigenvalue() override
void compute(std::shared_ptr< Matter > matter, AtomMatrix initialDirection) override
AtomMatrix m_eigenvector
RgsaddleMinMode * m_session
constexpr double pi
RAII resource manager for the ARTn C library with global synchronization.
FdScheme parseFdScheme(std::string_view scheme)