eOn client
Long-timescale dynamics: aKMC, NEB, parallel replica
☾
Toggle main menu visibility
Loading...
Searching...
No Matches
IDPPObjectiveFunction.cpp
Go to the documentation of this file.
1
/*
2
** This file is part of eOn.
3
**
4
** SPDX-License-Identifier: BSD-3-Clause
5
**
6
** Copyright (c) 2010--present, eOn Development Team
7
** All rights reserved.
8
**
9
** Repo:
10
** https://github.com/TheochemUI/eOn
11
*/
12
13
#include "
eon/IDPPObjectiveFunction.hpp
"
14
15
#include <algorithm>
16
#include <cmath>
17
18
double
IDPPObjectiveFunction::getEnergy
() {
19
double
energy = 0.0;
20
int
natoms =
matter
->numberOfAtoms();
21
AtomMatrix
pos =
matter
->getPositions();
22
23
// Loop over unique pairs
24
for
(
int
i = 0; i < natoms; ++i) {
25
for
(
int
j = i + 1; j < natoms; ++j) {
26
// Respect PBC
27
double
r =
matter
->pbc(pos.row(i) - pos.row(j)).norm();
28
29
// Weight function w = 1 / r^4
30
// Avoid division by zero if atoms overlap perfectly (unlikely in IDPP but
31
// possible)
32
if
(r < 1e-4)
33
r = 1e-4;
34
35
double
diff = r -
d_target
(i, j);
36
double
r2 = r * r;
37
double
weight = 1.0 / (r2 * r2);
// 1/r^4
38
39
energy += 0.5 * weight * diff * diff;
40
}
41
}
42
return
energy;
43
}
44
45
VectorXd
IDPPObjectiveFunction::getGradient
(
bool
fdstep) {
46
int
natoms =
matter
->numberOfAtoms();
47
AtomMatrix
pos =
matter
->getPositions();
48
AtomMatrix
forces = AtomMatrix::Zero(natoms, 3);
49
50
for
(
int
i = 0; i < natoms; ++i) {
51
for
(
int
j = i + 1; j < natoms; ++j) {
52
// Vector pointing from j to i
53
Eigen::RowVector3d dr_vec =
matter
->pbc(pos.row(i) - pos.row(j));
54
double
r = dr_vec.norm();
55
56
if
(r < 1e-4)
57
r = 1e-4;
58
59
double
diff = r -
d_target
(i, j);
60
double
r2 = r * r;
61
double
r5 = r2 * r2 * r;
62
63
// Derivative of E_pair = 0.5 * (1/r^4) * (r - d_target)^2
64
// dE/dr = (r - d_target)/r^4 - 2(r - d_target)^2 / r^5
65
// Simplified: (r - d_target) * (1 - 2(r - d_target)/r) / r^4
66
67
double
r4 = r2 * r2;
68
double
dEdr = (diff * (1.0 - 2.0 * diff / r)) / r4;
69
70
// Force contribution: F = -dE/dr * (dr_vec / r)
71
Eigen::RowVector3d f_contribution = -dEdr * (dr_vec / r);
72
73
forces.row(i) += f_contribution;
74
forces.row(j) -= f_contribution;
// Newton's 3rd law
75
}
76
}
77
78
// Convert N x 3 matrix to 3N vector and return negative gradient (force)
79
// BUT getGradient expects the Gradient (positive derivative), so we return
80
// -Forces Actually, typical eOn getGradient returns dV/dx.
81
return
VectorXd::Map(forces.data(), 3 * natoms) * -1.0;
82
}
83
84
MatrixXd
CollectiveIDPPObjectiveFunction::getDistanceMatrix
(
const
Matter
&m) {
85
int
natoms = m.
numberOfAtoms
();
86
MatrixXd
d(natoms, natoms);
87
auto
pos = m.
getPositions
();
88
for
(
int
i = 0; i < natoms; ++i) {
89
for
(
int
j = 0; j < natoms; ++j) {
90
d(i, j) = m.
pbc
(pos.row(i) - pos.row(j)).norm();
91
}
92
}
93
return
d;
94
}
95
96
MatrixXd
97
CollectiveIDPPObjectiveFunction::getIDPPForces
(
const
Matter
&m,
98
const
MatrixXd
&dTarget) {
99
int
natoms = m.
numberOfAtoms
();
100
AtomMatrix
forces = AtomMatrix::Zero(natoms, 3);
101
auto
pos = m.
getPositions
();
102
103
for
(
int
i = 0; i < natoms; ++i) {
104
for
(
int
j = i + 1; j < natoms; ++j) {
105
Eigen::RowVector3d dr_vec = m.
pbc
(pos.row(i) - pos.row(j));
106
double
r = dr_vec.norm();
107
if
(r < 1e-4)
108
r = 1e-4;
109
110
double
diff = r - dTarget(i, j);
111
// SOTA Weighting: w = 1/r^4. Gradient logic matches Smidstrup/ASE
112
double
r2 = r * r;
113
double
r5 = r2 * r2 * r;
114
double
dEdr = (diff / r5) * (2.0 * dTarget(i, j) - r);
115
116
Eigen::RowVector3d f = -dEdr * (dr_vec / r);
// -dE/dr * r_hat
117
forces.row(i) += f;
118
forces.row(j) -= f;
119
}
120
}
121
return
forces;
122
}
123
124
VectorXd
CollectiveIDPPObjectiveFunction::getGradient
(
bool
fdstep) {
125
int
nImgs =
path
.size() - 2;
// Exclude fixed endpoints
126
int
natoms =
path
[0].numberOfAtoms();
127
VectorXd totalGradient(3 * natoms * nImgs);
128
double
maxForce = 0.0;
129
130
// 1. Compute Raw IDPP Forces and Tangents
131
std::vector<AtomMatrix> rawForces(
path
.size());
132
std::vector<AtomMatrix> tangents(
path
.size());
133
134
// We compute for 1..N (moving images)
135
for
(
size_t
i = 1; i <= nImgs; ++i) {
136
// Interpolate Target
137
double
xi =
static_cast<
double
>
(i) / (nImgs + 1);
138
MatrixXd
dTarget = (1.0 - xi) *
dInit
+ xi *
dFinal
;
139
140
rawForces[i] =
getIDPPForces
(
path
[i], dTarget);
141
142
// Simple Tangent: Next - Prev
143
AtomMatrix
nextPos =
path
[i + 1].getPositions();
144
AtomMatrix
prevPos =
path
[i - 1].getPositions();
145
tangents[i] =
path
[i].pbc(nextPos - prevPos);
146
tangents[i].normalize();
// Unit tangent
147
}
148
149
// 2. Project Forces and Add Springs (The "NEB" part of IDPP-NEB)
150
double
k =
params
.neb_options.spring.constant;
151
152
for
(
size_t
i = 1; i <= nImgs; ++i) {
153
AtomMatrix
f = rawForces[i];
154
AtomMatrix
t = tangents[i];
155
156
// Perpendicular Force (IDPP optimization)
157
double
f_dot_t =
matDot
(f, t);
158
AtomMatrix
f_perp = f - f_dot_t * t;
159
160
// Spring Force (Spacing optimization)
161
double
distNext =
path
[i].distanceTo(
path
[i + 1]);
162
double
distPrev =
path
[i].distanceTo(
path
[i - 1]);
163
AtomMatrix
f_spring = k * (distNext - distPrev) * t;
164
165
// Total NEB Force
166
AtomMatrix
f_neb = f_perp + f_spring;
167
168
// Store as Gradient (-Force)
169
totalGradient.segment(3 * natoms * (i - 1), 3 * natoms) =
170
VectorXd::Map(f_neb.data(), 3 * natoms) * -1.0;
171
172
// Tracking convergence
173
maxForce = std::max(maxForce, f_neb.template lpNorm<Eigen::Infinity>());
174
}
175
176
lastMaxForce
= maxForce;
177
return
totalGradient;
178
}
matDot
double matDot(const AtomMatrix &a, const AtomMatrix &b)
SIMD-optimized dot product for contiguous Eigen matrices.
Definition
Eigen.h:50
MatrixXd
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, eOnStorageOrder > MatrixXd
Definition
Eigen.h:33
AtomMatrix
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition
Eigen.h:37
IDPPObjectiveFunction.hpp
IDPPObjectiveFunction::getEnergy
double getEnergy() override
Definition
IDPPObjectiveFunction.cpp:18
eonc::CollectiveIDPPObjectiveFunction::lastMaxForce
double lastMaxForce
Definition
IDPPObjectiveFunction.hpp:139
eonc::CollectiveIDPPObjectiveFunction::getIDPPForces
MatrixXd getIDPPForces(const Matter &m, const MatrixXd &dTarget)
Definition
IDPPObjectiveFunction.cpp:97
eonc::CollectiveIDPPObjectiveFunction::dInit
MatrixXd dInit
Definition
IDPPObjectiveFunction.hpp:138
eonc::CollectiveIDPPObjectiveFunction::path
std::vector< Matter > & path
Definition
IDPPObjectiveFunction.hpp:137
eonc::CollectiveIDPPObjectiveFunction::dFinal
MatrixXd dFinal
Definition
IDPPObjectiveFunction.hpp:138
eonc::CollectiveIDPPObjectiveFunction::getDistanceMatrix
MatrixXd getDistanceMatrix(const Matter &m)
Definition
IDPPObjectiveFunction.cpp:84
eonc::CollectiveIDPPObjectiveFunction::getGradient
VectorXd getGradient(bool fdstep=false) override
Definition
IDPPObjectiveFunction.cpp:124
eonc::IDPPObjectiveFunction::d_target
MatrixXd d_target
Definition
IDPPObjectiveFunction.hpp:76
eonc::IDPPObjectiveFunction::matter
std::shared_ptr< Matter > matter
Definition
IDPPObjectiveFunction.hpp:25
eonc::IDPPObjectiveFunction::getGradient
VectorXd getGradient(bool fdstep=false) override
Definition
IDPPObjectiveFunction.cpp:45
eonc::Matter
Definition
Matter.h:92
eonc::Matter::pbc
AtomMatrix pbc(const AtomMatrix &diff) const
Definition
Matter.h:163
eonc::Matter::numberOfAtoms
long int numberOfAtoms() const
Definition
Matter.cpp:209
eonc::Matter::getPositions
const AtomMatrix & getPositions() const
Definition
Matter.cpp:236
eonc::ObjectiveFunction::params
const Parameters & params
Definition
ObjectiveFunction.h:20
client
IDPPObjectiveFunction.cpp
Generated by
1.17.0
Generated by
Doxygen 1.17.0
Analytics by
Antics
provided by
TurtleTech ehf