Loading...
Searching...
No Matches
eonc::Davidson Class Reference

Davidson method for the lowest Hessian curvature mode (min-mode). More...

#include <Davidson.h>

Inheritance diagram for eonc::Davidson:

Public Member Functions

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

Private Attributes

AtomMatrix lowestEv
double lowestEw
eonc::log::Scoped log

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< Potential > pot
const Parameters & params

Detailed Description

Davidson method for the lowest Hessian curvature mode (min-mode).

Uses the same finite-difference Hessian-vector products as Lanczos, but expands a Ritz subspace with residual correction (optionally diagonal- preconditioned) instead of dimer rotational constrained minimization. See Olsen et al., J. Chem. Phys. 121, 9776 (2004) for FD context; Davidson subspace expansion follows the classical residual-correction form.

Default Ritz space = all free atoms ([Davidson] phva_atoms = All). Optional mobile list is the PHVA active set; free/fixed stays the optimizer mask.

Definition at line 31 of file Davidson.h.

Constructor & Destructor Documentation

◆ Davidson()

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

Definition at line 36 of file Davidson.cpp.

39 lowestEv.resize(matter->numberOfAtoms(), 3);
40 lowestEv.setZero();
41 lowestEw = 0.0;
42}
double lowestEw
Definition Davidson.h:46
AtomMatrix lowestEv
Definition Davidson.h:45
const Parameters & params
std::shared_ptr< Potential > pot
LowestEigenmode(std::shared_ptr< Potential > potPassed, const Parameters &parameters)

◆ ~Davidson()

eonc::Davidson::~Davidson ( )
default

Member Function Documentation

◆ compute() [1/2]

void eonc::Davidson::compute ( std::shared_ptr< Matter > matter,
AtomMatrix initialDirection )
overridevirtual

Implements eonc::LowestEigenmode.

Definition at line 44 of file Davidson.cpp.

44 {
45 const VectorXi mobile =
46 resolveMobileAtoms(matter.get(), params.davidson_options().phva_atoms);
47 compute(std::move(matter), std::move(direction), mobile);
48}
void compute(std::shared_ptr< Matter > matter, AtomMatrix initialDirection) override
Definition Davidson.cpp:44
VectorXi resolveMobileAtoms(const Matter *matter, const std::string &atomList)
PHVA-class mobile set for FD Hessian and matrix-free Krylov (Lanczos / Davidson).

◆ compute() [2/2]

void eonc::Davidson::compute ( std::shared_ptr< Matter > matter,
AtomMatrix initialDirection,
const VectorXi & mobileAtoms )

Definition at line 50 of file Davidson.cpp.

51 {
54 lowestEv.resize(matter->numberOfAtoms(), 3);
55 lowestEv.setZero();
56
57 const VectorXi mobile = resolveMobileAtoms(matter.get(), mobileIn);
58 const int size = 3 * static_cast<int>(mobile.size());
59 if (size == 0) {
60 lowestEw = 0.0;
61 return;
62 }
63
64 const long maxIter = std::max(1L, params.davidson_options().max_iterations);
65 const double tol = params.davidson_options().tolerance;
66 const double dr = params.main_options().finiteDifference;
67 const FdScheme requested = parseFdScheme(params.hessian_options().fd_scheme);
68 // Fourth-order is the only scheme that changes the matrix-free product.
69 // one_sided and central keep the forward difference used by min-mode.
70 const FdScheme hvpScheme =
72 const bool useDiagPrec = params.davidson_options().diagonal_preconditioner;
73
74 MatrixXd V(size, maxIter);
75 MatrixXd HV(size, maxIter);
76 V.setZero();
77 HV.setZero();
78
79 VectorXd r = packMobileRows(direction, mobile);
80 double beta = r.norm();
81 if (beta < eonc::safemath::eps) {
82 lowestEw = 0.0;
83 return;
84 }
85 r /= beta;
86
87 auto tmpMatter = std::make_unique<Matter>(*matter);
88 const long forceCallsStart = tmpMatter->getForceCalls();
89 const AtomMatrix pos0 = matter->getPositions();
90 const VectorXd force0 = mobileForces(tmpMatter.get(), mobile);
91
92 auto applyH = [&](const VectorXd &v) -> VectorXd {
93 auto at = [&](double scale) -> VectorXd {
94 AtomMatrix pos = pos0;
95 unpackMobileRows(packMobileRows(pos0, mobile) + (scale * dr) * v, mobile,
96 pos);
97 tmpMatter->setPositions(pos);
98 return mobileForces(tmpMatter.get(), mobile);
99 };
100 return fdHessianVector(hvpScheme, dr, force0, at);
101 };
102
103 VectorXd diagH = VectorXd::Ones(size);
104
105 V.col(0) = r;
106 HV.col(0) = applyH(V.col(0));
107 if (useDiagPrec) {
108 for (int k = 0; k < size; ++k) {
109 const double vk = V(k, 0);
110 if (std::fabs(vk) > 1e-8) {
111 diagH(k) = std::max(std::fabs(HV(k, 0) / vk), 1e-3);
112 }
113 }
114 }
115
116 double ew = 0.0, ewOld = 0.0;
117 VectorXd evEst = V.col(0);
118 VectorXd evOldEst = evEst;
119 int subspace = 1;
120
121 for (int iter = 0; iter < maxIter; ++iter) {
122 statsRotations = iter;
123 statsAngle = 0.0;
124
125 MatrixXd G = V.leftCols(subspace).transpose() * HV.leftCols(subspace);
126 G = 0.5 * (G + G.transpose());
127
128 Eigen::SelfAdjointEigenSolver<MatrixXd> es(G);
129 ew = es.eigenvalues()(0);
130 VectorXd y = es.eigenvectors().col(0);
131 evEst = V.leftCols(subspace) * y;
132 eonc::safemath::safe_normalize_inplace(evEst);
133
134 VectorXd Hx = HV.leftCols(subspace) * y;
135 VectorXd resid = Hx - ew * evEst;
136 const double residNorm = resid.norm();
137 const double ewAbsRelErr =
138 (iter == 0) ? 1.0
139 : eonc::safemath::safe_div(std::fabs(ew - ewOld),
140 std::fabs(ewOld), 1.0);
141 ewOld = ew;
142 statsTorque = std::max(ewAbsRelErr, residNorm / (std::fabs(ew) + 1e-12));
143 statsAngle = eonc::safemath::safe_acos(std::fabs(evEst.dot(evOldEst))) *
144 (180 / eonc::helpers::pi);
145 evOldEst = evEst;
146
147 QUILL_LOG_INFO(log,
148 "[Davidson] ew={:10.6f} rel_err={:10.6f} |r|={:10.6f} "
149 "angle={:7.3f} dim={:3d} iter={:3d} n_mobile={}",
150 ew, ewAbsRelErr, residNorm, statsAngle, subspace, iter,
151 mobile.size());
152
153 if (ewAbsRelErr < tol && residNorm < tol * (std::fabs(ew) + 1.0)) {
154 QUILL_LOG_INFO(log, "[Davidson] Tolerance reached: {}", tol);
155 break;
156 }
157 if (subspace >= maxIter) {
158 QUILL_LOG_ERROR(log, "[Davidson] Max subspace dimension");
159 break;
160 }
161
162 VectorXd t(size);
163 if (useDiagPrec) {
164 for (int k = 0; k < size; ++k) {
165 const double denom = diagH(k) - ew;
166 t(k) = resid(k) / (std::fabs(denom) > 1e-12 ? denom : 1e-12);
167 }
168 } else {
169 t = resid;
170 }
171
172 for (int c = 0; c < subspace; ++c) {
173 t -= V.col(c).dot(t) * V.col(c);
174 }
175 const double tnorm = t.norm();
176 if (tnorm < 1e-14) {
177 QUILL_LOG_ERROR(log, "[Davidson] Linear dependence in residual");
178 break;
179 }
180 t /= tnorm;
181
182 V.col(subspace) = t;
183 HV.col(subspace) = applyH(t);
184 if (useDiagPrec) {
185 for (int k = 0; k < size; ++k) {
186 const double vk = t(k);
187 if (std::fabs(vk) > 1e-8) {
188 const double d = std::fabs(HV(k, subspace) / vk);
189 diagH(k) = std::max(diagH(k), std::max(d, 1e-3));
190 }
191 }
192 }
193 ++subspace;
194
195 if (iter >= maxIter - 1) {
196 QUILL_LOG_ERROR(log, "[Davidson] Max iterations");
197 break;
198 }
199 }
200
201 lowestEw = ew;
202 totalForceCalls = tmpMatter->getForceCalls() - forceCallsStart;
203 lowestEv.setZero();
204 if (evEst.size() == size) {
205 unpackMobileRows(evEst, mobile, lowestEv);
206 }
207}
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, eOnStorageOrder > MatrixXd
Definition Eigen.h:33
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
eonc::log::Scoped log
Definition Davidson.h:47
constexpr double pi
constexpr double safe_div(double num, double denom, double fallback=0.0)
Definition SafeMath.h:21
double safe_acos(double x)
Definition SafeMath.h:34
constexpr double eps
Definition SafeMath.h:19
FdScheme
Real finite-difference scheme for the assembled Hessian and for Lanczos/Davidson Hessian-vector produ...
VectorXd fdHessianVector(FdScheme scheme, double dr, const VectorXd &force0, Eval &&eval)
Energy Hessian-vector product -dF.
void unpackMobileRows(const VectorXd &packed, const VectorXi &mobile, AtomMatrix &full)
Write a 3*n_mobile vector into full AtomMatrix rows (other rows unchanged).
FdScheme parseFdScheme(std::string_view scheme)
VectorXd packMobileRows(const AtomMatrix &full, const VectorXi &mobile)
Pack full (n_atoms,3) rows of mobile atoms into a 3*n_mobile vector.
VectorXd mobileForces(Matter *matter, const VectorXi &mobile)
Force components on mobile atoms after Matter has a valid force cache (calls getForces under the hood...

◆ getEigenvalue()

double eonc::Davidson::getEigenvalue ( )
overridevirtual

Implements eonc::LowestEigenmode.

Definition at line 209 of file Davidson.cpp.

209{ return lowestEw; }

◆ getEigenvector()

AtomMatrix eonc::Davidson::getEigenvector ( )
overridevirtual

Implements eonc::LowestEigenmode.

Definition at line 211 of file Davidson.cpp.

211{ return lowestEv; }

Member Data Documentation

◆ log

eonc::log::Scoped eonc::Davidson::log
private

Definition at line 47 of file Davidson.h.

◆ lowestEv

AtomMatrix eonc::Davidson::lowestEv
private

Definition at line 45 of file Davidson.h.

◆ lowestEw

double eonc::Davidson::lowestEw
private

Definition at line 46 of file Davidson.h.


The documentation for this class was generated from the following files: