138 {
139 int nImgs =
path.size() - 2;
140 int nfree =
static_cast<int>(
path[0].numberOfFreeAtoms());
141 VectorXd totalGradient(3 * nfree * nImgs);
142 double maxForce = 0.0;
143
144
145 std::vector<AtomMatrix> rawForces(
path.size());
146 std::vector<AtomMatrix> tangents(
path.size());
147
148
149 for (size_t i = 1; i <= nImgs; ++i) {
150
151 double xi = static_cast<double>(i) / (nImgs + 1);
153
155
156
159 tangents[i] =
path[i].pbc(nextPos - prevPos);
160 const double tnorm = tangents[i].norm();
161 if (tnorm > 1e-10) {
162 tangents[i] /= tnorm;
163 }
164 }
165
166
167 double k =
params.neb_options().spring.constant;
168
169 for (size_t i = 1; i <= nImgs; ++i) {
172
173
174 double f_dot_t =
matDot(f, t);
176
177
178 double distNext =
path[i].distanceTo(
path[i + 1]);
179 double distPrev =
path[i].distanceTo(
path[i - 1]);
180 AtomMatrix f_spring = k * (distNext - distPrev) * t;
181
183
184 VectorXd freeForce = packFree(
path[i], f_neb);
185 totalGradient.segment(3 * nfree * static_cast<int>(i - 1), 3 * nfree) =
186 freeForce * -1.0;
187
188
189
190 maxForce = std::max(maxForce, freeForce.lpNorm<Eigen::Infinity>());
191 }
192
194 return totalGradient;
195}
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