Loading...
Searching...
No Matches
eonc::neb Namespace Reference

Classes

struct  DNEB_Projection
 Trygubenko & Wales, JCP 120:2082, 2004. More...
struct  ExtremaResult
struct  ImageForceData
 Data for a single image needed by projection strategies. More...
struct  ImprovedTangent
 Henkelman & Jonsson, JCP 113:9978, 2000. More...
struct  NEB_Projection
 Jonsson, Mills, Jacobsen 1998 (World Scientific). More...
class  OCINEBController
 Goswami (in prep). More...
struct  OnsagerMachlupSpring
 Onsager-Machlup action-based springs (Mandelli & Parrinello 2021). More...
struct  PlainEB
 Mills, Jonsson, Schenter, Surf. More...
struct  SimpleTangent
 Mills, Jonsson, Schenter, Surf. More...
struct  SpringResult
 Result of spring force computation for a single image. More...
struct  UniformSpring
 Uniform spring constant for all images. More...
struct  WeightedSpring
 Energy-weighted spring constants (variable per segment). More...

Typedefs

using ProjectionStrategy
using SpringStrategy
using TangentStrategy = std::variant<SimpleTangent, ImprovedTangent>

Functions

AtomMatrix computeTangent (const AtomMatrix &posDiffNext, const AtomMatrix &posDiffPrev, double energy, double energyPrev, double energyNext, bool use_old_tangent)
 Compute the tangent vector at image i using the improved tangent scheme.
AtomMatrix forcePerp (const AtomMatrix &force, const AtomMatrix &tangent)
 Compute the perpendicular component of force relative to the tangent.
AtomMatrix climbingImageForce (const AtomMatrix &force, const AtomMatrix &tangent, const AtomMatrix &forceDNEB)
 Compute the climbing image projected force.
AtomMatrix computeDNEB (const AtomMatrix &forceSpring, const AtomMatrix &tangent, const AtomMatrix &forcePerp)
 Compute the doubly-nudged elastic band perpendicular spring force.
void zeroTranslation (AtomMatrix &projectedForce, int nFreeAtoms, int nAtoms)
 Zero net translational force for fully free systems.
ProjectionStrategy buildProjectionStrategy (const Parameters &params)
 Build the projection strategy from parameters.
AtomMatrix computeDNEBComponent (const AtomMatrix &forceSpring, const AtomMatrix &tangent, const AtomMatrix &forcePerp)
 Compute the DNEB force component for a given image.
ExtremaResult findSplineExtrema (const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, long numImages)
 Find extrema along the MEP using cubic spline interpolation.
void printImageData (const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, const std::vector< std::shared_ptr< EigenmodeStrategy > > &eigenmode_solvers, long numImages, bool estimateEigenvalues, bool writeToFile, size_t idx, eonc::log::Scoped log)
 Print NEB image data to log and optionally to file.
std::vector< readcon::ConFrame > pathToConFrames (const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, const std::vector< std::shared_ptr< EigenmodeStrategy > > &eigenmode_solvers, long numImages, bool estimateEigenvalues, std::optional< size_t > bandIndex=std::nullopt)
 Build stamped ConFrames for a NEB band (same metadata as writePathCon).
eonc::io::IoStatus writePathCon (const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, const std::vector< std::shared_ptr< EigenmodeStrategy > > &eigenmode_solvers, long numImages, bool estimateEigenvalues, std::string filename, std::optional< size_t > bandIndex=std::nullopt)
 Write a NEB band as a multi-frame .con via readcon ConFrameBuilder::clone().
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.
TangentStrategy buildTangentStrategy (const Parameters &params)
 Build the tangent strategy from parameters.

Typedef Documentation

◆ ProjectionStrategy

Initial value:
std::variant<PlainEB, NEB_Projection, DNEB_Projection>

Definition at line 49 of file NEBProjection.h.

◆ SpringStrategy

Initial value:
std::variant<UniformSpring, WeightedSpring, OnsagerMachlupSpring>

Definition at line 58 of file NEBSpringForce.h.

◆ TangentStrategy

Definition at line 38 of file NEBTangent.h.

Function Documentation

◆ buildProjectionStrategy()

ProjectionStrategy eonc::neb::buildProjectionStrategy ( const Parameters & params)

Build the projection strategy from parameters.

DNEB is incompatible with OM and weighted springs; falls back to NEB_Projection.

