Loading...
Searching...
No Matches
MinModeSaddleSearch Class Reference

#include <MinModeSaddleSearch.h>

Inheritance diagram for MinModeSaddleSearch:

Public Types

enum  Status : int {
  STATUS_GOOD , STATUS_INIT , STATUS_BAD_NO_CONVEX , STATUS_BAD_HIGH_ENERGY ,
  STATUS_BAD_MAX_CONCAVE_ITERATIONS , STATUS_BAD_MAX_ITERATIONS , STATUS_BAD_NOT_CONNECTED , STATUS_BAD_PREFACTOR ,
  STATUS_BAD_HIGH_BARRIER , STATUS_BAD_MINIMA , STATUS_FAILED_PREFACTOR , STATUS_POTENTIAL_FAILED ,
  STATUS_NONNEGATIVE_ABORT , STATUS_NONLOCAL_ABORT , STATUS_NEGATIVE_BARRIER , STATUS_BAD_MD_TRAJECTORY_TOO_SHORT ,
  STATUS_BAD_NO_NEGATIVE_MODE_AT_SADDLE , STATUS_BAD_NO_BARRIER , STATUS_ZEROMODE_ABORT , STATUS_OPTIMIZER_ERROR ,
  STATUS_DIMER_LOST_MODE , STATUS_DIMER_RESTORED_BEST , STATUS_KNOWN_SADDLE = 23
}

Public Member Functions

 MinModeSaddleSearch (std::shared_ptr< Matter > matterPassed, AtomMatrix modePassed, double reactantEnergyPassed, const Parameters &parametersPassed, std::shared_ptr< Potential > potPassed)
 MinModeSaddleSearch (const MinModeSaddleSearch &)=delete
 MinModeSaddleSearch (MinModeSaddleSearch &&) noexcept=default
 ~MinModeSaddleSearch ()=default
MinModeSaddleSearch & operator= (const MinModeSaddleSearch &)=delete
MinModeSaddleSearch & operator= (MinModeSaddleSearch &&) noexcept=default
AtomMatrix getEigenvector ()
double getEigenvalue ()
std::string_view describeStatus (int status) const override
int getStatus () const override
int getIterationCount () const override
int getForceCalls () const override
int run () override
int run (long max_iterations_override)
int runRetainFrames (long max_iterations_override=-1)
 Like run(), but also retain climb ConFrames in memory (same stamps as write_movies climb CON).
const std::vector< readcon::ConFrame > & climbFrames () const
void clearClimbFrames ()
void setKnownSaddles (std::vector< AtomMatrix > positions, std::vector< double > energies)
 Saddles the server already holds for this state (positions, energies).
std::vector< readcon::ConFrame > takeClimbFrames ()
Public Member Functions inherited from eonc::SaddleSearchMethod
 SaddleSearchMethod (std::shared_ptr< Potential > potPassed, const Parameters &paramsPassed)
virtual ~SaddleSearchMethod ()

Static Public Member Functions

static constexpr std::string_view statusMessage (int status)
 Human-readable message for a status code.
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 (unfeasible systems must not look like success).

Public Attributes

int forcecalls {0}
int iteration {0}
int status {0}

Private Member Functions

bool atKnownSaddle () const

Private Attributes

std::vector< readcon::ConFrame > climb_frames_
bool retain_climb_frames_ {false}
AtomMatrix mode
AtomMatrix initialTangent_
std::shared_ptr< Matter > matter
std::shared_ptr< EigenmodeStrategy > minModeMethod
double reactantEnergy
std::vector< AtomMatrix > knownSaddlePositions_
std::vector< double > knownSaddleEnergies_
eonc::log::Scoped log

Additional Inherited Members

Protected Attributes inherited from eonc::SaddleSearchMethod
std::shared_ptr< Potential > pot
const Parameters & params

Detailed Description

Definition at line 26 of file MinModeSaddleSearch.h.

Member Enumeration Documentation

◆ Status

