211 {
212 long effectiveMaxIter = max_iterations_override;
213 QUILL_LOG_DEBUG(
214 log,
"Saddle point search started from reactant with energy {} eV.",
216
217 int optStatus;
218 bool firstIteration = true;
219 const char *forceLabel =
220 params.optimizer_options.convergence_metric_label.c_str();
221
222 if (
params.saddle_search_options.minmode_method ==
224 QUILL_LOG_DEBUG(
225 log,
"================= Using the GP Dimer Library =================");
228 QUILL_LOG_DEBUG(
log,
"GPR eigenvalue: {}",
231 }
233 QUILL_LOG_DEBUG(
log,
"[MinModeSaddleSearch] eigenvalue not negative");
235 }
237 params.saddle_search_options.zero_mode_abort_curvature) {
238 QUILL_LOG_DEBUG(
log,
"Zero mode eigenvalue: {}",
241 }
244 } else {
245
246 if (
params.saddle_search_options.minmode_method ==
249 "[Dimer] {:9s} {:9s} {:10s} {:18s} {:9s} "
250 "{:7s} {:6s} {:4s} {:5s}\n",
251 "Step", "Step Size", "Delta E", forceLabel, "Curvature",
252 "Torque", "Angle", "Rots", "Align");
253 }
else if (
params.saddle_search_options.minmode_method ==
255 QUILL_LOG_INFO(
257 "[Lanczos] {:9s} {:9s} {:10s} {:18s} {:9s} {:10s} {:7s} {:5s}\n",
258 "Step", "Step Size", "Delta E", forceLabel, "Curvature", "Rel Change",
259 "Angle", "Iters");
260 }
else if (
params.saddle_search_options.minmode_method ==
263 "[GPRDimer] {:9s} {:9s} {:10s} {:18s} {:9s} "
264 " {:7s} {:6s} {:4s}\n",
265 "Step", "Step Size", "Delta E", forceLabel, "Curvature",
266 "Torque", "Angle", "Rots");
267 }
268
269 std::string climbLabel = "climb";
270 std::string climbDatFilename = "climb.dat";
271
273
274 auto objf = std::make_shared<MinModeObjectiveFunction>(
276 auto write_climb_frame = [&](uint64_t frameIndex, bool append,
277 double stepSize, double de, double conv,
278 double eigenval, double torque, double angle,
279 long rotations) {
280 eonc::io::ConFrameMetadata metadata;
283 metadata.
scalars.push_back({
"step_size", stepSize});
284 metadata.
scalars.push_back({
"delta_e", de});
285 metadata.
scalars.push_back({
"convergence", conv});
286 metadata.
scalars.push_back({
"eigenvalue", eigenval});
287 metadata.
scalars.push_back({
"torque", torque});
288 metadata.
scalars.push_back({
"angle", angle});
289 metadata.
scalars.push_back({
"rotations",
static_cast<double>(rotations)});
292 }
293 if (
params.debug_options.write_movies) {
295 matter->matter2con(climbLabel, append, &metadata))) {
296 QUILL_LOG_WARNING(
log,
"Failed to write climb movie frame {}",
297 climbLabel);
298 }
299 }
300
301 if (
params.debug_options.write_deprecated_outs) {
302 std::ofstream climbDat(climbDatFilename,
303 append ? (std::ios::binary | std::ios::app)
304 : std::ios::binary);
305 if (climbDat) {
306 if (!append) {
307 climbDat << "iteration\tstep_size\tdelta_e\tconvergence"
308 "\teigenvalue\ttorque\tangle\trotations\n";
309 }
310 climbDat << std::format("{}\t{:.7e}\t{:.6f}\t{:.5e}\t{:.6f}"
311 "\t{:.6f}\t{:.4f}\t{}\n",
312 frameIndex, stepSize, de, conv, eigenval,
313 torque, angle, rotations);
314 }
315 }
316 };
318 write_climb_frame(0, false, 0.0, 0.0, objf->getConvergence(),
320 0);
321 }
322 if (
params.saddle_search_options.nonnegative_displacement_abort) {
323 objf->getGradient();
325 QUILL_LOG_DEBUG(
log,
"Nonnegative eigenvalue: {}",
328 }
329 }
330
333
334 while (!objf->isConverged() ||
iteration == 0) {
335
336 if (!firstIteration) {
337
338 if (
params.saddle_search_options.nonlocal_count_abort != 0) {
340 initialPosition -
matter->getPositions(),
341 params.saddle_search_options.nonlocal_distance_abort);
342 if (nm >=
params.saddle_search_options.nonlocal_count_abort) {
344 break;
345 }
346 }
347
349 params.saddle_search_options.zero_mode_abort_curvature) {
350 QUILL_LOG_DEBUG(
log,
"Zero mode eigenvalue: {}",
353 break;
354 }
355 }
356 firstIteration = false;
357
360 break;
361 }
362
364
365 try {
366 if (
params.saddle_search_options.confine_positive.bowl_breakout &&
369 optStatus = optim->step(-
params.optimizer_options.max_move);
370 } else {
371 optStatus = optim->step(
params.optimizer_options.max_move);
372 }
373 } catch (const eonc::DimerModeRestoredException &) {
374 QUILL_LOG_DEBUG(
375 log,
"Dimer restored to best state. Checking convergence...");
377 break;
378 } catch (const eonc::DimerModeLostException &) {
379 QUILL_LOG_WARNING(
log,
"Dimer lost mode completely. Aborting.");
381 break;
382 }
383
384 if (optStatus < 0) {
386 break;
387 }
388
390
391
392 if (
params.saddle_search_options.remove_rotation) {
394 }
395 double stepSize = (
matter->pbc(
matter->getPositions() - pos)).norm();
396
398
399
404 double conv = objf->getConvergence();
405
406 if (
params.saddle_search_options.minmode_method ==
408 QUILL_LOG_DEBUG(
410 "[Lanczos] {:9} {:9.6f} {:10.4f} {:18.5e} {:9.4f} {:10.6f} "
411 "{:7.3f} {:5}\n",
412 iteration, stepSize, de, conv, eigenval, torque, angle, rotations);
413 } else {
414 QUILL_LOG_DEBUG(
416 "[Dimer] {:9} {:9.7f} {:10.4f} {:18.5e} {:9.4f} {:7.3f} "
417 " {:6.3f} {:4}\n",
418 iteration, stepSize, de, conv, eigenval, torque, angle, rotations);
419 }
420
422 write_climb_frame(
static_cast<uint64_t
>(
iteration),
true, stepSize, de,
423 conv, eigenval, torque, angle, rotations);
424 }
425
426 if (
params.main_options.checkpoint) {
428 matter->matter2con(
"displacement_cp.con",
false))) {
429 QUILL_LOG_WARNING(
log,
"Failed to write displacement_cp.con");
430 }
433 }
434
435 if (de >
params.saddle_search_options.max_energy) {
437 break;
438 }
439
440
442 if (!dimer->rotationDidConverge) {
446 QUILL_LOG_DEBUG(
log,
"Dimer restored to valid state. C_tau={:.4f}",
447 dimer->getEigenvalue());
448 }
449 break;
450 }
451 }
452 }
453
456 }
457
458
459
460 const bool climbConverged = objf->isConverged();
461 const int statusBeforeGuard =
status;
464 QUILL_LOG_WARNING(
log,
"[MinModeSaddleSearch] objective not converged; "
465 "refusing STATUS_GOOD");
466 }
467
469 QUILL_LOG_DEBUG(
log,
"[MinModeSaddleSearch] eigenvalue not negative");
471 }
472 }
473
475}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
bool retain_climb_frames_
static const char MINMODE_DIMER[]
static const char MINMODE_GPRDIMER[]
static const char MINMODE_LANCZOS[]
static constexpr int finalizeClimbStatus(int climbStatus, bool objectiveConverged) noexcept
Issue #20 policy: never leave climb as STATUS_GOOD when the climb objective is still unconverged (unf...
std::unique_ptr< Optimizer > mkOptim(std::shared_ptr< ObjectiveFunction > a_objf, OptType a_otype, const Parameters &a_params)
long numAtomsMoved(const AtomMatrix v1, double cutoff)
void saveMode(FILE *modeFile, std::shared_ptr< Matter > matter, AtomMatrix mode)
Write a mode; constrained axes are emitted as 0.
void rotationRemove(const AtomMatrix r1, std::shared_ptr< Matter > m2)
readcon::ConFrame matterToConFrame(Matter &m, const ConFrameMetadata *metadata)
Build a single stamped ConFrame from Matter (same builder as matter2con).
constexpr bool io_ok(IoStatus s) noexcept
double eigenmodeStatsAngle(EigenmodeStrategy &s)
double eigenmodeStatsTorque(EigenmodeStrategy &s)
void eigenmodeCompute(EigenmodeStrategy &s, std::shared_ptr< Matter > matter, AtomMatrix direction)
Dispatch compute() to the active variant.
long eigenmodeStatsRotations(EigenmodeStrategy &s)
long eigenmodeTotalForceCalls(EigenmodeStrategy &s)
Read stats from any variant (all inherit LowestEigenmode stats fields).
long eigenmodeTotalIterations(EigenmodeStrategy &s)