24 std::shared_ptr<Potential>
pot)
27 auto dimerPot = (
pot->needsPerImageInstance() &&
params.main_options.parallel)
34 nAtoms = matter->numberOfAtoms();
47 eonc::safemath::safe_normalize_inplace(initialDirection);
54 static_cast<quill::Logger *
>(
log))) {
59 eonc::safemath::safe_normalize_inplace(
direction);
66 long forceCallsDimer =
matterDimer->getForceCalls();
67 double curvature = 0.0;
68 double rotationAngle = 0.0;
74 rotationalForce.setZero();
75 rotationalForceOld.setZero();
76 rotationalPlaneOld.setZero();
79 double lengthRotationalForceOld = 0.0;
82 bool doneRotating =
false;
83 while (!doneRotating) {
87 rotationalPlaneOld, lengthRotationalForceOld);
89 torque = rotationalForce.norm();
90 assert(std::isnormal(torque));
93 if ((torque >
params.dimer_options.torque_max &&
94 rotations >=
params.dimer_options.rotations_max) ||
95 (torque <
params.dimer_options.torque_max &&
96 torque >=
params.dimer_options.torque_min &&
97 rotations >=
params.dimer_options.rotations_min) ||
98 (torque <
params.dimer_options.torque_min)) {
109 double rotForceChange =
110 (rotForce1 - rotForce2) /
params.dimer_options.rotation_angle;
111 double forceDimer = (rotForce1 + rotForce2) / 2.0;
114 rotForceChange, 0.0) /
116 params.dimer_options.rotation_angle / 2.0;
118 if (rotForceChange < 0) {
127 "[DimerRot] ----- --------- ---------------- "
128 "--------- {:9.3e} {:9.3e} {:9.3e} ---------\n",
135 eonc::safemath::safe_normalize_inplace(
direction);
141 forceCallsCenter =
matterCenter->getForceCalls() - forceCallsCenter;
142 forceCallsDimer =
matterDimer->getForceCalls() - forceCallsDimer;
158 if (
params.dimer_options.remove_rotation) {
163 eonc::safemath::safe_normalize_inplace(
direction);
164 posDimer = posCenter +
direction *
params.main_options.finiteDifference;
170 if (
pot->supportsBatchEvaluation()) {
175 if (centerDirty && dimerDirty) {
181 const double *posVec[] = {
matterCenter->getPositions().data(),
183 const int *nrsVec[] = {nrs0.data(), nrs1.data()};
186 double energies[2], vars[2];
187 const double *boxVec[] = {box0.data(), box1.data()};
188 pot->forceBatch(2,
nAtoms, posVec, nrsVec, frcVec, energies, vars,
190 matterCenter->setComputedPotential(energies[0], vars[0]);
191 matterDimer->setComputedPotential(energies[1], vars[1]);
192 }
else if (dimerDirty) {
196 const double *posVec[] = {posDimer.data()};
197 const int *nrsVec[] = {nrs.data()};
199 double energies[1], vars[1];
200 const double *boxVec[] = {box.data()};
201 pot->forceBatch(1,
nAtoms, posVec, nrsVec, frcVec, energies, vars,
203 matterDimer->setComputedPotential(energies[0], vars[0]);
204 }
else if (centerDirty) {
215 AtomMatrix forceB = 2.0 * forceCenter - forceA;
226 (forceA - forceB) / (2.0 *
params.main_options.finiteDifference);
229 return (projB - projA) / (2.0 *
params.main_options.finiteDifference);
235 double &lengthRotationalForceOld) {
237 double a = std::abs(
matDot(rotationalForce, rotationalForceOld));
238 double b = rotationalForceOld.squaredNorm();
241 gamma = (rotationalForce.array() *
242 (rotationalForce - rotationalForceOld).array())
249 rotationalForce + rotationalPlaneOld * lengthRotationalForceOld * gamma;
256 rotationalForceOld = rotationalForce;
262 double cosA = std::cos(rotationAngle);
263 double sinA = std::sin(rotationAngle);
268 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
Dimer(std::shared_ptr< Matter > matter, const Parameters ¶ms, std::shared_ptr< Potential > pot)
AtomMatrix rotationalPlane
std::shared_ptr< Matter > matterDimer
std::shared_ptr< Matter > matterCenter
AtomMatrix getEigenvector()
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).
void compute(std::shared_ptr< Matter > matter, AtomMatrix initialDirection)
eonc::log::FileScoped log
void determineRotationalPlane(const AtomMatrix &rotationalForce, AtomMatrix &rotationalForceOld, const AtomMatrix &rotationalPlaneOld, double &lengthRotationalForceOld)
Determine rotational plane via conjugate gradient.
const Parameters & params
std::shared_ptr< Potential > pot
LowestEigenmode(std::shared_ptr< Potential > potPassed, const Parameters ¶meters)
std::shared_ptr< Potential > makePotential(const Parameters ¶ms)
void rotationRemove(const AtomMatrix r1, std::shared_ptr< Matter > m2)
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)
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.