Definition at line 33 of file NEBProjection.cpp.

33 {
34 bool omActive = params.neb_options.spring.om.enabled;
35 bool weightedActive = params.neb_options.spring.weighting.enabled;
36
37 if (params.neb_options.spring.use_elastic_band && !omActive &&
38 !weightedActive) {
39 return PlainEB{};
40 }
41 if (params.neb_options.spring.doubly_nudged && !omActive && !weightedActive) {
42 return DNEB_Projection{};
43 }
44 return NEB_Projection{};
45}
struct eonc::Parameters::neb_options_t neb_options
struct eonc::Parameters::neb_options_t::spring_options_t::onsager_machlup_t om
struct eonc::Parameters::neb_options_t::spring_options_t::energy_weighting_t weighting
struct eonc::Parameters::neb_options_t::spring_options_t spring
Jonsson, Mills, Jacobsen 1998 (World Scientific).
Mills, Jonsson, Schenter, Surf.

◆ buildSpringStrategy()

SpringStrategy eonc::neb::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.

Definition at line 65 of file NEBSpringForce.cpp.

67 {
68
69 if (params.neb_options.spring.om.enabled) {
70 double base_k = params.neb_options.spring.constant;
71
72 if (params.neb_options.spring.om.optimize_k) {
73 double avgPotForce = 0.0;
74 double avgPathCurvature = 0.0;
75 int count = 0;
76 for (long j = 1; j <= numImages; j++) {
77 avgPotForce += path[j]->getForces().norm();
78 AtomMatrix next = path[j + 1]->getPositions();
79 AtomMatrix prev = path[j - 1]->getPositions();
80 AtomMatrix curr = path[j]->getPositions();
81 AtomMatrix curvVec = path[j]->pbc(next + prev - 2.0 * curr);
82 avgPathCurvature += curvVec.norm();
83 count++;
84 }
85 if (count > 0 && avgPathCurvature > 1e-6) {
86 double scale = params.neb_options.spring.om.k_scale;
87 base_k = scale * (avgPotForce / avgPathCurvature);
88 base_k = std::max(base_k, params.neb_options.spring.om.k_min);
89 base_k = std::min(base_k, params.neb_options.spring.om.k_max);
90 }
91 }
92
93 // Pre-calculate L vectors for all images
94 std::vector<AtomMatrix> L_vecs(numImages + 2);
95 for (long j = 0; j <= numImages + 1; j++) {
96 L_vecs[j].resize(atoms, 3);
97 if (j == 0 || j == numImages + 1) {
98 L_vecs[j].setZero();
99 } else {
100 const AtomMatrix &forces = path[j]->getForces();
101 double alpha_k = eonc::safemath::safe_recip(2.0 * base_k, 0.0);
102 for (int k = 0; k < atoms; k++) {
103 L_vecs[j].row(k) = alpha_k * forces.row(k);
104 }
105 }
106 }
107
108 return OnsagerMachlupSpring{base_k, std::move(L_vecs)};
109
110 } else if (params.neb_options.spring.weighting.enabled) {
111 double k_l = params.neb_options.spring.weighting.k_min;
112 double k_u = params.neb_options.spring.weighting.k_max;
113 std::vector<double> springConstants(numImages + 2, k_l);
114
115 double energyRange = maxEnergy - E_ref;
116 if (energyRange < 1e-10) {
117 std::fill(springConstants.begin(), springConstants.end(), k_l);
118 } else {
119 for (int idx = 1; idx <= numImages + 1; idx++) {
120 double Ei = std::max(path[idx]->getPotentialEnergy(),
121 path[idx - 1]->getPotentialEnergy());
122 if (Ei > E_ref) {
123 double alpha_i = (maxEnergy - Ei) / energyRange;
124 alpha_i = std::max(0.0, std::min(1.0, alpha_i));
125 springConstants[idx - 1] = (1.0 - alpha_i) * k_u + alpha_i * k_l;
126 } else {
127 springConstants[idx - 1] = k_l;
128 }
129 }
130 }
131
132 return WeightedSpring{std::move(springConstants)};
133
134 } else {
136 }
137}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
constexpr double safe_recip(double x, double fallback=0.0)
Definition SafeMath.h:29
Onsager-Machlup action-based springs (Mandelli & Parrinello 2021).
Uniform spring constant for all images.
Energy-weighted spring constants (variable per segment).

