28bool batchForcesFinite(
long nAtoms,
const double *forces,
double energy) {
29 if (forces ==
nullptr || !std::isfinite(energy)) {
32 return Eigen::Map<const AtomMatrix>(forces, nAtoms, 3).allFinite();
35void rejectNonFiniteBatch(
long nAtoms,
const double *forces,
double energy) {
36 if (!batchForcesFinite(nAtoms, forces, energy)) {
37 throw std::runtime_error(
"Dimer::calcRotationalForceReturnCurvature: "
38 "non-finite batch forces");
45 std::shared_ptr<Potential>
pot)
49 (
pot->needsPerImageInstance() &&
params.main_options().parallel)
56 nAtoms = matter->numberOfAtoms();
69 eonc::safemath::safe_normalize_inplace(initialDirection);
76 static_cast<quill::Logger *
>(
log))) {
81 eonc::safemath::safe_normalize_inplace(
direction);
82 for (
long i = 0; i <
nAtoms; ++i) {
93 long forceCallsDimer =
matterDimer->getForceCalls();
94 double curvature = 0.0;
95 double rotationAngle = 0.0;
101 rotationalForce.setZero();
102 rotationalForceOld.setZero();
103 rotationalPlaneOld.setZero();
106 double lengthRotationalForceOld = 0.0;
109 bool doneRotating =
false;
110 while (!doneRotating) {
114 rotationalPlaneOld, lengthRotationalForceOld);
116 torque = rotationalForce.norm();
118 assert(std::isfinite(torque));
121 if ((torque >
params.dimer_options().torque_max &&
122 rotations >=
params.dimer_options().rotations_max) ||
123 (torque <
params.dimer_options().torque_max &&
124 torque >=
params.dimer_options().torque_min &&
125 rotations >=
params.dimer_options().rotations_min) ||
126 (torque <
params.dimer_options().torque_min)) {
139 double rotForceChange =
140 (rotForce1 - rotForce2) /
params.dimer_options().rotation_angle;
141 double forceDimer = (rotForce1 + rotForce2) / 2.0;
144 rotForceChange, 0.0) /
146 params.dimer_options().rotation_angle / 2.0;
148 if (rotForceChange < 0) {
157 "[DimerRot] ----- --------- ---------------- "
158 "--------- {:9.3e} {:9.3e} {:9.3e} ---------\n",
165 eonc::safemath::safe_normalize_inplace(
direction);
166 for (
long i = 0; i <
nAtoms; ++i) {
176 forceCallsCenter =
matterCenter->getForceCalls() - forceCallsCenter;
177 forceCallsDimer =
matterDimer->getForceCalls() - forceCallsDimer;
193 if (
params.dimer_options().remove_rotation) {
198 eonc::safemath::safe_normalize_inplace(
direction);
199 posDimer = posCenter +
direction *
params.main_options().finiteDifference;
205 if (
pot->supportsBatchEvaluation()) {
210 if (centerDirty && dimerDirty) {
216 const double *posVec[] = {
matterCenter->getPositions().data(),
218 const int *nrsVec[] = {nrs0.data(), nrs1.data()};
221 double energies[2], vars[2];
222 const double *boxVec[] = {box0.data(), box1.data()};
223 pot->forceBatch(2,
nAtoms, posVec, nrsVec, frcVec, energies, vars,
225 rejectNonFiniteBatch(
nAtoms, frcVec[0], energies[0]);
226 rejectNonFiniteBatch(
nAtoms, frcVec[1], energies[1]);
227 matterCenter->setComputedPotential(energies[0], vars[0]);
228 matterDimer->setComputedPotential(energies[1], vars[1]);
229 }
else if (dimerDirty) {
233 const double *posVec[] = {posDimer.data()};
234 const int *nrsVec[] = {nrs.data()};
236 double energies[1], vars[1];
237 const double *boxVec[] = {box.data()};
238 pot->forceBatch(1,
nAtoms, posVec, nrsVec, frcVec, energies, vars,
240 rejectNonFiniteBatch(
nAtoms, frcVec[0], energies[0]);
241 matterDimer->setComputedPotential(energies[0], vars[0]);
242 }
else if (centerDirty) {
253 AtomMatrix forceB = 2.0 * forceCenter - forceA;
264 (forceA - forceB) / (2.0 *
params.main_options().finiteDifference);
267 return (projB - projA) / (2.0 *
params.main_options().finiteDifference);
273 double &lengthRotationalForceOld) {
275 double a = std::abs(
matDot(rotationalForce, rotationalForceOld));
276 double b = rotationalForceOld.squaredNorm();
279 gamma = (rotationalForce.array() *
280 (rotationalForce - rotationalForceOld).array())
287 rotationalForce + rotationalPlaneOld * lengthRotationalForceOld * gamma;
294 rotationalForceOld = rotationalForce;
300 double cosA = std::cos(rotationAngle);
301 double sinA = std::sin(rotationAngle);
308 eonc::safemath::safe_normalize_inplace(
direction);
double matDot(const AtomMatrix &a, const AtomMatrix &b)
SIMD-optimized dot product for contiguous Eigen matrices.
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
AtomMatrix rotationalPlane
std::shared_ptr< Matter > matterDimer
double getEigenvalue() override
AtomMatrix getEigenvector() override
std::shared_ptr< Matter > matterCenter
void determineRotationalPlane(const AtomMatrix &rotationalForce, AtomMatrix &rotationalForceOld, const AtomMatrix &rotationalPlaneOld, double &lengthRotationalForceOld)
Determine rotational plane via conjugate gradient.
double calcRotationalForceReturnCurvature(AtomMatrix &rotationalForce)
Compute rotational force and return curvature along the dimer.
void rotate(double rotationAngle)
Rotate the dimer by the given angle (radians).
eonc::log::FileScoped log
void compute(std::shared_ptr< Matter > matter, AtomMatrix initialDirection) override
Dimer(std::shared_ptr< Matter > matter, const Parameters ¶ms, std::shared_ptr< Potential > pot)
const Parameters & params
std::shared_ptr< Potential > pot
LowestEigenmode(std::shared_ptr< Potential > potPassed, const Parameters ¶meters)
void rotationRemove(const AtomMatrix r1, std::shared_ptr< Matter > m2)
std::shared_ptr< Potential > makePotential(const Parameters ¶ms)
AtomMatrix makeOrthogonal(const AtomMatrix v1, const AtomMatrix v2)
double safe_acos(double x)
double safe_atan_ratio(double num, double denom, double fallback=0.0)
RAII resource manager for the ARTn C library with global synchronization.
std::optional< DimerRotationResult > runAlternativeRotation(DimerRotationBackend backend, const std::shared_ptr< Matter > &matter, const Parameters ¶ms, const std::shared_ptr< Potential > &pot, const AtomMatrix &initialDirection, quill::Logger *log=nullptr)
Run Lanczos, Davidson, or LOR.