66 VectorXd initialDirection = VectorXd::Map(initialDirectionAtomMatrix.data(),
67 3 * matter->numberOfAtoms());
68 tau = initialDirection.array() * matter->getFreeV().array();
71 if (
tau.norm() > 1e-10) {
72 eonc::safemath::safe_normalize_inplace(
tau);
76 tau =
tau.array() * matter->getFreeV().array();
77 eonc::safemath::safe_normalize_inplace(
tau);
81 double bestNegativeCurvature = std::numeric_limits<double>::max();
82 VectorXd bestTau =
tau;
83 VectorXd bestX0Positions;
84 VectorXd bestG0, bestG1;
91 VectorXd x0_r =
x0->getPositionsV();
92 bestX0Positions = x0_r;
94 double delta =
params.main_options.finiteDifference;
95 x1->setPositionsV(x0_r + delta *
tau);
98 if (
x1->getPotentialEnergy() -
x0->getPotentialEnergy() > 10.0 * delta) {
100 x1->setPositionsV(x0_r + delta *
tau);
102 log,
"[IDimer] Initial tangent flipped due to high energy wall.");
108 AtomMatrix::Map(
tau.data(), matter->numberOfAtoms(), 3),
109 static_cast<quill::Logger *
>(
log))) {
110 C_tau = alt->eigenvalue;
111 tau = VectorXd::Map(alt->eigenvector.data(), 3 * matter->numberOfAtoms());
114 tau =
tau.array() * matter->getFreeV().array();
115 eonc::safemath::safe_normalize_inplace(
tau);
116 x0_r = matter->getPositionsV();
117 x0->setPositionsV(x0_r);
118 x1->setPositionsV(x0_r + delta *
tau);
132 VectorXd x1_rp, x1_r, tau_prime, tau_Old, g1_prime;
135 double phi_prime = 0.0;
136 double phi_min = 0.0;
141 auto x1p = std::make_shared<Matter>(
x1->getPotential(),
params);
144 if (
params.dimer_options.remove_rotation) {
146 x1_r =
x1->getPositionsV();
148 eonc::safemath::safe_normalize_inplace(
tau);
149 x1_r = x0_r +
tau * delta;
159 pot->isSharedInstanceThreadSafe() ||
pot->needsPerImageInstance();
160 if (
pot->supportsBatchEvaluation()) {
161 long n =
x0->numberOfAtoms();
162 bool x0dirty =
x0->needsForceUpdate();
163 bool x1dirty =
x1->needsForceUpdate();
165 if (x0dirty && x1dirty) {
166 auto nrs0 =
x0->getAtomicNrs();
167 auto nrs1 =
x1->getAtomicNrs();
168 auto box0 =
x0->getCell();
169 auto box1 =
x1->getCell();
170 const double *posVec[] = {
x0->getPositions().data(),
171 x1->getPositions().data()};
172 const int *nrsVec[] = {nrs0.data(), nrs1.data()};
173 double *frcVec[] = {
x0->forcesData(),
x1->forcesData()};
174 double energies[2], vars[2];
175 const double *boxVec[] = {box0.data(), box1.data()};
176 pot->forceBatch(2, n, posVec, nrsVec, frcVec, energies, vars, boxVec);
177 x0->setComputedPotential(energies[0], vars[0]);
178 x1->setComputedPotential(energies[1], vars[1]);
179 }
else if (x1dirty) {
180 auto nrs =
x1->getAtomicNrs();
181 auto box =
x1->getCell();
182 const double *posVec[] = {
x1->getPositions().data()};
183 const int *nrsVec[] = {nrs.data()};
184 double *frcVec[] = {
x1->forcesData()};
185 double energies[1], vars[1];
186 const double *boxVec[] = {box.data()};
187 pot->forceBatch(1, n, posVec, nrsVec, frcVec, energies, vars, boxVec);
188 x1->setComputedPotential(energies[0], vars[0]);
189 }
else if (x0dirty) {
192 g0 = -
x0->getForcesV();
193 g1 = -
x1->getForcesV();
194 }
else if (
params.main_options.parallel && canParallel) {
197 std::thread t0([&] { g0 = -
x0->getForcesV(); });
199 g1 = -
x1->getForcesV();
207 g0 = -
x0->getForcesV();
208 g1 = -
x1->getForcesV();
224 F_R = -2.0 * (g1 - g0) + 2.0 * ((g1 - g0).dot(
tau)) *
tau;
229 theta = eonc::safemath::safe_normalized(
F_R);
249 eonc::safemath::safe_normalize_inplace(
theta);
254 VectorXd s0 =
tau - tau_Old;
264 double H0 = 1.0 / 60.0;
265 size_t loopmax =
s.size();
266 std::vector<double> alpha(loopmax);
269 for (
long i =
static_cast<long>(loopmax) - 1; i >= 0; i--) {
270 alpha[i] =
rho[i] *
s[i].dot(q);
271 q -= alpha[i] *
y[i];
274 for (
size_t i = 0; i < loopmax; i++) {
275 double bv =
rho[i] *
y[i].dot(z);
276 z +=
s[i] * (alpha[i] - bv);
279 double vd = std::clamp(-eonc::safemath::safe_normalized(z).dot(
280 eonc::safemath::safe_normalized(
F_R)),
292 theta = -eonc::safemath::safe_normalized(z);
294 eonc::safemath::safe_normalize_inplace(
theta);
305 if (
C_tau < bestNegativeCurvature) {
306 bestNegativeCurvature =
C_tau;
308 bestX0Positions =
x0->getPositionsV();
315 double d_C_tau_d_phi =
318 d_C_tau_d_phi, 2.0 * std::abs(
C_tau), 0.0);
321 double alignment = std::abs(
tau.dot(referenceMode));
323 if (std::abs(phi_prime) > phi_tol) {
324 double b1 = 0.5 * d_C_tau_d_phi;
327 x0_r =
x0->getPositionsV();
328 tau_prime =
tau * std::cos(phi_prime) +
theta * std::sin(phi_prime);
329 tau_prime = eonc::safemath::safe_normalized(tau_prime);
330 x1_rp = x0_r + tau_prime * delta;
333 x1p->setPositionsV(x1_rp);
334 g1_prime = -x1p->getForcesV();
344 C_tau - C_tau_prime + b1 * std::sin(2.0 * phi_prime),
345 1.0 - std::cos(2.0 * phi_prime), 0.0);
346 double a0 = 2.0 * (
C_tau - a1);
349 double C_tau_min = 0.5 * a0 + a1 * std::cos(2.0 * phi_min) +
350 b1 * std::sin(2.0 * phi_min);
353 if (C_tau_min >
C_tau) {
355 C_tau_min = 0.5 * a0 + a1 * std::cos(2.0 * phi_min) +
356 b1 * std::sin(2.0 * phi_min);
366 tau =
tau * std::cos(phi_min) +
theta * std::sin(phi_min);
367 tau = eonc::safemath::safe_normalized(
tau);
368 x1_r = x0_r +
tau * delta;
371 if (
params.dimer_options.remove_rotation) {
372 x1->setPositionsV(x1_r);
375 x1_r =
x1->getPositionsV();
377 eonc::safemath::safe_normalize_inplace(
tau);
378 x1_r = x0_r +
tau * delta;
381 x1->setPositionsV(x1_r);
385 double sin_pp = std::sin(phi_prime);
389 g0 * (1.0 - std::cos(phi_min) -
390 std::sin(phi_min) * std::tan(phi_prime * 0.5));
396 "[IDimerRot] ----- --------- ---------- ------------------ "
397 "{:9.4f} {:7.3f} {:6.3f} {:4} {:5.3f}",
402 "[IDimerRot] ----- --------- ---------- ------------------ "
403 "{:9.4f} {:7.3f} ------ ---- {:5.3f}",
404 C_tau,
F_R.norm() / delta, alignment);
408 if (alignment <
params.neb_options.climbing_image.ocineb.angle_tol &&
409 params.neb_options.climbing_image.ocineb.use_mmf) {
411 log,
"Terminating dimer due to lost mode (align {:.3f}).", alignment);
414 if (bestNegativeCurvature < 0.0) {
416 C_tau = bestNegativeCurvature;
418 x0->setPositionsV(bestX0Positions);
419 x1->setPositionsV(bestX0Positions + delta * bestTau);
422 log,
"Restored best negative curvature state: C_tau={:.4f}",
C_tau);
426 log,
"Never found negative curvature. Final C_tau: {:.4f}",
C_tau);
431 }
while (std::abs(phi_prime) > std::abs(phi_tol) &&
432 std::abs(phi_min) > std::abs(phi_tol) &&