◆ buildTangentStrategy()

TangentStrategy eonc::neb::buildTangentStrategy ( const Parameters & params)

Build the tangent strategy from parameters.

Definition at line 72 of file NEBTangent.cpp.

72 {
74 return SimpleTangent{};
75 }
76 return ImprovedTangent{};
77}
struct eonc::Parameters::neb_options_t::climbing_image_options_t climbing_image
Mills, Jonsson, Schenter, Surf.
Definition NEBTangent.h:23

◆ climbingImageForce()

AtomMatrix eonc::neb::climbingImageForce ( const AtomMatrix & force,
const AtomMatrix & tangent,
const AtomMatrix & forceDNEB )

Compute the climbing image projected force.

F_CI = F - 2*(F.t)*t + forceDNEB

Definition at line 73 of file NEBForceProjection.cpp.

75 {
76 return force - 2.0 * matDot(force, tangent) * tangent + forceDNEB;
77}
double matDot(const AtomMatrix &a, const AtomMatrix &b)
SIMD-optimized dot product for contiguous Eigen matrices.
Definition Eigen.h:50

◆ computeDNEB()

AtomMatrix eonc::neb::computeDNEB ( const AtomMatrix & forceSpring,
const AtomMatrix & tangent,
const AtomMatrix & fPerp )

Compute the doubly-nudged elastic band perpendicular spring force.

Definition at line 79 of file NEBForceProjection.cpp.

80 {
81 AtomMatrix forceSpringPerp =
82 forceSpring - matDot(forceSpring, tangent) * tangent;
83
84 const double forceSpringPerpNorm = forceSpringPerp.norm();
85 const double forcePerpNorm = fPerp.norm();
86
87 if (forceSpringPerpNorm > 1e-10 && forcePerpNorm > 1e-10) {
88 AtomMatrix forcePerpNormalized = fPerp / forcePerpNorm;
89 AtomMatrix dneb =
90 forceSpringPerp -
91 matDot(forceSpringPerp, forcePerpNormalized) * forcePerpNormalized;
92
93 double switching = 2.0 / eonc::helpers::pi *
94 std::atan(forcePerpNorm * forcePerpNorm /
95 (forceSpringPerpNorm * forceSpringPerpNorm));
96 dneb *= switching;
97 return dneb;
98 }
99
100 return AtomMatrix::Zero(tangent.rows(), tangent.cols());
101}
constexpr double pi

◆ computeDNEBComponent()

AtomMatrix eonc::neb::computeDNEBComponent ( const AtomMatrix & forceSpring,
const AtomMatrix & tangent,
const AtomMatrix & forcePerp )

Compute the DNEB force component for a given image.

Returned separately so it can be added to the CI force when DNEB is active.

Definition at line 47 of file NEBProjection.cpp.

49 {
50 return eonc::neb::computeDNEB(forceSpring, tangent, fPerp);
51}
AtomMatrix computeDNEB(const AtomMatrix &forceSpring, const AtomMatrix &tangent, const AtomMatrix &fPerp)
Compute the doubly-nudged elastic band perpendicular spring force.

◆ computeTangent()

AtomMatrix eonc::neb::computeTangent ( const AtomMatrix & posDiffNext,
const AtomMatrix & posDiffPrev,
double energy,
double energyPrev,
double energyNext,
bool use_old_tangent )

Compute the tangent vector at image i using the improved tangent scheme.

Returns a normalized tangent vector.

Definition at line 22 of file NEBForceProjection.cpp.

25 {
26 AtomMatrix tang;
27
28 if (use_old_tangent) {
29 tang = posDiffNext;
30 } else {
31 // Improved tangent scheme
32 if (energyNext > energy && energy > energyPrev) {
33 tang = posDiffNext;
34 } else if (energy > energyNext && energyPrev > energy) {
35 tang = posDiffPrev;
36 } else {
37 // Extremum: energy-weighted combination
38 double energyDiffPrev = energyPrev - energy;
39 double energyDiffNext = energyNext - energy;
40 double minDiffEnergy =
41 std::min(std::abs(energyDiffPrev), std::abs(energyDiffNext));
42 double maxDiffEnergy =
43 std::max(std::abs(energyDiffPrev), std::abs(energyDiffNext));
44
45 if (energyDiffPrev > energyDiffNext) {
46 tang = posDiffNext * minDiffEnergy + posDiffPrev * maxDiffEnergy;
47 } else {
48 tang = posDiffNext * maxDiffEnergy + posDiffPrev * minDiffEnergy;
49 }
50 }
51 }
52
53 // Normalize with safety check
54 double norm = tang.norm();
55 if (norm > 1e-10) {
56 tang /= norm;
57 } else {
58 // Fallback: use direction to next image
59 tang = posDiffNext;
60 norm = tang.norm();
61 if (norm > 1e-10) {
62 tang /= norm;
63 }
64 }
65
66 return tang;
67}