Enumerator
STATUS_GOOD 
STATUS_INIT 
STATUS_BAD_NO_CONVEX 
STATUS_BAD_HIGH_ENERGY 
STATUS_BAD_MAX_CONCAVE_ITERATIONS 
STATUS_BAD_MAX_ITERATIONS 
STATUS_BAD_NOT_CONNECTED 
STATUS_BAD_PREFACTOR 
STATUS_BAD_HIGH_BARRIER 
STATUS_BAD_MINIMA 
STATUS_FAILED_PREFACTOR 
STATUS_POTENTIAL_FAILED 
STATUS_NONNEGATIVE_ABORT 
STATUS_NONLOCAL_ABORT 
STATUS_NEGATIVE_BARRIER 
STATUS_BAD_MD_TRAJECTORY_TOO_SHORT 
STATUS_BAD_NO_NEGATIVE_MODE_AT_SADDLE 
STATUS_BAD_NO_BARRIER 
STATUS_ZEROMODE_ABORT 
STATUS_OPTIMIZER_ERROR 
STATUS_DIMER_LOST_MODE 
STATUS_DIMER_RESTORED_BEST 
STATUS_KNOWN_SADDLE 

Definition at line 29 of file MinModeSaddleSearch.h.

29 : int {
30 // DO NOT CHANGE THE ORDER OF THIS LIST
31 STATUS_GOOD, // 0
32 STATUS_INIT, // 1
53 // 22 is ARTnSaddleSearch::STATUS_BAD_ARTN_ERROR
55 };

Constructor & Destructor Documentation

◆ MinModeSaddleSearch() [1/3]

eonc::MinModeSaddleSearch::MinModeSaddleSearch ( std::shared_ptr< Matter > matterPassed,
AtomMatrix modePassed,
double reactantEnergyPassed,
const Parameters & parametersPassed,
std::shared_ptr< Potential > potPassed )

Definition at line 318 of file MinModeSaddleSearch.cpp.

323 : SaddleSearchMethod(potPassed, parametersPassed),
324 matter{matterPassed} {
326 params.optimizer_options().convergence_metric, "[MinModeSaddleSearch]");
327 reactantEnergy = reactantEnergyPassed;
328 mode = modePassed;
329 initialTangent_ = modePassed;
331 iteration = 0;
332
334
335 // Set reference mode for ImprovedDimer (prevents mode switching)
336 if (auto *dimer = eonc::asImprovedDimer(*minModeMethod)) {
337 VectorXd refVec = VectorXd::Map(mode.data(), 3 * matter->numberOfAtoms());
338 refVec = refVec.array() * matter->getFreeV().array();
339 dimer->setReferenceMode(refVec);
340 }
341}
std::shared_ptr< Matter > matter
std::shared_ptr< EigenmodeStrategy > minModeMethod
std::shared_ptr< Potential > pot
SaddleSearchMethod(std::shared_ptr< Potential > potPassed, const Parameters &paramsPassed)
void requireKnownConvergenceMetric(std::string_view metric, std::string_view context)
Throws std::invalid_argument naming context when metric is unrecognized.
std::shared_ptr< LowestEigenmode > buildEigenmodeStrategy(std::shared_ptr< Matter > matter, const Parameters &params, std::shared_ptr< Potential > pot)
ImprovedDimer * asImprovedDimer(LowestEigenmode &s)

◆ MinModeSaddleSearch() [2/3]

◆ MinModeSaddleSearch() [3/3]

◆ ~MinModeSaddleSearch()

Member Function Documentation

◆ atKnownSaddle()

bool eonc::MinModeSaddleSearch::atKnownSaddle ( ) const
nodiscardprivate

Definition at line 755 of file MinModeSaddleSearch.cpp.

