28 const Eigen::Vector3d &rij,
double k_par,
30 const double r = rij.norm();
31 if (r < 1.0e-14 || (!std::isfinite(k_par) && !std::isfinite(k_perp))) {
34 if (k_par == 0.0 && k_perp == 0.0) {
37 const Eigen::Vector3d u = rij / r;
38 const Eigen::Matrix3d H = k_perp * Eigen::Matrix3d::Identity() +
39 (k_par - k_perp) * (u * u.transpose());
40 for (
int a = 0; a < 3; ++a) {
41 for (
int b = 0; b < 3; ++b) {
42 const int ia = 3 * i + a;
43 const int ib = 3 * i + b;
44 const int ja = 3 * j + a;
45 const int jb = 3 * j + b;
54inline void ljDerivs(
double r,
double eps,
double sigma,
double &vp,
56 const double inv = sigma / r;
57 const double x6 = inv * inv * inv * inv * inv * inv;
58 const double x12 = x6 * x6;
59 vp = 4.0 * eps * (-12.0 * x12 + 6.0 * x6) / r;
60 vpp = 4.0 * eps * (156.0 * x12 - 42.0 * x6) / (r * r);
63inline void morseDerivs(
double r,
double D,
double a,
double re,
double &vp,
65 const double e1 = std::exp(-a * (r - re));
66 const double e2 = e1 * e1;
67 vp = 2.0 * D * a * (e1 - e2);
68 vpp = 2.0 * D * a * a * (2.0 * e2 - e1);
72 return kind ==
"lindh_full" || kind ==
"swart" || kind ==
"fischer" ||
78 const Eigen::Vector3d *g,
int n_atoms,
80 if (!(k > 0.0) || !std::isfinite(k)) {
83 for (
int a = 0; a < n_atoms; ++a) {
84 for (
int b = 0; b < n_atoms; ++b) {
85 for (
int x = 0; x < 3; ++x) {
86 for (
int y = 0; y < 3; ++y) {
87 P(3 * atoms[a] + x, 3 * atoms[b] + y) += k * g[a][x] * g[b][y];
95inline bool angleGrads(
const Eigen::Vector3d &ri,
const Eigen::Vector3d &rj,
96 const Eigen::Vector3d &rk, Eigen::Vector3d g[3]) {
97 Eigen::Vector3d u = ri - rj;
98 Eigen::Vector3d v = rk - rj;
99 const double pu = u.norm();
100 const double pv = v.norm();
101 if (pu < 1.0e-14 || pv < 1.0e-14) {
107 c = std::max(-1.0 + 1.0e-14, std::min(1.0 - 1.0e-14, c));
108 const double s = std::sqrt(std::max(0.0, 1.0 - c * c));
112 g[0] = (c * u - v) / (pu * s);
113 g[2] = (c * v - u) / (pv * s);
119inline bool torsionGrads(
const Eigen::Vector3d &ri,
const Eigen::Vector3d &rj,
120 const Eigen::Vector3d &rk,
const Eigen::Vector3d &rl,
121 Eigen::Vector3d g[4]) {
122 const Eigen::Vector3d vij = ri - rj;
123 const Eigen::Vector3d vkj = rk - rj;
124 const Eigen::Vector3d vkl = rk - rl;
125 Eigen::Vector3d n1 = vij.cross(vkj);
126 Eigen::Vector3d n2 = vkl.cross(vkj);
127 const double n1n = n1.norm();
128 const double n2n = n2.norm();
129 const double b2 = vkj.norm();
130 if (n1n < 1.0e-14 || n2n < 1.0e-14 || b2 < 1.0e-14) {
133 g[0] = -(b2 / (n1n * n1n)) * n1;
134 g[3] = (b2 / (n2n * n2n)) * n2;
135 const double c1 = vij.dot(vkj) / (b2 * b2);
136 const double c2 = vkl.dot(vkj) / (b2 * b2);
137 g[1] = -g[0] + c1 * g[0] - c2 * g[3];
138 g[2] = -g[3] - c1 * g[0] + c2 * g[3];
142inline double lindhRho(
double r,
double r_nn,
double alpha) {
143 return std::exp(alpha * (r_nn * r_nn - r * r));
147 return 1.0 / (1.0 + std::exp(12.0 * (r / r_nn - 1.2)));
150inline void addModelHess(Eigen::MatrixXd &P,
const Eigen::VectorXd &pos,
151 const std::string &kind,
double A,
double mu,
153 const int nat =
static_cast<int>(pos.size()) / 3;
154 const double alpha = A > 0.0 ? A : 1.0;
155 const double scale = mu > 0.0 ? mu : 1.0;
156 const double r_bond = std::min(rcut, 1.35 * r_nn);
157 const double r_cov = 0.5 * r_nn;
158 auto mic = [&](
int i,
int j) {
159 Eigen::Vector3d dr = pos.segment<3>(3 * i) - pos.segment<3>(3 * j);
163 std::vector<std::vector<int>> nb(
static_cast<size_t>(nat));
164 const double r_pair = (kind ==
"swart") ? rcut : r_bond;
165 for (
int i = 0; i < nat; ++i) {
166 for (
int j = i + 1; j < nat; ++j) {
167 const Eigen::Vector3d rij = mic(i, j);
168 const double r = rij.norm();
169 if (r >= r_pair || r < 1.0e-14) {
173 if (kind ==
"schlegel") {
174 const double den = std::max(r - 0.45 * r_nn, 0.15);
175 k = scale * 1.734 / (den * den * den);
176 }
else if (kind ==
"fischer") {
177 k = scale * 0.3601 * std::exp(-1.944 * (r - r_cov));
179 k = scale *
lindhRho(r, r_nn, alpha);
180 if (kind ==
"swart") {
186 nb[
static_cast<size_t>(i)].push_back(j);
187 nb[
static_cast<size_t>(j)].push_back(i);
191 for (
int j = 0; j < nat; ++j) {
192 const auto &nbr = nb[
static_cast<size_t>(j)];
193 for (
size_t a = 0; a < nbr.size(); ++a) {
194 for (
size_t b = a + 1; b < nbr.size(); ++b) {
195 const int i = nbr[a];
196 const int k = nbr[b];
197 const Eigen::Vector3d ri = pos.segment<3>(3 * i);
198 const Eigen::Vector3d rj = pos.segment<3>(3 * j);
199 const Eigen::Vector3d rk = pos.segment<3>(3 * k);
200 Eigen::Vector3d g[3];
201 Eigen::Vector3d rji = ri - rj;
202 Eigen::Vector3d rjk = rk - rj;
208 const double rij = rji.norm();
209 const double rkj = rjk.norm();
210 double kang = 0.16 * scale;
211 if (kind ==
"fischer") {
212 kang = scale * (0.089 + 0.11 *
213 std::pow(r_cov * r_cov, -0.42) *
214 std::exp(-0.44 * ((rij - r_cov) +
216 }
else if (kind ==
"lindh_full" || kind ==
"swart") {
217 kang = 0.15 * scale *
lindhRho(rij, r_nn, alpha) *
219 if (kind ==
"swart") {
223 const int atoms[3] = {i, j, k};
228 for (
int j = 0; j < nat; ++j) {
229 const auto &nj = nb[
static_cast<size_t>(j)];
234 const auto &nk = nb[
static_cast<size_t>(k)];
240 if (l == j || l == i) {
243 Eigen::Vector3d rijv = pos.segment<3>(3 * i) - pos.segment<3>(3 * j);
244 Eigen::Vector3d rkjv = pos.segment<3>(3 * k) - pos.segment<3>(3 * j);
245 Eigen::Vector3d rklv = pos.segment<3>(3 * k) - pos.segment<3>(3 * l);
249 const Eigen::Vector3d rj = pos.segment<3>(3 * j);
250 Eigen::Vector3d g[4];
251 if (!
torsionGrads(rj + rijv, rj, rj + rkjv, rj + rkjv - rklv, g)) {
254 const double rij = rijv.norm();
255 const double rjk = rkjv.norm();
256 const double rkl = rklv.norm();
257 double ktor = 0.01 * scale;
258 if (kind ==
"fischer") {
259 ktor = 0.0015 * scale;
260 }
else if (kind ==
"lindh_full" || kind ==
"swart") {
261 ktor = 0.005 * scale *
lindhRho(rij, r_nn, alpha) *
263 if (kind ==
"swart") {
268 const int atoms[4] = {i, j, k, l};
278inline Eigen::MatrixXd
281 const int n =
static_cast<int>(pos.size());
282 const int nat = n / 3;
283 std::vector<double> nn(
static_cast<size_t>(nat),
284 std::numeric_limits<double>::infinity());
285 auto mic = [&](
int i,
int j) {
286 Eigen::Vector3d dr = pos.segment<3>(3 * i) - pos.segment<3>(3 * j);
290 for (
int i = 0; i < nat; ++i) {
291 for (
int j = 0; j < nat; ++j) {
295 const double r = mic(i, j).norm();
296 if (r > 1.0e-12 && r < nn[
static_cast<size_t>(i)]) {
297 nn[
static_cast<size_t>(i)] = r;
302 for (
double v : nn) {
303 if (std::isfinite(v)) {
304 r_nn = std::max(r_nn, v);
310 const double rcut = rcut_in > 0.0 ? rcut_in : 2.5 * r_nn;
311 Eigen::MatrixXd P = Eigen::MatrixXd::Zero(n, n);
314 const double shift = 1.0e-6 * std::max(1.0, P.diagonal().mean());
315 P.diagonal().array() += shift;
318 for (
int i = 0; i < nat; ++i) {
319 for (
int j = i + 1; j < nat; ++j) {
320 const Eigen::Vector3d rij = mic(i, j);
321 const double r = rij.norm();
322 if (r >= rcut || r < 1.0e-14) {
325 if (kind ==
"pair" || kind ==
"pair_abs" || kind ==
"pair_full") {
333 double k_par = std::max(vpp, 0.0);
334 double k_perp = std::max(vp / r, 0.0);
335 if (kind ==
"pair_abs") {
336 k_par = std::abs(vpp);
337 k_perp = std::abs(vp / r);
338 }
else if (kind ==
"pair_full") {
343 }
else if (kind ==
"lindh") {
344 const double alpha = A > 0.0 ? A : 1.0;
345 const double k = mu * std::exp(alpha * (r_nn * r_nn - r * r));
347 }
else if (kind ==
"c1") {
348 const double x = r / rcut;
349 const double w = mu * (1.0 - x) * (1.0 - x) * (1.0 + 2.0 * x);
352 const double w = mu * std::exp(-A * (r / r_nn - 1.0));
357 const double shift = 1.0e-6 * std::max(1.0, P.diagonal().mean());
358 P.diagonal().array() += shift;
virtual void minimumImage(Eigen::Ref< Eigen::Vector3d >) const
bool angleGrads(const Eigen::Vector3d &ri, const Eigen::Vector3d &rj, const Eigen::Vector3d &rk, Eigen::Vector3d g[3])
Wilson (B) rows for the valence angle (i{-}j{-}k) ((j) central).
void addModelHess(Eigen::MatrixXd &P, const Eigen::VectorXd &pos, const std::string &kind, double A, double mu, double r_nn, double rcut, ObjectiveFunction &objf)
Eigen::MatrixXd build(const Eigen::VectorXd &pos, const std::string &kind, PotType pot, double A, double mu, double rcut_in, ObjectiveFunction &objf)
Analytic pair or model Hessian.
double lindhRho(double r, double r_nn, double alpha)
void ljDerivs(double r, double eps, double sigma, double &vp, double &vpp)
void addPairBlock(Eigen::MatrixXd &P, int i, int j, const Eigen::Vector3d &rij, double k_par, double k_perp)
bool torsionGrads(const Eigen::Vector3d &ri, const Eigen::Vector3d &rj, const Eigen::Vector3d &rk, const Eigen::Vector3d &rl, Eigen::Vector3d g[4])
Wilson (B) rows for the torsion (i{-}j{-}k{-}l).
void addScalarInternal(Eigen::MatrixXd &P, const int *atoms, const Eigen::Vector3d *g, int n_atoms, double k)
Rank-1 update (k\,(\nabla q)(\nabla q)^\top) for a scalar internal.
double swartScreen(double r, double r_nn)
void morseDerivs(double r, double D, double a, double re, double &vp, double &vpp)
bool isModelHess(const std::string &kind)
RAII resource manager for the ARTn C library with global synchronization.