◆ findSplineExtrema()

ExtremaResult eonc::neb::findSplineExtrema ( const std::vector< std::shared_ptr< Matter > > & path,
const std::vector< std::shared_ptr< AtomMatrix > > & tangent,
long numImages )

Find extrema along the MEP using cubic spline interpolation.

Definition at line 74 of file NEBSplineExtrema.cpp.

76 {
77
78 auto *log = eonc::log::get();
79
80 // Calculate cubic parameters for each interval
81 AtomMatrix tangentEndpoint;
82 std::vector<double> a(numImages + 1), b(numImages + 1), c(numImages + 1),
83 d(numImages + 1);
84 double F1, F2, U1, U2, dist;
85
86 for (long i = 0; i <= numImages; i++) {
87 dist = path[i]->distanceTo(*path[i + 1]);
88 if (i == 0) {
89 tangentEndpoint =
90 path[i]->pbc(path[1]->getPositions() - path[0]->getPositions());
91 tangentEndpoint.normalize();
92 F1 = matDot(path[i]->getForces(), tangentEndpoint) * dist;
93 } else {
94 F1 = matDot(path[i]->getForces(), *tangent[i]) * dist;
95 }
96 if (i == numImages) {
97 tangentEndpoint = path[i + 1]->pbc(path[numImages + 1]->getPositions() -
98 path[numImages]->getPositions());
99 tangentEndpoint.normalize();
100 F2 = matDot(path[i + 1]->getForces(), tangentEndpoint) * dist;
101 } else {
102 F2 = matDot(path[i + 1]->getForces(), *tangent[i + 1]) * dist;
103 }
104 U1 = path[i]->getPotentialEnergy();
105 U2 = path[i + 1]->getPotentialEnergy();
106 a[i] = U1;
107 b[i] = -F1;
108 c[i] = 3. * (U2 - U1) + 2. * F1 + F2;
109 d[i] = -2. * (U2 - U1) - (F1 + F2);
110 }
111
112 ExtremaResult result;
113 result.positions.resize(2 * (numImages + 1));
114 result.energies.resize(2 * (numImages + 1));
115 result.curvatures.resize(2 * (numImages + 1));
116
117 double discriminant, f;
118
119 for (long i = 0; i <= numImages; i++) {
120 discriminant = c[i] * c[i] - 3.0 * b[i] * d[i];
121 if (discriminant >= 0) {
122 f = -1;
123
124 // Quadratic case
125 if ((d[i] == 0) && (c[i] != 0)) {
126 f = (-b[i] / (2. * c[i]));
127 }
128 // Cubic case 1
129 else if (d[i] != 0) {
130 f = -(c[i] + std::sqrt(discriminant)) / (3. * d[i]);
131 }
132 if ((f >= 0) && (f <= 1)) {
133 result.positions[result.numExtrema] = i + f;
134 result.energies[result.numExtrema] =
135 ((d[i] * f + c[i]) * f + b[i]) * f + a[i]; // Horner's method
136 result.curvatures[result.numExtrema] = 6.0 * d[i] * f + 2 * c[i];
137 result.numExtrema++;
138 }
139 // Cubic case 2
140 if (d[i] != 0) {
141 f = (-(c[i] - std::sqrt(discriminant)) / (3. * d[i]));
142 }
143 if ((f >= 0) && (f <= 1)) {
144 result.positions[result.numExtrema] = i + f;
145 result.energies[result.numExtrema] =
146 ((d[i] * f + c[i]) * f + b[i]) * f + a[i]; // Horner's method
147 result.curvatures[result.numExtrema] = 6 * d[i] * f + 2 * c[i];
148 result.numExtrema++;
149 }
150 }
151 }
152
153 QUILL_LOG_DEBUG(log, "Found {} extrema", result.numExtrema);
154 QUILL_LOG_DEBUG(log, "Energy reference: {}", path[0]->getPotentialEnergy());
155 for (long i = 0; i < result.numExtrema; i++) {
156 QUILL_LOG_DEBUG(
157 log, "extrema #{} at image position {} with energy {} and curvature {}",
158 i + 1, result.positions[i],
159 result.energies[i] - path[0]->getPotentialEnergy(),
160 result.curvatures[i]);
161 }
162
163 return result;
164}
quill::Logger * get() noexcept
Get or create the default "combi" logger.
Definition EonLogger.h:44
std::vector< double > positions