755 {
756 if (knownSaddleEnergies_.empty() ||
758 return false;
759 }
760 const double energy = matter->getPotentialEnergy();
761 const double eps = params.structure_comparison_options().energy_difference;
762 for (size_t k = 0; k < knownSaddleEnergies_.size(); ++k) {
763 if (std::abs(energy - knownSaddleEnergies_[k]) > eps) {
764 continue;
765 }
766 Matter known(*matter);
767 known.setPositions(knownSaddlePositions_[k]);
768 if (known.compare(*matter)) {
769 return true;
770 }
771 }
772 return false;
773}
std::vector< double > knownSaddleEnergies_
std::vector< AtomMatrix > knownSaddlePositions_
constexpr double eps
Definition SafeMath.h:19
double eigenmodeGetEigenvalue(LowestEigenmode &s)

◆ clearClimbFrames()

Definition at line 134 of file MinModeSaddleSearch.h.

134{ climb_frames_.clear(); }
std::vector< readcon::ConFrame > climb_frames_

◆ climbFrames()

const std::vector< readcon::ConFrame > & eonc::MinModeSaddleSearch::climbFrames ( ) const
inlinenodiscard

Definition at line 131 of file MinModeSaddleSearch.h.

131 {
132 return climb_frames_;
133 }

◆ describeStatus()

std::string_view eonc::MinModeSaddleSearch::describeStatus ( int status) const
inlineoverridevirtual

Implements eonc::SaddleSearchMethod.

Definition at line 114 of file MinModeSaddleSearch.h.

114 {
115 return statusMessage(status);
116 }
static constexpr std::string_view statusMessage(int status)
Human-readable message for a status code.

◆ finalizeClimbStatus()

constexpr int eonc::MinModeSaddleSearch::finalizeClimbStatus ( int climbStatus,
bool objectiveConverged )
inlinestaticnodiscardconstexprnoexcept

Issue #20 policy: never leave climb as STATUS_GOOD when the climb objective is still unconverged (unfeasible systems must not look like success).

Used by run(); unit-tested so deleting this rule fails CI.

Definition at line 95 of file MinModeSaddleSearch.h.

95 {
96 if (climbStatus == STATUS_GOOD && !objectiveConverged) {
98 }
99 return climbStatus;
100 }

◆ getEigenvalue()

Implements eonc::SaddleSearchMethod.

Definition at line 775 of file MinModeSaddleSearch.cpp.

775 {
777}

◆ getEigenvector()

Implements eonc::SaddleSearchMethod.

Definition at line 779 of file MinModeSaddleSearch.cpp.

779 {
781}
AtomMatrix eigenmodeGetEigenvector(LowestEigenmode &s)

◆ getForceCalls()

int eonc::MinModeSaddleSearch::getForceCalls ( ) const
inlineoverridevirtual

Reimplemented from eonc::SaddleSearchMethod.

Definition at line 119 of file MinModeSaddleSearch.h.

◆ getIterationCount()

int eonc::MinModeSaddleSearch::getIterationCount ( ) const
inlineoverridevirtual

Reimplemented from eonc::SaddleSearchMethod.

Definition at line 118 of file MinModeSaddleSearch.h.

118{ return iteration; }

◆ getStatus()

int eonc::MinModeSaddleSearch::getStatus ( ) const
inlineoverridevirtual

Reimplemented from eonc::SaddleSearchMethod.

Definition at line 117 of file MinModeSaddleSearch.h.

117{ return status; }

◆ operator=() [1/2]

MinModeSaddleSearch & eonc::MinModeSaddleSearch::operator= ( const MinModeSaddleSearch & )
delete

◆ operator=() [2/2]

MinModeSaddleSearch & eonc::MinModeSaddleSearch::operator= ( MinModeSaddleSearch && )
defaultnoexcept

◆ run() [1/2]

int eonc::MinModeSaddleSearch::run ( void )
overridevirtual

Implements eonc::SaddleSearchMethod.

Definition at line 343 of file MinModeSaddleSearch.cpp.

343 {
344 return run(params.saddle_search_options().max_iterations);
345}

◆ run() [2/2]

int eonc::MinModeSaddleSearch::run ( long max_iterations_override)

Definition at line 358 of file MinModeSaddleSearch.cpp.

