192 {
193 std::string resultsFilename("results.dat");
195
196 {
201 params.potential_options().potential,
203 env.job_type = "neb";
204 env.extras.emplace_back(
"force_calls_neb",
static_cast<double>(
fCallsNEB));
205 env.extras.emplace_back("energy_reference", neb->reactantEnergy);
206 env.extras.emplace_back("number_of_images",
207 static_cast<double>(neb->numImages));
208 env.writeResultsDat(resultsFilename);
209 std::ofstream out(resultsFilename, std::ios::binary | std::ios::app);
210 if (!out) {
211 QUILL_LOG_ERROR(
m_log,
"Failed to reopen {} for image keys",
212 resultsFilename);
213 return;
214 }
215
216 for (long i = 0; i <= neb->numImages + 1; i++) {
217 out << std::format(
218 "{:f} image{}_energy\n",
219 neb->path[i]->getPotentialEnergy() - neb->reactantEnergy, i);
220 out << std::format("{:f} image{}_force\n",
221 neb->path[i]->getForces().norm(), i);
222 double proj_norm = (i >= 1 && i <= neb->numImages)
223 ? neb->projectedForce[i]->norm()
224 : 0.0;
225 out << std::format("{:f} image{}_projected_force\n", proj_norm, i);
226 }
227
228 long safeNumExtrema = std::min(
229 neb->numExtrema, static_cast<long>(neb->extremumPosition.size()));
230 out << std::format("{} number_of_extrema\n", safeNumExtrema);
231 for (long i = 0; i < safeNumExtrema; i++) {
232 out << std::format("{:f} extremum{}_position\n", neb->extremumPosition[i],
233 i);
234 out << std::format("{:f} extremum{}_energy\n", neb->extremumEnergy[i], i);
235 }
236 }
237
238
239 std::string nebFilename("neb.con");
242 neb->path, neb->tangent, neb->eigenmode_solvers, neb->numImages,
243 params.debug_options().estimate_neb_eigenvalues, nebFilename,
244 std::nullopt, neb->reactantEnergy))) {
245 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", nebFilename);
246 }
247
248
249 std::string spFilename("sp.con");
251 neb->path[neb->maxEnergyImage]->matter2con(spFilename))) {
252 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", spFilename);
253 }
255
256
257 if (
params.neb_options().mmf_peaks.enabled && neb->numExtrema > 0) {
258 int peakCount = 0;
259 for (long i = 0; i < neb->numExtrema; i++) {
260
261
262
263 double relativeEnergy = neb->extremumEnergy[i] - neb->reactantEnergy;
264
265 if (neb->extremumCurvature[i] < 0 &&
266 relativeEnergy >
params.neb_options().mmf_peaks.tolerance) {
267 double posFraction = neb->extremumPosition[i];
268 int leftIdx = static_cast<int>(std::floor(posFraction));
269 double f = posFraction - leftIdx;
270
271 if (leftIdx < 0 || leftIdx >= neb->numImages + 1)
272 continue;
273
274
276 *neb->path[leftIdx], *neb->path[leftIdx + 1], f);
277 std::string peakPosFile = std::format("peak{:02d}_pos.con", peakCount);
279 QUILL_LOG_ERROR(
m_log,
"Failed to write {}", peakPosFile);
280 }
282
283
285 neb->path, neb->tangent, neb->numImages, posFraction);
286
287 std::string peakModeFile =
288 std::format("peak{:02d}_mode.dat", peakCount);
289 {
290 std::ofstream modeOut(peakModeFile);
291 if (modeOut) {
292 for (long row = 0; row < peakMode.rows(); ++row) {
293 modeOut << std::format("{:.17g} {:.17g} {:.17g}\n",
294 peakMode(row, 0), peakMode(row, 1),
295 peakMode(row, 2));
296 }
298 }
299 }
300
301 QUILL_LOG_INFO(
303 "Generated MMF peak {:02d} at position {:.3f} (Energy: {:.3f} eV)",
304 peakCount, posFraction, relativeEnergy);
305 peakCount++;
306 }
307 }
308 }
309
311 neb->printImageData(true, std::numeric_limits<size_t>::max());
312}
Matter interpolateImage(const Matter &A, const Matter &B, double fraction)
AtomMatrix interpolatedPeakMode(const std::vector< std::shared_ptr< Matter > > &path, const std::vector< std::shared_ptr< AtomMatrix > > &tangent, long numImages, double posFraction)
Unit tangent at a fractional image index.
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, double referenceEnergy)
Write a NEB band as a multi-frame .con via readcon ConFrameBuilder::clone().
static JobResultEnvelope fromMinimization(RunStatus status, PotType pot, std::uint64_t fcalls, bool hasE, double energy)