Loading...
Searching...
No Matches
potential_base.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*/
19#include <cassert>
20#include <cmath>
21#include <cstdlib>
22#include <iostream>
23
34using namespace forcefields;
35
41 : cutoff_(6.5),
42 switchingWidth_(2.0) {
43 periods_[0] = 0.0;
44 periods_[1] = 0.0;
45 periods_[2] = 0.0;
46}
47
48PotentialBase::PotentialBase(double cutoff, double switchingWidth)
49 : cutoff_(cutoff),
50 switchingWidth_(switchingWidth) {
52 std::cerr << "Error: getSwitchingWidth() > getCutoff()" << std::endl;
53 std::exit(EXIT_FAILURE);
54 };
55 periods_[0] = 0.0;
56 periods_[1] = 0.0;
57 periods_[2] = 0.0;
58}
59
60double PotentialBase::getCutoff() const { return cutoff_; }
61
62void PotentialBase::setCutoff(double cutoff) { cutoff_ = cutoff; }
63
75
77// @}
78
85double PotentialBase::applyPeriodicity0(double r, double const period) {
86 if (not std::isinf(period)) {
87 double n = r / period + 0.5;
88 // This is slightly more efficient than using function floor().
89 int m = static_cast<int>(n);
90 if (n < 0)
91 --m;
92 r -= m * period;
93 };
94 return r;
95}
96
104void PotentialBase::applyPeriodicity0(double r[], double const periods[]) {
105 for (int i = 0; i < 3; ++i)
106 r[i] = applyPeriodicity0(r[i], periods[i]);
107}
108
116double PotentialBase::isoscelesBase(double length, double angle) {
117 return 2.0 * length * std::sin(angle / 2.0);
118}
119
130void PotentialBase::restrainLength(const double R1[], const double R2[],
131 double F1[], double F2[], double &u,
132 const double k, const double req) {
133 double f, fx;
134 double r[3], r1, r2;
135 distance(R2, R1, r, r1, r2);
136 double d = r1 - req;
137 f = -2.0 * k * d;
138 u += k * d * d;
139 fx = f * r[0] / r1;
140 F1[0] -= fx;
141 F2[0] += fx;
142 fx = f * r[1] / r1;
143 F1[1] -= fx;
144 F2[1] += fx;
145 fx = f * r[2] / r1;
146 F1[2] -= fx;
147 F2[2] += fx;
148}
149
160void PotentialBase::restrainAngle(double const r1[], double const r2[],
161 double const r3[], double f1[], double f2[],
162 double f3[], double &u, const double k,
163 const double aeq) {
164 double r12[3], r12_1, r12_2, r23[3], r23_1, r23_2, cosa, a, m, d12, d23;
165 distance(r2, r1, r12, r12_1, r12_2);
166 distance(r3, r2, r23, r23_1, r23_2);
167 cosa = -dotProduct(r12, r23) / r12_1 / r23_1;
168 a = std::acos(cosa);
169 u += k * (a - aeq) * (a - aeq);
170 m = -2 * k * (a - aeq); // moment
171 m /= -1 * std::sqrt(1 - cosa * cosa); //*d(a)/d(cos a)
172 for (int i = 0; i < 3; ++i) {
173 d12 = -cosa * r12[i] / r12_2 - r23[i] / r12_1 / r23_1; // d(cos a)/d(r12)
174 d23 = -cosa * r23[i] / r23_2 - r12[i] / r12_1 / r23_1; // d(cos a)/d(r23)
175 f1[i] -= m * d12;
176 f2[i] += m * (d12 - d23);
177 f3[i] += m * d23;
178 };
179}
180
182 14.399644532010862; // eV e^2 / Angstrom
183
188void PotentialBase::calculateCentre(double const r1[], double const r2[],
189 double rc[]) {
190 for (int i = 0; i < 3; ++i) {
191 rc[i] = (r1[i] + unBreak1(r2[i], r1[i], i)) * 0.5;
192 };
193}
194
196void PotentialBase::calculateCentre(double const r1[], double const r2[],
197 double const r3[], double rc[]) {
198 for (int i = 0; i < 3; ++i) {
199 rc[i] =
200 (r1[i] + unBreak1(r2[i], r1[i], i) + unBreak1(r3[i], r1[i], i)) / 3.0;
201 };
202}
203
208void PotentialBase::calculateWeightedCentre(double const w1, double const w2,
209 double const w3, double const r1[],
210 double const r2[],
211 double const r3[], double rc[]) {
212 assert(std::fabs(w1 + w2 + w3 - 1.0) < 1e-9);
213 for (int i = 0; i < 3; ++i) {
214 rc[i] = w1 * r1[i] + w2 * unBreak1(r2[i], r1[i], i) +
215 w3 * unBreak1(r3[i], r1[i], i);
216 };
217}
218
229void PotentialBase::coulombWithCutoff(const double r1[], const double r2[],
230 double f1[], double f2[], double &energy,
231 double qq) {
232 double d1, d[3];
233 // d: vector distance (r1-r2), d1 distance and d2 squared distance
234 distance(r1, r2, d, d1);
235 if (d1 < cutoff_) {
236 double f, en;
237 coulomb(d1, f, en, qq);
238 switching(d1, f, en);
239 for (int l = 0; l < 3; ++l) {
240 f1[l] += f * d[l] / d1;
241 f2[l] -= f * d[l] / d1;
242 };
243 energy += en;
244 }
245}
246
256void PotentialBase::coulomb(const double r1[], const double r2[], double f1[],
257 double f2[], double &energy, double qq) {
258 double d1, d[3], f, en;
259 // d: vector distance (r1-r2), d1 distance and d2 squared distance
260 distance(r1, r2, d, d1);
261 coulomb(d1, f, en, qq);
262 for (int l = 0; l < 3; ++l) {
263 f1[l] += f * d[l] / d1;
264 f2[l] -= f * d[l] / d1;
265 };
266 energy += en;
267}
268
275void PotentialBase::coulomb(double distance, double &force, double &energy,
276 double const qq) {
277 energy = ONE_OVER_4_PI_EPSILON0 * qq / distance;
278 force = energy / distance;
279}
280
284void PotentialBase::spreadForce(double f1[], double f2[], double const fc[]) {
285 for (int i = 0; i < 3; ++i) {
286 double const f = fc[i] / 2;
287 f1[i] += f;
288 f2[i] += f;
289 };
290}
291
295void PotentialBase::spreadForce(double f1[], double f2[], double f3[],
296 double const fc[]) {
297 for (int i = 0; i < 3; ++i) {
298 double const f = fc[i] / 3;
299 f1[i] += f;
300 f2[i] += f;
301 f3[i] += f;
302 };
303}
304
312void PotentialBase::spreadWeightedForce(double const w1, double const w2,
313 double const w3, double f1[],
314 double f2[], double f3[],
315 double const fc[]) {
316 for (int i = 0; i < 3; ++i) {
317 double const f = fc[i];
318 f1[i] += f * w1;
319 f2[i] += f * w2;
320 f3[i] += f * w3;
321 };
322}
323
333double PotentialBase::epsilon(double const A, double const B) {
334 return B * B / A / 4.0;
335}
336
347void PotentialBase::lennardJones(double distance, double &force, double &energy,
348 double const epsilon, double const sigma) {
349 double x = sigma * sigma / (distance * distance);
350 x *= x * x;
351 energy = 4 * epsilon * (x - 1) * x;
352 force = 24 * epsilon * (2 * x - 1) * x / distance;
353}
354
363void PotentialBase::lennardJones(const double R1[], const double R2[],
364 double F1[], double F2[], double &E,
365 double const epsilon, double const sigma) {
366 // WARNING: F1 and F2 are incremented.
367 double F, Fx, en;
368 double R12[3];
369 double L, L2;
370 distance(R2, R1, R12, L, L2);
371 lennardJones(L, F, en, epsilon, sigma);
372 E += en;
373 Fx = F * R12[0] / L;
374 F1[0] -= Fx;
375 F2[0] += Fx;
376 Fx = F * R12[1] / L;
377 F1[1] -= Fx;
378 F2[1] += Fx;
379 Fx = F * R12[2] / L;
380 F1[2] -= Fx;
381 F2[2] += Fx;
382}
383
397void PotentialBase::lennardJonesWithCutoff(double const r1[], double const r2[],
398 double f1[], double f2[],
399 double &energy, double const epsilon,
400 double const sigma) {
401 double r12[3] = {0}, d = 0.0, f, en; // distance
402 distance(r2, r1, r12, d);
403 if (d < cutoff_) {
404 lennardJones(d, f, en, epsilon, sigma);
405 switching(d, f, en);
406 for (int i = 0; i < 3; ++i) {
407 f1[i] -= f * r12[i] / d;
408 f2[i] += f * r12[i] / d;
409 };
410 energy += en;
411 };
412}
413
423double PotentialBase::sigma(double const A, double const B) {
424 return std::pow(A / B, 1.0 / 6.0);
425}
426
433double PotentialBase::smithKongEpsilon(double sigma1, double epsilon1,
434 double sigma2, double epsilon2) {
435 double n, d;
436 double s1_2 = sigma1 * sigma1, sa6 = s1_2 * s1_2 * s1_2;
437 double s2_2 = sigma2 * sigma2, sb6 = s2_2 * s2_2 * s2_2;
438 n = 8192.0 * epsilon1 * sa6 * epsilon2 * sb6; // 2^13 = 8192
439 d = std::pow(epsilon1 * sa6 * sa6, 1.0 / 13.0) +
440 std::pow(epsilon2 * sb6 * sb6, 1.0 / 13.0);
441 d = std::pow(d, 13);
442 return n / d;
443}
444
455double PotentialBase::smithKongSigma(double sigma1, double epsilon1,
456 double sigma2, double epsilon2) {
457 double s1_2 = sigma1 * sigma1;
458 double const sa6 = s1_2 * s1_2 * s1_2;
459 double s2_2 = sigma2 * sigma2;
460 double const sb6 = s2_2 * s2_2 * s2_2;
461 double n, d;
462 n = std::pow(epsilon1 * sa6 * sa6, 1.0 / 13.0) +
463 std::pow(epsilon2 * sb6 * sb6, 1.0 / 13.0);
464 n = std::pow(n, 13.0);
465 d = 8192.0 * std::sqrt(epsilon1 * sa6 * epsilon2 * sb6); // 2^13 = 8192
466 return std::pow(n / d, 1.0 / 6.0);
467}
468
481void PotentialBase::switching(double const distance, double &force,
482 double &energy) {
484 if (x >= 1.0) { // if more: cutoff
485 force = 0.0;
486 energy = 0.0;
487 }
488 if (x > 0.0) { // if more: switching zone
489 double S = (2 * x - 3) * x * x + 1;
490 double dS = 6 * x * (x - 1);
491 double const u = energy;
492 force = force * S - u * dS / switchingWidth_;
493 energy *= S;
494 };
495 // else: full interaction, leave force and energy as the are.
496}
497
511void PotentialBase::switching(double const r1[], double const r2[], double f1[],
512 double f2[], double &energy) {
513 double z[3], z1;
514 distance(r1, r2, z, z1);
515 double x = (z1 - cutoff_ + switchingWidth_) / switchingWidth_;
516 assert(x > 0.0);
517 assert(x < 1.0);
518 double S = (2 * x - 3) * x * x + 1;
519 double dS = 6 * x * (x - 1);
520 double const u = energy;
521 for (int k = 0; k < 3; ++k) {
522 f1[k] = f1[k] * S - u * dS / switchingWidth_ * z[k] / z1;
523 f2[k] = f2[k] * S + u * dS / switchingWidth_ * z[k] / z1;
524 };
525 energy *= S;
526}
527
537
545double PotentialBase::applyPeriodicity1(double r, int const axis) {
546 return applyPeriodicity0(r, periods_[axis]);
547}
548
555void PotentialBase::distance(const double x[], const double y[], double z[],
556 double &z1, double &z2) {
557 assert(x);
558 assert(y);
559 assert(z);
560 for (int i = 0; i < 3; ++i)
561 z[i] = x[i] - y[i];
563 z2 = z[0] * z[0] + z[1] * z[1] + z[2] * z[2];
564 z1 = std::sqrt(z2);
565}
566
572void PotentialBase::distance(const double x[], const double y[], double z[],
573 double &z1) {
574 double z2;
575 distance(x, y, z, z1, z2);
576}
577
581void PotentialBase::distance(const double x[], const double y[], double z[]) {
582 double z1;
583 distance(x, y, z, z1);
584}
585
586void PotentialBase::distance(const double x[], const double y[], double &z1) {
587 double z[3];
588 distance(x, y, z, z1);
589}
590
595void PotentialBase::distance(const double x[], const double y[], Vector3 &z) {
596 distance(x, y, z.v, z._1, z._2);
597}
598
603void PotentialBase::setPeriodicity(const double periods[]) {
604 periods_[0] = periods[0];
605 periods_[1] = periods[1];
606 periods_[2] = periods[2];
607}
608
623double PotentialBase::unBreak0(double const r, double const ref,
624 double const period) {
625 return applyPeriodicity0(r - ref, period) + ref;
626}
627
629void PotentialBase::unBreak0(double r[], double const ref[],
630 double const periods[]) {
631 for (int a = 0; a < 3; ++a)
632 r[a] = unBreak0(r[a], ref[a], periods[a]);
633}
634
636void PotentialBase::unBreak1(double r[], double const ref[]) {
637 unBreak0(r, ref, periods_);
638}
639
641double PotentialBase::unBreak1(double const r, double const ref,
642 int const axis) {
643 return unBreak0(r, ref, periods_[axis]);
644}
645
658void PotentialBase::computePt(int const nAtoms, double positions[],
659 double forces[], double &energy,
660 double const periods[], bool const fixed[]) {
661 int const nCoord = 3 * nAtoms;
662 for (int i = 0; i < nCoord; ++i)
663 forces[i] = 0.0;
664 energy = 0.0;
665 double const(*r)[3] = reinterpret_cast<double const(*)[3]>(positions);
666 double (*f)[3] = reinterpret_cast<double (*)[3]>(forces);
667 setPeriodicity(periods); // Set Periodic boundaries. Essential in order for
668 // some functions to work.
669 for (int i = nAtoms - 1; i > 0; --i) {
670 for (int j = i - 1; j >= 0; --j) {
671 if (not fixed[i] and not fixed[j]) {
672 lennardJonesWithCutoff(r[i], r[j], f[i], f[j], energy, EPSILON_PT,
673 SIGMA_PT);
674 };
675 };
676 };
677}
678
683const double PotentialBase::EPSILON_PT = 0.68165797577788501; // eV
684const double PotentialBase::SIGMA_PT = 2.54; // Angstrom
686/*
687 const double PotentialBase::ONE_OVER_4_PI_EPSILON0 =
688 unit_system::ONE_OVER_4_PI_EPSILON0; const double
689 PotentialBase::EPSILON_PT=65.77*KJ_PER_MOL; const double
690 PotentialBase::SIGMA_PT=0.254*NM;
691 */
PotentialBase()
Non bond interaction cutoff.
static double sigma(double const A, double const B)
Conversion for Lennard-Jones.
void spreadWeightedForce(double const w1, double const w2, double const w3, double f1[], double f2[], double f3[], double const fc[])
Spread force on barycentre to atoms.
static double applyPeriodicity0(double r, double const period)
Minimum image representation.
double unBreak1(double const r, double const ref, int const axis)
Undo the separation of two atoms created by the periodic boundaries.
void switching(ChargeGroup< N, R, F > &g1, ChargeGroup< N, R, F > &g2, double &energy, double cutoff, double switchingWidth)
static double unBreak0(double const r, double const ref, double const period)
Undo the separation of two atoms created by the periodic boundaries.
void restrainLength(const double R1[], const double R2[], double F1[], double F2[], double &u, const double k, const double r0)
Compute quadratic restraints between two atoms.
void restrainAngle(double const r1[], double const r2[], double const r3[], double f1[], double f2[], double f3[], double &u, const double k, const double aeq)
Angular quadratic restraints.
static double smithKongSigma(double sigma1, double epsilon1, double sigma2, double epsilon2)
Smith and Kong combination rules.
void setCutoff(double cutoff)
void coulomb(const double r1[], const double r2[], double f1[], double f2[], double &u, double const qq)
Compute Coulomb interaction between two charges.
void lennardJones(double const distance, double &force, double &energy, double const epsilon, double const sigma)
Lennard-Jones 12-6 between two atoms.
void calculateCentre(double const r1[], double const r2[], double rc[])
Calculate centre of two points.
void setPeriodicity(const double periods[])
Set periodicity.
void distance(const double x[], const double y[], double z[], double &z1, double &z2)
Distance vector, norm and norm square.
void setSwitchingWidth(double width)
void lennardJonesWithCutoff(double const r1[], double const r2[], double f1[], double f2[], double &energy, double const epsilon, double const sigma)
Lennard Jones 12-6 Potential with cutoff.
static const double EPSILON_PT
Platinum Lennard-Jones.
double applyPeriodicity1(double r, int const axis)
Minimum image representation.
void computePt(int const nAtoms, double positions[], double forces[], double &energy, double const periods[], bool const fixed[])
Potential for Platinum.
static const double SIGMA_PT
static double smithKongEpsilon(double sigma1, double epsilon1, double sigma2, double epsilon2)
Smith and Kong combination rules.
static double epsilon(double const A, double const B)
Conversion for Lennard-Jones.
void spreadForce(double f1[], double f2[], double const fc[])
Spread force on centre to other points.
double getSwitchingWidth() const
Width of the switching zone.
void coulombWithCutoff(const double r1[], const double r2[], double f1[], double f2[], double &u, double const qq)
Coulomb interaction with single charge based cutoff.
static double isoscelesBase(double length, double angle)
Calculate the base of an isosceles triangle.
void calculateWeightedCentre(double const w1, double const w2, double const w3, double const r1[], double const r2[], double const r3[], double rc[])
Calculate barycentre.
static const double ONE_OVER_4_PI_EPSILON0
static double dotProduct(double const v[], double const w[])
Dot product.
Basic tools to write potentials.
double _2
Vector's norm square.