124 {
125 int nImgs =
path.size() - 2;
126 int natoms =
path[0].numberOfAtoms();
127 VectorXd totalGradient(3 * natoms * nImgs);
128 double maxForce = 0.0;
129
130
131 std::vector<AtomMatrix> rawForces(
path.size());
132 std::vector<AtomMatrix> tangents(
path.size());
133
134
135 for (size_t i = 1; i <= nImgs; ++i) {
136
137 double xi = static_cast<double>(i) / (nImgs + 1);
139
141
142
145 tangents[i] =
path[i].pbc(nextPos - prevPos);
146 tangents[i].normalize();
147 }
148
149
150 double k =
params.neb_options.spring.constant;
151
152 for (size_t i = 1; i <= nImgs; ++i) {
155
156
157 double f_dot_t =
matDot(f, t);
159
160
161 double distNext =
path[i].distanceTo(
path[i + 1]);
162 double distPrev =
path[i].distanceTo(
path[i - 1]);
163 AtomMatrix f_spring = k * (distNext - distPrev) * t;
164
165
167
168
169 totalGradient.segment(3 * natoms * (i - 1), 3 * natoms) =
170 VectorXd::Map(f_neb.data(), 3 * natoms) * -1.0;
171
172
173 maxForce = std::max(maxForce, f_neb.template lpNorm<Eigen::Infinity>());
174 }
175
177 return totalGradient;
178}
double matDot(const AtomMatrix &a, const AtomMatrix &b)
SIMD-optimized dot product for contiguous Eigen matrices.
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
MatrixXd getIDPPForces(const Matter &m, const MatrixXd &dTarget)
const Parameters & params