358 {
359 long effectiveMaxIter = max_iterations_override;
360 QUILL_LOG_DEBUG(
361 log, "Saddle point search started from reactant with energy {} eV.",
363
364 int optStatus;
365 bool firstIteration = true;
366 const char *forceLabel =
367 params.optimizer_options().convergence_metric_label.c_str();
368
369 if (params.saddle_search_options().minmode_method ==
370 LowestEigenmode::MINMODE_GPRDIMER) {
371 QUILL_LOG_DEBUG(
372 log, "================= Using the GP Dimer Library =================");
375 QUILL_LOG_DEBUG(log, "GPR eigenvalue: {}",
378 return status;
379 }
380 if (getEigenvalue() > 0.0 && status == STATUS_GOOD) {
381 QUILL_LOG_DEBUG(log, "[MinModeSaddleSearch] eigenvalue not negative");
383 }
385 params.saddle_search_options().zero_mode_abort_curvature) {
386 QUILL_LOG_DEBUG(log, "Zero mode eigenvalue: {}",
389 }
392 } else {
393
394 if (params.saddle_search_options().minmode_method ==
395 LowestEigenmode::MINMODE_DIMER) {
396 QUILL_LOG_INFO(log,
397 "[Dimer] {:9s} {:9s} {:10s} {:18s} {:9s} "
398 "{:7s} {:6s} {:4s} {:5s}\n",
399 "Step", "Step Size", "Delta E", forceLabel, "Curvature",
400 "Torque", "Angle", "Rots", "Align");
401 } else if (params.saddle_search_options().minmode_method ==
402 LowestEigenmode::MINMODE_LANCZOS) {
403 QUILL_LOG_INFO(
404 log,
405 "[Lanczos] {:9s} {:9s} {:10s} {:18s} {:9s} {:10s} {:7s} {:5s}\n",
406 "Step", "Step Size", "Delta E", forceLabel, "Curvature", "Rel Change",
407 "Angle", "Iters");
408 } else if (params.saddle_search_options().minmode_method ==
409 LowestEigenmode::MINMODE_GPRDIMER) {
410 QUILL_LOG_INFO(log,
411 "[GPRDimer] {:9s} {:9s} {:10s} {:18s} {:9s} "
412 " {:7s} {:6s} {:4s}\n",
413 "Step", "Step Size", "Delta E", forceLabel, "Curvature",
414 "Torque", "Angle", "Rots");
415 }
416
417 std::string climbLabel = "climb";
418 std::string climbDatFilename = "climb.dat";
419
420 AtomMatrix initialPosition = matter->getPositions();
421
422 auto objf = std::make_shared<MinModeObjectiveFunction>(
424 auto write_climb_frame = [&](uint64_t frameIndex, bool append,
425 double stepSize, double de, double conv,
426 double eigenval, double torque, double angle,
427 long rotations) {
428 eonc::io::ConFrameMetadata metadata;
429 metadata.frame_index = frameIndex;
430 metadata.energy = matter->getPotentialEnergy();
431 metadata.scalars.push_back({"step_size", stepSize});
432 metadata.scalars.push_back({"delta_e", de});
433 metadata.scalars.push_back({"convergence", conv});
434 metadata.scalars.push_back({"eigenvalue", eigenval});
435 metadata.scalars.push_back({"torque", torque});
436 metadata.scalars.push_back({"angle", angle});
437 metadata.scalars.push_back({"rotations", static_cast<double>(rotations)});
439 climb_frames_.push_back(eonc::io::matterToConFrame(*matter, &metadata));
440 }
441 if (params.debug_options().write_movies) {
442 if (!eonc::io::io_ok(
443 matter->matter2con(climbLabel, append, &metadata))) {
444 QUILL_LOG_WARNING(log, "Failed to write climb movie frame {}",
445 climbLabel);
446 }
447 eonc::helpers::saveMode(std::format("mode_{:03}.dat", frameIndex),
448 matter,
450 }
451
452 if (params.debug_options().write_deprecated_outs) {
453 std::ofstream climbDat(climbDatFilename,
454 append ? (std::ios::binary | std::ios::app)
455 : std::ios::binary);
456 if (climbDat) {
457 if (!append) {
458 climbDat << "iteration\tstep_size\tdelta_e\tconvergence"
459 "\teigenvalue\ttorque\tangle\trotations\n";
460 }
461 climbDat << std::format("{}\t{:.7e}\t{:.6f}\t{:.5e}\t{:.6f}"
462 "\t{:.6f}\t{:.4f}\t{}\n",
463 frameIndex, stepSize, de, conv, eigenval,
464 torque, angle, rotations);
465 }
466 }
467 };
468 if (params.debug_options().write_movies || retain_climb_frames_) {
469 write_climb_frame(0, false, 0.0, 0.0, objf->getConvergence(),
471 0);
472 }
473 if (params.saddle_search_options().nonnegative_displacement_abort) {
474 objf->getGradient();
476 QUILL_LOG_DEBUG(log, "Nonnegative eigenvalue: {}",
479 return status;
480 }
481 }
482
484 objf, params.optimizer_options().method, params);
485
486 // Stock max_move stays the cap until a translation crosses max_energy.
487 // A search that never crosses it takes the same steps as before.
488 const double stockMove = params.optimizer_options().max_move;
489 double translationCap = stockMove;
490 int wallStalls = 0;
491 int wallEvents = 0;
492 int reseeds = 0;
493 int probeForceCalls = 0;
494 auto probedEnergy = [&]() {
495 const long beforeCalls = matter->getForceCalls();
496 const double energy = objf->getEnergy();
497 probeForceCalls +=
498 static_cast<int>(matter->getForceCalls() - beforeCalls);
499 return energy;
500 };
501 auto pullUnderEnergy = [&](const VectorXd &before) -> bool {
502 const double limit = params.saddle_search_options().max_energy;
503 const VectorXd after = objf->getPositions();
504 const VectorXd delta = objf->difference(after, before);
505 double alpha = 0.5;
506 bool found = false;
507 for (int probe = 0; probe < kMaxEnergyHalvings; ++probe) {
508 if (eonc::geometry::maxAtomMotionV(alpha * delta) <
509 kMaxEnergyMotionFloor) {
510 break;
511 }
512 objf->setPositions(before + alpha * delta);
513 if (probedEnergy() - reactantEnergy <= limit) {
514 found = true;
515 break;
516 }
517 alpha *= 0.5;
518 }
519 auto freshMode = [&]() {
521 if (reseeds % 2 == 1) {
522 seed = -seed;
523 }
524 objf->reseedMode(seed);
525 ++reseeds;
526 };
527 if (found) {
528 wallStalls = 0;
529 ++wallEvents;
530 const double accepted = eonc::geometry::maxAtomMotionV(alpha * delta);
531 translationCap =
532 std::min(stockMove, std::max(accepted, kMaxEnergyStepFloor));
533 if (alpha < kMaxEnergyReseedAlpha) {
534 freshMode();
535 QUILL_LOG_DEBUG(
536 log,
537 "[MinMode] energy above max_energy {:.4f}, shortened "
538 "the translation to {:.3e} and re-initialised the mode",
539 limit, accepted);
540 } else {
541 QUILL_LOG_DEBUG(log,
542 "[MinMode] energy above max_energy {:.4f}, shortened "
543 "the translation to {:.3e}",
544 limit, accepted);
545 }
546 if (wallEvents >= kMaxEnergyWallEvents) {
547 QUILL_LOG_DEBUG(log,
548 "[MinMode] {} crossings of max_energy {:.4f}, "
549 "ending the search",
550 wallEvents, limit);
551 return false;
552 }
553 return true;
554 }
555 objf->setPositions(before);
556 if (probedEnergy() - reactantEnergy > limit) {
557 return false;
558 }
559 ++wallEvents;
560 ++wallStalls;
561 translationCap = std::min(
562 stockMove, std::max(translationCap * 0.5, kMaxEnergyStepFloor));
563 freshMode();
564 QUILL_LOG_DEBUG(log,
565 "[MinMode] translation stays above max_energy {:.4f}, "
566 "restored the centre and re-initialised the mode",
567 limit);
568 if (wallEvents >= kMaxEnergyWallEvents) {
569 QUILL_LOG_DEBUG(log,
570 "[MinMode] {} crossings of max_energy {:.4f}, "
571 "ending the search",
572 wallEvents, limit);
573 return false;
574 }
575 return wallStalls < kMaxEnergyStalls;
576 };
577
578 while (!objf->isConverged() || iteration == 0) {
579 matter->pollCancel("saddle");
580
581 if (!firstIteration) {
582
583 if (params.saddle_search_options().nonlocal_count_abort != 0) {
585 initialPosition - matter->getPositions(),
586 params.saddle_search_options().nonlocal_distance_abort);
587 if (nm >= params.saddle_search_options().nonlocal_count_abort) {
589 break;
590 }
591 }
592
594 params.saddle_search_options().zero_mode_abort_curvature) {
595 QUILL_LOG_DEBUG(log, "Zero mode eigenvalue: {}",
598 break;
599 }
600 }
601 firstIteration = false;
602
603 if (iteration >= effectiveMaxIter) {
605 break;
606 }
607
608 const VectorXd posBefore = objf->getPositions();
609 AtomMatrix pos = matter->getPositions();
610
611 try {
612 if (params.saddle_search_options().confine_positive.bowl_breakout &&
614 params.optimizer_options().method == OptType::CG) {
615 optStatus = optim->step(-translationCap);
616 } else {
617 optStatus = optim->step(translationCap);
618 }
619 } catch (const eonc::DimerModeRestoredException &) {
620 QUILL_LOG_DEBUG(
621 log, "Dimer restored to best state. Checking convergence...");
622 status = objf->isConverged() ? STATUS_GOOD : STATUS_DIMER_RESTORED_BEST;
623 break;
624 } catch (const eonc::DimerModeLostException &) {
625 QUILL_LOG_WARNING(log, "Dimer lost mode completely. Aborting.");
627 break;
628 }
629
630 if (optStatus < 0) {
632 break;
633 }
634
635 double de = objf->getEnergy() - reactantEnergy;
636 const double energyLimit = params.saddle_search_options().max_energy;
637 if (de > energyLimit) {
638 const bool kept = pullUnderEnergy(posBefore);
639 if (kept) {
640 optim->onTranslationPulledBack();
641 }
642 if (!kept || objf->getEnergy() - reactantEnergy > energyLimit) {
644 // Count the attempt so a first-step abort does not fall through
645 // to the iteration == 0 mode estimate below.
646 ++iteration;
647 break;
648 }
649 de = objf->getEnergy() - reactantEnergy;
650 } else if (translationCap < stockMove) {
651 translationCap =
652 std::min(stockMove, translationCap * kMaxEnergyCapRegrowth);
653 wallStalls = 0;
654 }
655
656 // Melander, Laasonen, Jonsson, JCTC 11(3), 1055-1062, 2015
657 if (params.saddle_search_options().remove_rotation) {
659 }
660 double stepSize = (matter->pbc(matter->getPositions() - pos)).norm();
661
662 iteration++;
663
664 // Logging
669 double conv = objf->getConvergence();
670
671 if (params.saddle_search_options().minmode_method ==
672 LowestEigenmode::MINMODE_LANCZOS) {
673 QUILL_LOG_DEBUG(
674 log,
675 "[Lanczos] {:9} {:9.6f} {:10.4f} {:18.5e} {:9.4f} {:10.6f} "
676 "{:7.3f} {:5}\n",
677 iteration, stepSize, de, conv, eigenval, torque, angle, rotations);
678 } else {
679 QUILL_LOG_DEBUG(
680 log,
681 "[Dimer] {:9} {:9.7f} {:10.4f} {:18.5e} {:9.4f} {:7.3f} "
682 " {:6.3f} {:4}\n",
683 iteration, stepSize, de, conv, eigenval, torque, angle, rotations);
684 }
685
686 if (params.debug_options().write_movies || retain_climb_frames_) {
687 write_climb_frame(static_cast<uint64_t>(iteration), true, stepSize, de,
688 conv, eigenval, torque, angle, rotations);
689 }
690
691 if (params.main_options().checkpoint) {
692 if (!eonc::io::io_ok(
693 matter->matter2con("displacement_cp.con", false))) {
694 QUILL_LOG_WARNING(log, "Failed to write displacement_cp.con");
695 }
696 eonc::helpers::saveMode("mode_cp.dat", matter,
698 }
699
700 if (atKnownSaddle()) {
701 QUILL_LOG_DEBUG(log, "[MinModeSaddleSearch] reached a known saddle");
703 break;
704 }
705
706 // Check ImprovedDimer mode convergence
707 if (auto *dimer = eonc::asImprovedDimer(*minModeMethod)) {
708 if (!dimer->rotationDidConverge) {
709 status = (dimer->getEigenvalue() < 0.0) ? STATUS_DIMER_RESTORED_BEST
712 QUILL_LOG_DEBUG(log, "Dimer restored to valid state. C_tau={:.4f}",
713 dimer->getEigenvalue());
714 }
715 break;
716 }
717 }
718 }
719
720 if (iteration == 0) {
722 }
723
724 // Never report STATUS_GOOD when the climb objective is unconverged
725 // (issue #20: unfeasible systems must not look like success).
726 const bool climbConverged = objf->isConverged();
727 const int statusBeforeGuard = status;
728 status = finalizeClimbStatus(status, climbConverged);
729 if (statusBeforeGuard == STATUS_GOOD && status != STATUS_GOOD) {
730 QUILL_LOG_WARNING(log, "[MinModeSaddleSearch] objective not converged; "
731 "refusing STATUS_GOOD");
732 }
733
734 if (getEigenvalue() > 0.0 && status == STATUS_GOOD) {
735 QUILL_LOG_DEBUG(log, "[MinModeSaddleSearch] eigenvalue not negative");
737 }
738 forcecalls = static_cast<int>(
740 }
741
742 return status;
743}
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
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...
long numAtomsMoved(const AtomMatrix v1, double cutoff)
double maxAtomMotionV(const VectorXd v1)
void rotationRemove(const AtomMatrix r1, std::shared_ptr< Matter > m2)
std::unique_ptr< Optimizer > mkOptim(std::shared_ptr< ObjectiveFunction > a_objf, OptType a_otype, const Parameters &a_params)
Definition Optimizer.cpp:55
void saveMode(FILE *modeFile, std::shared_ptr< Matter > matter, AtomMatrix mode)
Write a mode; constrained axes are emitted as 0.
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
Definition ConFileIO.h:38
void eigenmodeCompute(LowestEigenmode &s, std::shared_ptr< Matter > matter, AtomMatrix direction)
long eigenmodeTotalForceCalls(LowestEigenmode &s)
long eigenmodeStatsRotations(LowestEigenmode &s)
long eigenmodeTotalIterations(LowestEigenmode &s)
double eigenmodeStatsTorque(LowestEigenmode &s)
double eigenmodeStatsAngle(LowestEigenmode &s)
std::optional< uint64_t > frame_index
Definition ConFileIO.h:72
std::vector< ConMetadataValue > scalars
Definition ConFileIO.h:79
std::optional< double > energy
Definition ConFileIO.h:73

◆ runRetainFrames()

int eonc::MinModeSaddleSearch::runRetainFrames ( long max_iterations_override = -1)

Like run(), but also retain climb ConFrames in memory (same stamps as write_movies climb CON).

Does not require write_movies on disk.

Definition at line 347 of file MinModeSaddleSearch.cpp.

347 {
349 climb_frames_.clear();
350 const long maxIter = max_iterations_override < 0
351 ? params.saddle_search_options().max_iterations
352 : max_iterations_override;
353 const int st = run(maxIter);
354 retain_climb_frames_ = false;
355 return st;
356}

◆ setKnownSaddles()

void eonc::MinModeSaddleSearch::setKnownSaddles ( std::vector< AtomMatrix > positions,
std::vector< double > energies )

Saddles the server already holds for this state (positions, energies).

A climb point with negative curvature that matches one under the structure comparison ends the search with STATUS_KNOWN_SADDLE.

Definition at line 745 of file MinModeSaddleSearch.cpp.

746 {
747 if (positions.size() != energies.size()) {
748 throw std::invalid_argument(
749 "MinModeSaddleSearch::setKnownSaddles: size mismatch");
750 }
751 knownSaddlePositions_ = std::move(positions);
752 knownSaddleEnergies_ = std::move(energies);
753}

◆ statusMessage()

constexpr std::string_view eonc::MinModeSaddleSearch::statusMessage ( int status)
inlinestaticconstexpr

Human-readable message for a status code.

Definition at line 58 of file MinModeSaddleSearch.h.

58 {
59 constexpr std::string_view msgs[] = {
60 "Success", // 0
61 "Initialized", // 1
62 "Initial displacement unable to reach convex region", // 2
63 "Barrier too high", // 3
64 "Too many iterations in concave region", // 4
65 "Too many iterations", // 5
66 "Saddle is not connected to initial state", // 6
67 "Prefactors not within window", // 7
68 "Energy barrier not within window", // 8
69 "Minimizations from saddle did not converge", // 9
70 "Hessian calculation failed", // 10
71 "Potential evaluation failed", // 11
72 "Nonnegative initial mode, aborting", // 12
73 "Nonlocal abort", // 13
74 "Negative barrier detected", // 14
75 "No reaction found during MD trajectory", // 15
76 "Converged to stationary point with zero negative modes", // 16
77 "No forward barrier found along minimized band", // 17
78 "Zero mode abort", // 18
79 "Optimizer error", // 19
80 "Dimer lost mode", // 20
81 "Dimer restored best", // 21
82 };
84 return "Climb reached a known saddle";
85 if (status >= 0 &&
86 status < static_cast<int>(sizeof(msgs) / sizeof(msgs[0])))
87 return msgs[status];
88 return "Unknown status";
89 }

◆ takeClimbFrames()

std::vector< readcon::ConFrame > eonc::MinModeSaddleSearch::takeClimbFrames ( )
inlinenodiscard

Definition at line 141 of file MinModeSaddleSearch.h.

141 {
142 return std::move(climb_frames_);
143 }

Member Data Documentation

◆ climb_frames_

std::vector<readcon::ConFrame> eonc::MinModeSaddleSearch::climb_frames_
private

Definition at line 146 of file MinModeSaddleSearch.h.

◆ forcecalls

Definition at line 127 of file MinModeSaddleSearch.h.

127{0};

◆ initialTangent_

◆ iteration

Definition at line 128 of file MinModeSaddleSearch.h.

128{0};

◆ knownSaddleEnergies_

Definition at line 155 of file MinModeSaddleSearch.h.

◆ knownSaddlePositions_

◆ log

◆ matter

std::shared_ptr<Matter> eonc::MinModeSaddleSearch::matter
private

Definition at line 150 of file MinModeSaddleSearch.h.

◆ minModeMethod

std::shared_ptr<EigenmodeStrategy> eonc::MinModeSaddleSearch::minModeMethod
private

Definition at line 152 of file MinModeSaddleSearch.h.

◆ mode

◆ reactantEnergy

Definition at line 153 of file MinModeSaddleSearch.h.

◆ retain_climb_frames_

Definition at line 147 of file MinModeSaddleSearch.h.

147{false};

◆ status

Definition at line 129 of file MinModeSaddleSearch.h.

129{0};

The documentation for this class was generated from the following files: