Loading...
Searching...
No Matches
Dimer Class Reference

Classic dimer method to find the lowest curvature mode. More...

#include <Dimer.h>

Inheritance diagram for Dimer:

Public Member Functions

 Dimer (std::shared_ptr< Matter > matter, const Parameters &params, std::shared_ptr< Potential > pot)
 ~Dimer ()=default
void compute (std::shared_ptr< Matter > matter, AtomMatrix initialDirection)
double getEigenvalue ()
AtomMatrix getEigenvector ()
Public Member Functions inherited from eonc::LowestEigenmode
 LowestEigenmode (std::shared_ptr< Potential > potPassed, const Parameters &parameters)
 ~LowestEigenmode ()=default

Private Member Functions

void determineRotationalPlane (const AtomMatrix &rotationalForce, AtomMatrix &rotationalForceOld, const AtomMatrix &rotationalPlaneOld, double &lengthRotationalForceOld)
 Determine rotational plane via conjugate gradient.
void rotate (double rotationAngle)
 Rotate the dimer by the given angle (radians).
double calcRotationalForceReturnCurvature (AtomMatrix &rotationalForce)
 Compute rotational force and return curvature along the dimer.

Private Attributes

eonc::log::FileScoped log {"dimer", "dimer.log"}
std::shared_ptr< MattermatterCenter
std::shared_ptr< MattermatterDimer
AtomMatrix direction
AtomMatrix rotationalPlane
double eigenvalue {0.0}
long nAtoms {0}

Additional Inherited Members

Public Attributes inherited from eonc::LowestEigenmode
long totalForceCalls {0}
double statsTorque {0.0}
double statsCurvature {0.0}
double statsAngle {0.0}
long statsRotations {0}
long totalIterations {0}
Static Public Attributes inherited from eonc::LowestEigenmode
static const char MINMODE_DIMER [] = "dimer"
static const char MINMODE_GPRDIMER [] = "gprdimer"
static const char MINMODE_LANCZOS [] = "lanczos"
static const char MINMODE_DAVIDSON [] = "davidson"
Protected Attributes inherited from eonc::LowestEigenmode
std::shared_ptr< Potentialpot
const Parametersparams

Detailed Description

Classic dimer method to find the lowest curvature mode.

Uses finite-difference rotation to converge on the minimum eigenmode.

Definition at line 22 of file Dimer.h.

Constructor & Destructor Documentation

◆ Dimer()

Dimer::Dimer ( std::shared_ptr< Matter > matter,
const Parameters & params,
std::shared_ptr< Potential > pot )

Definition at line 23 of file Dimer.cpp.

26 // Give matterDimer its own potential for parallel force evaluation
27 auto dimerPot = (pot->needsPerImageInstance() && params.main_options.parallel)
29 : pot;
30 matterCenter = std::make_shared<Matter>(pot, params);
31 matterDimer = std::make_shared<Matter>(dimerPot, params);
32 *matterCenter = *matter;
33 *matterDimer = *matter;
34 nAtoms = matter->numberOfAtoms();
35
36 direction.resize(nAtoms, 3);
37 rotationalPlane.resize(nAtoms, 3);
38 direction.setZero();
39 rotationalPlane.setZero();
41}
AtomMatrix direction
Definition Dimer.h:36
AtomMatrix rotationalPlane
Definition Dimer.h:37
std::shared_ptr< Matter > matterDimer
Definition Dimer.h:35
long nAtoms
Definition Dimer.h:39
std::shared_ptr< Matter > matterCenter
Definition Dimer.h:34
const Parameters & params
std::shared_ptr< Potential > pot
LowestEigenmode(std::shared_ptr< Potential > potPassed, const Parameters &parameters)
std::shared_ptr< Potential > makePotential(const Parameters &params)

◆ ~Dimer()

eonc::Dimer::~Dimer ( )
default

Member Function Documentation

◆ calcRotationalForceReturnCurvature()

double Dimer::calcRotationalForceReturnCurvature ( AtomMatrix & rotationalForce)
private

Compute rotational force and return curvature along the dimer.

Definition at line 150 of file Dimer.cpp.

150 {
151 AtomMatrix posCenter = matterCenter->getPositions();
152
153 // Displace to get dimer configuration A
154 AtomMatrix posDimer =
155 posCenter + direction * params.main_options.finiteDifference;
156
157 // Optional rotation removal (Melander, Laasonen, Jonsson, JCTC 2015)
158 if (params.dimer_options.remove_rotation) {
159 matterDimer->setPositions(posDimer);
161 posDimer = matterDimer->getPositions();
162 direction = posDimer - posCenter;
163 eonc::safemath::safe_normalize_inplace(direction);
164 posDimer = posCenter + direction * params.main_options.finiteDifference;
165 }
166
167 // Obtain forces for dimer and center
168 matterDimer->setPositions(posDimer);
169 AtomMatrix forceA, forceCenter;
170 if (pot->supportsBatchEvaluation()) {
171 // Only batch systems that actually need recomputation.
172 bool centerDirty = matterCenter->needsForceUpdate();
173 bool dimerDirty = matterDimer->needsForceUpdate();
174
175 if (centerDirty && dimerDirty) {
176 // Both need eval -- batch together
177 auto nrs0 = matterCenter->getAtomicNrs();
178 auto nrs1 = matterDimer->getAtomicNrs();
179 auto box0 = matterCenter->getCell();
180 auto box1 = matterDimer->getCell();
181 const double *posVec[] = {matterCenter->getPositions().data(),
182 posDimer.data()};
183 const int *nrsVec[] = {nrs0.data(), nrs1.data()};
184 double *frcVec[] = {matterCenter->forcesData(),
185 matterDimer->forcesData()};
186 double energies[2], vars[2];
187 const double *boxVec[] = {box0.data(), box1.data()};
188 pot->forceBatch(2, nAtoms, posVec, nrsVec, frcVec, energies, vars,
189 boxVec);
190 matterCenter->setComputedPotential(energies[0], vars[0]);
191 matterDimer->setComputedPotential(energies[1], vars[1]);
192 } else if (dimerDirty) {
193 // Only dimer moved -- eval just dimer, center is cached
194 auto nrs = matterDimer->getAtomicNrs();
195 auto box = matterDimer->getCell();
196 const double *posVec[] = {posDimer.data()};
197 const int *nrsVec[] = {nrs.data()};
198 double *frcVec[] = {matterDimer->forcesData()};
199 double energies[1], vars[1];
200 const double *boxVec[] = {box.data()};
201 pot->forceBatch(1, nAtoms, posVec, nrsVec, frcVec, energies, vars,
202 boxVec);
203 matterDimer->setComputedPotential(energies[0], vars[0]);
204 } else if (centerDirty) {
205 // Only center moved (rare)
206 matterCenter->getForces(); // through computePotential
207 }
208 // else: both cached, nothing to do
209 forceCenter = matterCenter->getForces();
210 forceA = matterDimer->getForces();
211 } else {
212 forceA = matterDimer->getForces();
213 forceCenter = matterCenter->getForces();
214 }
215 AtomMatrix forceB = 2.0 * forceCenter - forceA;
216
217 double projA = matDot(direction, forceA);
218 double projB = matDot(direction, forceB);
219
220 // Remove force component parallel to dimer
221 forceA = makeOrthogonal(forceA, direction);
222 forceB = makeOrthogonal(forceB, direction);
223
224 // Rotational force = orthogonal force difference
225 rotationalForce =
226 (forceA - forceB) / (2.0 * params.main_options.finiteDifference);
227
228 // Curvature along the dimer
229 return (projB - projA) / (2.0 * params.main_options.finiteDifference);
230}
double matDot(const AtomMatrix &a, const AtomMatrix &b)
SIMD-optimized dot product for contiguous Eigen matrices.
Definition Eigen.h:50
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
void rotationRemove(const AtomMatrix r1, std::shared_ptr< Matter > m2)
AtomMatrix makeOrthogonal(const AtomMatrix v1, const AtomMatrix v2)

◆ compute()

void Dimer::compute ( std::shared_ptr< Matter > matter,
AtomMatrix initialDirection )

Definition at line 43 of file Dimer.cpp.

