Loading...
Searching...
No Matches
NEBSpringForce.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/NEBSpringForce.h"
13
14#include <stdexcept>
15
16namespace eonc::neb {
17
18// --- UniformSpring ---
19
21UniformSpring::compute(long i, const AtomMatrix &tangent, double distNext,
22 double distPrev, const AtomMatrix &posDiffNext,
23 const AtomMatrix &posDiffPrev,
24 const std::shared_ptr<Matter> &image) const {
25 SpringResult result;
26 result.forceSpringPar = ksp * (distNext - distPrev) * tangent;
27 result.forceSpring = ksp * image->pbc((posDiffNext) - (posDiffPrev));
28 return result;
29}
30
31// --- WeightedSpring ---
32
34 double distNext, double distPrev) const {
35 if (i < 1 || static_cast<size_t>(i) >= springConstants.size()) {
36 throw std::invalid_argument(
37 "WeightedSpring::compute: image index out of range");
38 }
39 double kspNext = springConstants[i];
40 double kspPrev = springConstants[i - 1];
41
42 SpringResult result;
43 result.forceSpringPar =
44 ((kspNext * distNext) - (kspPrev * distPrev)) * tangent;
45 result.forceSpring = AtomMatrix::Zero(tangent.rows(), tangent.cols());
46 return result;
47}
48
49// --- OnsagerMachlupSpring ---
50
53 const AtomMatrix &posNext,
54 const AtomMatrix &posPrev, const AtomMatrix &pos,
55 const std::shared_ptr<Matter> &image) const {
56 // Mandelli Eq. 13: k * ( R(i+1) + R(i-1) - 2R(i) + L(i+1) - L(i) )
57 AtomMatrix diff =
58 image->pbc(posNext + posPrev - 2.0 * pos + L_vecs[i + 1] - L_vecs[i]);
59 AtomMatrix f_om_vec = base_k * diff;
60
61 // Mandelli Eq. 15: Project onto tangent
62 SpringResult result;
63 result.forceSpringPar = matDot(f_om_vec, tangent) * tangent;
64 result.forceSpring = AtomMatrix::Zero(tangent.rows(), tangent.cols());
65 return result;
66}
67
68// --- Factory ---
69
72 const std::vector<std::shared_ptr<Matter>> &path,
73 long numImages, int atoms, double maxEnergy, double E_ref) {
74
75 if (params.neb_options().spring.om.enabled) {
76 double base_k = params.neb_options().spring.constant;
77
78 if (params.neb_options().spring.om.optimize_k) {
79 double avgPotForce = 0.0;
80 double avgPathCurvature = 0.0;
81 int count = 0;
82 for (long j = 1; j <= numImages; j++) {
83 avgPotForce += path[j]->getForces().norm();
84 AtomMatrix next = path[j + 1]->getPositions();
85 AtomMatrix prev = path[j - 1]->getPositions();
86 AtomMatrix curr = path[j]->getPositions();
87 AtomMatrix curvVec = path[j]->pbc(next + prev - 2.0 * curr);
88 avgPathCurvature += curvVec.norm();
89 count++;
90 }
91 if (count > 0 && avgPathCurvature > 1e-6) {
92 double scale = params.neb_options().spring.om.k_scale;
93 base_k = scale * (avgPotForce / avgPathCurvature);
94 base_k = std::max(base_k, params.neb_options().spring.om.k_min);
95 base_k = std::min(base_k, params.neb_options().spring.om.k_max);
96 }
97 }
98
99 // Pre-calculate L vectors for all images
100 std::vector<AtomMatrix> L_vecs(numImages + 2);
101 for (long j = 0; j <= numImages + 1; j++) {
102 L_vecs[j].resize(atoms, 3);
103 if (j == 0 || j == numImages + 1) {
104 L_vecs[j].setZero();
105 } else {
106 const AtomMatrix &forces = path[j]->getForces();
107 double alpha_k = eonc::safemath::safe_recip(2.0 * base_k, 0.0);
108 for (int k = 0; k < atoms; k++) {
109 L_vecs[j].row(k) = alpha_k * forces.row(k);
110 }
111 }
112 }
113
114 return OnsagerMachlupSpring{base_k, std::move(L_vecs)};
115
116 } else if (params.neb_options().spring.weighting.enabled) {
117 double k_l = params.neb_options().spring.weighting.k_min;
118 double k_u = params.neb_options().spring.weighting.k_max;
119 std::vector<double> springConstants(numImages + 2, k_l);
120
121 double energyRange = maxEnergy - E_ref;
122 if (energyRange < 1e-10) {
123 std::fill(springConstants.begin(), springConstants.end(), k_l);
124 } else {
125 for (int idx = 1; idx <= numImages + 1; idx++) {
126 double Ei = std::max(path[idx]->getPotentialEnergy(),
127 path[idx - 1]->getPotentialEnergy());
128 if (Ei > E_ref) {
129 double alpha_i = (maxEnergy - Ei) / energyRange;
130 alpha_i = std::max(0.0, std::min(1.0, alpha_i));
131 springConstants[idx - 1] = (1.0 - alpha_i) * k_u + alpha_i * k_l;
132 } else {
133 springConstants[idx - 1] = k_l;
134 }
135 }
136 }
137
138 return WeightedSpring{std::move(springConstants)};
139
140 } else {
141 return UniformSpring{params.neb_options().spring.constant};
142 }
143}
144
145} // namespace eonc::neb
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
const neb_options_t & neb_options() const
SpringStrategy buildSpringStrategy(const Parameters &params, const std::vector< std::shared_ptr< Matter > > &path, long numImages, int atoms, double maxEnergy, double E_ref)
Build the appropriate spring strategy from parameters and current path state.
std::variant< UniformSpring, WeightedSpring, OnsagerMachlupSpring > SpringStrategy
constexpr double safe_recip(double x, double fallback=0.0)
Definition SafeMath.h:29
Onsager-Machlup action-based springs (Mandelli & Parrinello 2021).
SpringResult compute(long i, const AtomMatrix &tangent, const AtomMatrix &posNext, const AtomMatrix &posPrev, const AtomMatrix &pos, const std::shared_ptr< Matter > &image) const
std::vector< AtomMatrix > L_vecs
Result of spring force computation for a single image.
Uniform spring constant for all images.
SpringResult compute(long i, const AtomMatrix &tangent, double distNext, double distPrev, const AtomMatrix &posDiffNext, const AtomMatrix &posDiffPrev, const std::shared_ptr< Matter > &image) const
Energy-weighted spring constants (variable per segment).
std::vector< double > springConstants
SpringResult compute(long i, const AtomMatrix &tangent, double distNext, double distPrev) const
struct eonc::neb_options_t::spring_options_t::energy_weighting_t weighting
struct eonc::neb_options_t::spring_options_t::onsager_machlup_t om
struct eonc::neb_options_t::spring_options_t spring