◆ forcePerp()

AtomMatrix eonc::neb::forcePerp ( const AtomMatrix & force,
const AtomMatrix & tangent )

Compute the perpendicular component of force relative to the tangent.

Definition at line 69 of file NEBForceProjection.cpp.

69 {
70 return force - matDot(force, tangent) * tangent;
71}

◆ pathToConFrames()

std::vector< readcon::ConFrame > eonc::neb::pathToConFrames ( const std::vector< std::shared_ptr< Matter > > & path,
const std::vector< std::shared_ptr< AtomMatrix > > & tangent,
const std::vector< std::shared_ptr< EigenmodeStrategy > > & eigenmode_solvers,
long numImages,
bool estimateEigenvalues,
std::optional< size_t > bandIndex = std::nullopt )
nodiscard

Build stamped ConFrames for a NEB band (same metadata as writePathCon).

Empty on invalid path size. Does not write to disk.

Definition at line 251 of file NEBSplineExtrema.cpp.

255 {
256 const size_t nframes = static_cast<size_t>(numImages) + 2;
257 if (path.size() < nframes) {
258 return {};
259 }
260
261 double distTotal = 0.0;
262 std::vector<eonc::io::ConFrameMetadata> metas;
263 metas.reserve(nframes);
264
265 for (long i = 0; i <= numImages + 1; i++) {
266 if (i > 0) {
267 distTotal += path[i]->distanceTo(*path[i - 1]);
268 }
269 metas.push_back(neb_frame_metadata(path, tangent, eigenmode_solvers,
270 numImages, estimateEigenvalues, i,
271 distTotal, bandIndex));
272 }
273
274 std::vector<std::shared_ptr<Matter>> band(
275 path.begin(), path.begin() + static_cast<std::ptrdiff_t>(nframes));
276 return eonc::io::buildNebPathFrames(band, metas);
277}
std::vector< readcon::ConFrame > buildNebPathFrames(const std::vector< std::shared_ptr< Matter > > &path, const std::vector< ConFrameMetadata > &metadata_per_image)
Build NEB band ConFrames without writing (clone builder path of writeNebPath).

◆ printImageData()

void eonc::neb::printImageData ( const std::vector< std::shared_ptr< Matter > > & path,
const std::vector< std::shared_ptr< AtomMatrix > > & tangent,
const std::vector< std::shared_ptr< EigenmodeStrategy > > & eigenmode_solvers,
long numImages,
bool estimateEigenvalues,
bool writeToFile,
size_t idx,
eonc::log::Scoped log )

Print NEB image data to log and optionally to file.

Definition at line 166 of file NEBSplineExtrema.cpp.