44 {
45 *matterCenter = *matter;
46
47 eonc::safemath::safe_normalize_inplace(initialDirection);
48 direction = initialDirection;
49
50 // Optional: LOR / Lanczos / Davidson (enum dispatch; classical falls
51 // through).
52 if (auto alt = runAlternativeRotation(params.dimer_options.rotation_backend,
53 matter, params, pot, direction,
54 static_cast<quill::Logger *>(log))) {
55 eigenvalue = alt->eigenvalue;
56 direction = alt->eigenvector;
57 totalForceCalls += alt->forceCalls;
58 statsRotations = alt->rotations;
59 eonc::safemath::safe_normalize_inplace(direction);
60 *matterCenter = *matter;
61 return;
62 }
63
64 long rotations = 0;
65 long forceCallsCenter = matterCenter->getForceCalls();
66 long forceCallsDimer = matterDimer->getForceCalls();
67 double curvature = 0.0;
68 double rotationAngle = 0.0;
69 double torque = 0.0;
70
71 AtomMatrix rotationalForce(nAtoms, 3);
72 AtomMatrix rotationalForceOld(nAtoms, 3);
73 AtomMatrix rotationalPlaneOld(nAtoms, 3);
74 rotationalForce.setZero();
75 rotationalForceOld.setZero();
76 rotationalPlaneOld.setZero();
77
78 statsAngle = 0;
79 double lengthRotationalForceOld = 0.0;
80
81 // Two force calls per rotation iteration
82 bool doneRotating = false;
83 while (!doneRotating) {
84 curvature = calcRotationalForceReturnCurvature(rotationalForce);
85
86 determineRotationalPlane(rotationalForce, rotationalForceOld,
87 rotationalPlaneOld, lengthRotationalForceOld);
88
89 torque = rotationalForce.norm();
90 assert(std::isnormal(torque));
91
92 // Convergence: stop if torque is below threshold or max rotations reached
93 if ((torque > params.dimer_options.torque_max &&
94 rotations >= params.dimer_options.rotations_max) ||
95 (torque < params.dimer_options.torque_max &&
96 torque >= params.dimer_options.torque_min &&
97 rotations >= params.dimer_options.rotations_min) ||
98 (torque < params.dimer_options.torque_min)) {
99 doneRotating = true;
100 }
101
102 double rotForce1 = matDot(rotationalForce, rotationalPlane);
103 rotate(params.dimer_options.rotation_angle);
104
105 if (!doneRotating) {
106 curvature = calcRotationalForceReturnCurvature(rotationalForce);
107 double rotForce2 = matDot(rotationalForce, rotationalPlane);
108
109 double rotForceChange =
110 (rotForce1 - rotForce2) / params.dimer_options.rotation_angle;
111 double forceDimer = (rotForce1 + rotForce2) / 2.0;
112
113 rotationAngle = eonc::safemath::safe_atan_ratio(2.0 * forceDimer,
114 rotForceChange, 0.0) /
115 2.0 -
116 params.dimer_options.rotation_angle / 2.0;
117
118 if (rotForceChange < 0) {
119 rotationAngle += eonc::helpers::pi / 2.0;
120 }
121
122 rotate(rotationAngle);
123 rotationalPlaneOld = rotationalPlane;
124 rotations++;
125 }
126 QUILL_LOG_DEBUG(log,
127 "[DimerRot] ----- --------- ---------------- "
128 "--------- {:9.3e} {:9.3e} {:9.3e} ---------\n",
129 curvature, torque,
130 rotationAngle * (180.0 / eonc::helpers::pi));
131 }
132
133 statsTorque = torque;
134 statsCurvature = curvature;
135 eonc::safemath::safe_normalize_inplace(direction);
137 statsAngle *= (180.0 / eonc::helpers::pi);
138 statsRotations = rotations;
139 eigenvalue = curvature;
140
141 forceCallsCenter = matterCenter->getForceCalls() - forceCallsCenter;
142 forceCallsDimer = matterDimer->getForceCalls() - forceCallsDimer;
143 totalForceCalls += forceCallsCenter + forceCallsDimer;
144}
double eigenvalue
Definition Dimer.h:38
eonc::log::FileScoped log
Definition Dimer.h:33
double calcRotationalForceReturnCurvature(AtomMatrix &rotationalForce)
Compute rotational force and return curvature along the dimer.
Definition Dimer.cpp:150
void rotate(double rotationAngle)
Rotate the dimer by the given angle (radians).
Definition Dimer.cpp:259
void determineRotationalPlane(const AtomMatrix &rotationalForce, AtomMatrix &rotationalForceOld, const AtomMatrix &rotationalPlaneOld, double &lengthRotationalForceOld)
Determine rotational plane via conjugate gradient.
Definition Dimer.cpp:232
constexpr double pi
double safe_acos(double x)
Definition SafeMath.h:34
double safe_atan_ratio(double num, double denom, double fallback=0.0)
Definition SafeMath.h:42
std::optional< DimerRotationResult > runAlternativeRotation(DimerRotationBackend backend, const std::shared_ptr< Matter > &matter, const Parameters &params, const std::shared_ptr< Potential > &pot, const AtomMatrix &initialDirection, quill::Logger *log=nullptr)
Run Lanczos, Davidson, or LOR.

◆ determineRotationalPlane()

void Dimer::determineRotationalPlane ( const AtomMatrix & rotationalForce,
AtomMatrix & rotationalForceOld,
const AtomMatrix & rotationalPlaneOld,
double & lengthRotationalForceOld )
private

Determine rotational plane via conjugate gradient.

Definition at line 232 of file Dimer.cpp.

235 {
236 double gamma = 0.0;
237 double a = std::abs(matDot(rotationalForce, rotationalForceOld));
238 double b = rotationalForceOld.squaredNorm();
239 if (a < 0.5 * b) {
240 // Polak-Ribiere conjugate gradient direction
241 gamma = (rotationalForce.array() *
242 (rotationalForce - rotationalForceOld).array())
243 .sum() /
244 b;
245 }
246
247 // New rotational plane from current force and previous plane
249 rotationalForce + rotationalPlaneOld * lengthRotationalForceOld * gamma;
250
251 // Orthogonalize to dimer direction and normalize
252 lengthRotationalForceOld = rotationalPlane.norm();
254 eonc::safemath::safe_normalize_inplace(rotationalPlane);
255
256 rotationalForceOld = rotationalForce;
257}

◆ getEigenvalue()

double Dimer::getEigenvalue ( )
nodiscard

Definition at line 146 of file Dimer.cpp.

146{ return eigenvalue; }

◆ getEigenvector()

Definition at line 148 of file Dimer.cpp.

148{ return direction; }

◆ rotate()

void Dimer::rotate ( double rotationAngle)
private

Rotate the dimer by the given angle (radians).

Definition at line 259 of file Dimer.cpp.

259 {
260 statsAngle += rotationAngle;
261
262 double cosA = std::cos(rotationAngle);
263 double sinA = std::sin(rotationAngle);
264
265 direction = direction * cosA + rotationalPlane * sinA;
266 rotationalPlane = rotationalPlane * cosA - direction * sinA;
267
268 eonc::safemath::safe_normalize_inplace(direction);
269 eonc::safemath::safe_normalize_inplace(rotationalPlane);
270
271 // Remove component from rotationalPlane parallel to direction
273 eonc::safemath::safe_normalize_inplace(rotationalPlane);
274}

Member Data Documentation

◆ direction

Definition at line 36 of file Dimer.h.

◆ eigenvalue

double eonc::Dimer::eigenvalue {0.0}
private

Definition at line 38 of file Dimer.h.

38{0.0};

◆ log

eonc::log::FileScoped eonc::Dimer::log {"dimer", "dimer.log"}
private

Definition at line 33 of file Dimer.h.

33{"dimer", "dimer.log"};

◆ matterCenter

std::shared_ptr<Matter> eonc::Dimer::matterCenter
private

Definition at line 34 of file Dimer.h.

◆ matterDimer

std::shared_ptr<Matter> eonc::Dimer::matterDimer
private

Definition at line 35 of file Dimer.h.

◆ nAtoms

long eonc::Dimer::nAtoms {0}
private

Definition at line 39 of file Dimer.h.

39{0};

◆ rotationalPlane

Definition at line 37 of file Dimer.h.


The documentation for this class was generated from the following files:
  • /home/runner/work/eOn/eOn/include/eon/Dimer.h
  • /home/runner/work/eOn/eOn/client/Dimer.cpp