171 {
172
173 double dist, distTotal = 0;
174 AtomMatrix tangentStart =
175 path[0]->pbc(path[1]->getPositions() - path[0]->getPositions());
176 AtomMatrix tangentEnd = path[numImages]->pbc(
177 path[numImages + 1]->getPositions() - path[numImages]->getPositions());
178 AtomMatrix tang;
179 std::string header;
180 if (estimateEigenvalues) {
181 header = std::format("{:>3s} {:>12s} {:>12s} {:>12s} {:>12s}", "img",
182 "rxn_coord", "energy", "f_para", "eigval");
183 } else {
184 header = std::format("{:>3s} {:>12s} {:>12s} {:>12s}", "img", "rxn_coord",
185 "energy", "f_para");
186 }
187
188 tangentStart.normalize();
189 tangentEnd.normalize();
190
191 std::ofstream fileLogger;
192 if (writeToFile) {
193 std::string neb_dat_fs;
194 if (idx == std::numeric_limits<size_t>::max()) {
195 neb_dat_fs = "neb.dat";
196 } else {
197 neb_dat_fs = std::format("neb_{:03}.dat", idx);
198 }
199 if (fs::exists(neb_dat_fs)) {
200 fs::remove(neb_dat_fs);
201 }
202 fileLogger.open(neb_dat_fs);
203 if (fileLogger.is_open()) {
204 fileLogger << header << "\n";
205 }
206 }
207 const double energy_reactant = path[0]->getPotentialEnergy();
208
209 for (long i = 0; i <= numImages + 1; i++) {
210 if (i == 0) {
211 tang = tangentStart;
212 } else if (i == numImages + 1) {
213 tang = tangentEnd;
214 } else {
215 tang = *tangent[i];
216 }
217
218 if (i > 0) {
219 dist = path[i]->distanceTo(*path[i - 1]);
220 distTotal += dist;
221 }
222
223 double relative_energy = path[i]->getPotentialEnergy() - energy_reactant;
224 double parallel_force = matDot(path[i]->getForces(), tang);
225
226 if (estimateEigenvalues) {
227 eonc::eigenmodeCompute(*eigenmode_solvers[i], path[i], tang);
228 double lowest_eigenvalue =
229 eonc::eigenmodeGetEigenvalue(*eigenmode_solvers[i]);
230 if (fileLogger.is_open()) {
231 fileLogger << std::format(
232 "{:>3} {:>12.6f} {:>12.6f} {:>12.6f} {:>12.6f}\n", i, distTotal,
233 relative_energy, parallel_force, lowest_eigenvalue);
234 } else {
235 QUILL_LOG_DEBUG(log, "{:>3} {:>12.6f} {:>12.6f} {:>12.6f} {:>12.6f}", i,
236 distTotal, relative_energy, parallel_force,
237 lowest_eigenvalue);
238 }
239 } else {
240 if (fileLogger.is_open()) {
241 fileLogger << std::format("{:>3} {:>12.6f} {:>12.6f} {:>12.6f}\n", i,
242 distTotal, relative_energy, parallel_force);
243 } else {
244 QUILL_LOG_DEBUG(log, "{:>3} {:>12.6f} {:>12.6f} {:>12.6f}", i,
245 distTotal, relative_energy, parallel_force);
246 }
247 }
248 }
249}
void eigenmodeCompute(EigenmodeStrategy &s, std::shared_ptr< Matter > matter, AtomMatrix direction)
Dispatch compute() to the active variant.
double eigenmodeGetEigenvalue(EigenmodeStrategy &s)
Dispatch getEigenvalue() to the active variant.

◆ writePathCon()

eonc::io::IoStatus eonc::neb::writePathCon ( const std::vector< std::shared_ptr< Matter > > & path,
const std::vector< std::shared_ptr< AtomMatrix > > & tangent,
const std::vector< std::shared_ptr< EigenmodeStrategy > > & eigenmode_solvers,
long numImages,
bool estimateEigenvalues,
std::string filename,
std::optional< size_t > bandIndex )
nodiscard

Write a NEB band as a multi-frame .con via readcon ConFrameBuilder::clone().

Definition at line 279 of file NEBSplineExtrema.cpp.

284 {
285 auto frames = pathToConFrames(path, tangent, eigenmode_solvers, numImages,
286 estimateEigenvalues, bandIndex);
287 if (frames.empty()) {
289 }
290 return eonc::io::writeConFrames(std::move(filename), frames);
291}
IoStatus writeConFrames(std::string filename, const std::vector< readcon::ConFrame > &frames)
Write already-built ConFrames to a multi-frame .con (temp or durable).
std::vector< readcon::ConFrame > pathToConFrames(const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, const std::vector< std::shared_ptr< EigenmodeStrategy > > &eigenmode_solvers, long numImages, bool estimateEigenvalues, std::optional< size_t > bandIndex)
Build stamped ConFrames for a NEB band (same metadata as writePathCon).

◆ zeroTranslation()

void eonc::neb::zeroTranslation ( AtomMatrix & projectedForce,
int nFreeAtoms,
int nAtoms )

Zero net translational force for fully free systems.

Definition at line 103 of file NEBForceProjection.cpp.

103 {
104 if (nFreeAtoms == nAtoms) {
105 for (int j = 0; j <= 2; j++) {
106 double translationMag = projectedForce.col(j).sum();
107 int natoms = projectedForce.col(j).size();
108 projectedForce.col(j).array() -=
109 translationMag / static_cast<double>(natoms);
110 }
111 }
112}