Loading...
Searching...
No Matches
zhu_philpott.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
52
53// #define DEBUG_LEVEL 5
54#ifdef DEBUG_LEVEL
55#warning zhu_philpott debug: DEBUG_LEVEL defined
56#define DEBUG_LEVEL_RETURN(x) \
57 if (x == DEBUG_LEVEL) \
58 continue;
59#else
60#define DEBUG_LEVEL_RETURN(x)
61#endif
62
63using namespace forcefields;
64
65template <class P>
67 : SpceCcl() {
68 nPlatinum_ = 0;
69 positions_ = 0;
70 forces_ = 0;
71}
72
73template <class P>
74ZhuPhilpott<P>::ZhuPhilpott(double cutoff, double switchingWidth)
75 : SpceCcl(cutoff, switchingWidth) {
76 nPlatinum_ = 0;
77 positions_ = 0;
78 forces_ = 0;
79}
80
81template <class P> ZhuPhilpott<P>::ZhuPhilpott(ZhuPhilpott const &zhuPhilpott) {
82 operator=(zhuPhilpott);
83}
84
85template <class P>
86void ZhuPhilpott<P>::operator=(ZhuPhilpott const &zhuPhilpott) {
87 setPlatinum(zhuPhilpott.nPlatinum_, zhuPhilpott.positions_.data());
88}
89
90template <class P> ZhuPhilpott<P>::~ZhuPhilpott() = default;
91
110template <class P>
111void ZhuPhilpott<P>::computeHH_O_Pt_(const int nWater, const int nPt,
112 const double r[], double f[],
113 double &energy, double const b[],
114 bool const fixed[]) {
115 const double (*const rh1)[6] = reinterpret_cast<const double (*)[6]>(r);
116 const double (*const rh2)[6] = reinterpret_cast<const double (*)[6]>(&r[3]);
117 const double (*const ro)[3] =
118 reinterpret_cast<const double (*)[3]>(&r[nWater * 6]);
119 const double (*const rPt)[3] =
120 reinterpret_cast<const double (*)[3]>(&r[nWater * 9]);
121
122 double (*const fh1)[6] = reinterpret_cast<double (*)[6]>(f);
123 double (*const fh2)[6] = reinterpret_cast<double (*)[6]>(&f[3]);
124 double (*const fo)[3] = reinterpret_cast<double (*)[3]>(&f[nWater * 6]);
125 double (*const fPt)[3] = reinterpret_cast<double (*)[3]>(&f[nWater * 9]);
126
127 if (fixed) {
128 bool const(*const xh1)[2] = reinterpret_cast<bool const(*)[2]>(fixed);
129 bool const(*const xh2)[2] = reinterpret_cast<bool const(*)[2]>(&fixed[1]);
130 bool const(*const xo)[1] =
131 reinterpret_cast<bool const(*)[1]>(&fixed[nWater * 2]);
132 bool const *const xPt = &fixed[nWater * 3];
133 computeTemplate(nWater, rh1, rh2, ro, fh1, fh2, fo, nPt, rPt, fPt, energy,
134 b, xh1, xh2, xo, xPt);
135 } else {
136 bool const(*const xh1)[2] = 0;
137 bool const(*const xh2)[2] = 0;
138 bool const(*const xo)[1] = 0;
139 computeTemplate(nWater, rh1, rh2, ro, fh1, fh2, fo, nPt, rPt, fPt, energy,
140 b, xh1, xh2, xo, 0);
141 };
142}
143
160template <class P>
161void ZhuPhilpott<P>::computeHH_O_(const int nWater, const double r[],
162 double f[], double &energy, double const b[],
163 bool const fixed[]) {
164 const double (*const rh1)[6] = reinterpret_cast<const double (*)[6]>(r);
165 const double (*const rh2)[6] = reinterpret_cast<const double (*)[6]>(&r[3]);
166 const double (*const ro)[3] =
167 reinterpret_cast<const double (*)[3]>(&r[nWater * 6]);
168 const double (*const rPt)[3] =
169 reinterpret_cast<const double (*)[3]>(positions_.data());
170
171 double (*const fh1)[6] = reinterpret_cast<double (*)[6]>(f);
172 double (*const fh2)[6] = reinterpret_cast<double (*)[6]>(&f[3]);
173 double (*const fo)[3] = reinterpret_cast<double (*)[3]>(&f[nWater * 6]);
174 double (*const fPt)[3] = reinterpret_cast<double (*)[3]>(forces_.data());
175
176 if (fixed) {
177 bool const(*const xh1)[2] = reinterpret_cast<bool const(*)[2]>(fixed);
178 bool const(*const xh2)[2] = reinterpret_cast<bool const(*)[2]>(&fixed[1]);
179 bool const(*const xo)[1] =
180 reinterpret_cast<bool const(*)[1]>(&fixed[nWater * 2]);
181 computeTemplate(nWater, rh1, rh2, ro, fh1, fh2, fo, nPlatinum_, rPt, fPt,
182 energy, b, xh1, xh2, xo, 0);
183 } else {
184 bool const(*const xh1)[2] = 0;
185 bool const(*const xh2)[2] = 0;
186 bool const(*const xo)[1] = 0;
187 computeTemplate(nWater, rh1, rh2, ro, fh1, fh2, fo, nPlatinum_, rPt, fPt,
188 energy, b, xh1, xh2, xo, 0);
189 };
190}
191
194template <class P> int ZhuPhilpott<P>::nPlatinum() const { return nPlatinum_; }
195
199template <class P>
200void ZhuPhilpott<P>::setPlatinum(int nPlatinum, double const positions[]) {
201 assert(nPlatinum >= 0);
203 int const n = nPlatinum * 3;
204 positions_.assign(positions, positions + n);
205 forces_.assign(n, 0.0);
206}
207
209template <class P> char const *ZhuPhilpott<P>::getName() {
210 return "ZhuPhilpott";
211}
212
213template <class P>
214template <int H, int O, int H3, int O3>
216 const int nWater, const double (*const rh1)[H3],
217 const double (*const rh2)[H3], const double (*const ro)[O3],
218 double (*const fh1)[H3], double (*const fh2)[H3], double (*const fo)[O3],
219 const int nPt, const double rPt[][3], double fPt[][3], double &energy,
220 double const b[], bool const (*const xh1)[H], bool const (*const xh2)[H],
221 bool const (*const xo)[O], bool const xPt[]) {
222 for (int i = 0; i < nWater; ++i) {
223 for (int a = 0; a < 3; ++a) {
224 fh1[i][a] = 0.0;
225 fh2[i][a] = 0.0;
226 fo[i][a] = 0.0;
227 };
228 };
229 for (int i = 0; i < nPt; ++i) {
230 for (int a = 0; a < 3; ++a)
231 fPt[i][a] = 0.0;
232 };
233 energy = 0.0;
235 for (int i = nWater - 1; i >= 0; --i) {
236 double centre1[3] = {0};
237 calculateCentre(rh1[i], rh2[i], ro[i], centre1);
238 Water w1(rh1[i], rh2[i], ro[i], centre1, fh1[i], fh2[i], fo[i]);
239 intramolecular(w1, energy);
240 assert(not std::isnan(energy));
242 // Two next lines for interaction with platinum
243 interactWithCorePt(w1, nPt, rPt, fPt,
244 energy); // called Vw-core in Zhu and Philpott
245 assert(not std::isnan(energy));
247 interactWithImage(w1, w1, energy); // called Vw-cond in Zhu and Philpott
249 assert(not std::isnan(energy));
250 for (int j = i - 1; j >= 0; --j) {
251 bool areFixed = false;
252 if (xh1 and xh2 and xo) {
253 areFixed = xh1[i][0] and xh2[i][0] and xo[i][0];
254 // check if all the atoms of molecule j are fixed.
255 areFixed &= xh1[j][0] and xh2[j][0] and xo[j][0];
256 };
257 // if both molecules are fixed skip force calculation
258 if (not areFixed) {
259 double centre2[3] = {0};
260 calculateCentre(rh1[j], rh2[j], ro[j], centre2);
261 Water w2(rh1[j], rh2[j], ro[j], centre2, fh1[j], fh2[j], fo[j]);
262 lennardJonesWithCutoff(w1, w2, energy);
264 coulombWithCutoff(w1, w2, energy, 1.0);
266 // Two next lines for interaction with platinum
267 // called Vw-cond in Zhu and Philpott
268 interactWithImage(w1, w2, energy); // w1 interacts with image of w2
269 interactWithImage(w2, w1, energy); // w2 interacts with image of w1
271 };
272 };
273#ifndef NDEBUG
274 for (int a = 0; a < 3; a++) {
275 assert(not std::isnan(fh1[i][a]));
276 assert(not std::isnan(fh2[i][a]));
277 assert(not std::isnan(fo[i][a]));
278 };
279#endif
280 };
281 assert(not std::isnan(energy) and not std::isinf(energy));
282}
283
291template <class P>
292void ZhuPhilpott<P>::interactWithCorePt(Water &water, int const nPt,
293 double const rPt[][3], double fPt[][3],
294 double &energy) {
295 for (int i = 0; i < nPt; ++i) {
296 double r[3], r1; // for distance between two atoms
297 // Pt - O
298 distance(water.ro_, rPt[i], r, r1);
299 if (r1 <= cutoff_ - switchingWidth_) {
300 interactionPtO(water.ro_, rPt[i], water.fo_, fPt[i], energy);
301 } else if (r1 < cutoff_) {
302 double en = 0;
303 double fo[3] = {0}, fPtTmp[3] = {0};
304 interactionPtO(water.ro_, rPt[i], fo, fPtTmp, en);
305 switching(water.ro_, rPt[i], fo, fPtTmp, en);
306 energy += en;
307 for (int j = 0; j < 3; j++) {
308 water.fo_[j] += fo[j];
309 fPt[i][j] += fPtTmp[j];
310 };
311 };
312 // Pt - H1
313 distance(water.rh1_, rPt[i], r, r1);
314 if (r1 <= cutoff_ - switchingWidth_) {
315 interactionPtH(water.rh1_, rPt[i], water.fh1_, fPt[i], energy);
316 } else if (r1 < cutoff_) {
317 double en = 0;
318 double fh[3] = {0}, fPtTmp[3] = {0};
319 interactionPtH(water.rh1_, rPt[i], fh, fPtTmp, en);
320 switching(water.rh1_, rPt[i], fh, fPtTmp, en);
321 energy += en;
322 for (int j = 0; j < 3; j++) {
323 water.fh1_[j] += fh[j];
324 fPt[i][j] += fPtTmp[j];
325 };
326 };
327 // Pt - H2
328 distance(water.rh2_, rPt[i], r, r1);
329 if (r1 <= cutoff_ - switchingWidth_) {
330 interactionPtH(water.rh2_, rPt[i], water.fh2_, fPt[i], energy);
331 } else if (r1 < cutoff_) {
332 double en = 0;
333 double fh[3] = {0}, fPtTmp[3] = {0};
334 interactionPtH(water.rh2_, rPt[i], fh, fPtTmp, en);
335 switching(water.rh2_, rPt[i], fh, fPtTmp, en);
336 energy += en;
337 for (int j = 0; j < 3; j++) {
338 water.fh2_[j] += fh[j];
339 fPt[i][j] += fPtTmp[j];
340 };
341 };
342 };
343}
344
345template <class P>
346void ZhuPhilpott<P>::interactionPtO(double const R1[], double const R2[],
347 double F1[], double F2[], double &energy) {
348 double R12[3], F12[3] = {0}, r1, f1 = 0;
349 distance(R1, R2, R12, r1);
350 anisotropic(R12, F12, energy, P::epsilonOPt_, P::sigmaOPt_, P::alpha_);
351 isotropic10(r1, f1, energy, P::epsilonOPt_, P::sigmaOPt_, P::C10_O_);
352 for (int j = 0; j < 3; j++) {
353 F1[j] += F12[j] + f1 * R12[j] / r1;
354 F2[j] -= F12[j] + f1 * R12[j] / r1;
355 };
356}
357
358template <class P>
359void ZhuPhilpott<P>::interactionPtH(double const R1[], double const R2[],
360 double F1[], double F2[], double &energy) {
361 double R12[3], F12[3] = {0}, r1, f1 = 0;
362 distance(R1, R2, R12, r1);
363 anisotropic(R12, F12, energy, P::epsilonHPt_, P::sigmaHPt_, P::alpha_);
364 isotropic10(r1, f1, energy, P::epsilonHPt_, P::sigmaHPt_, P::C10_H_);
365 for (int j = 0; j < 3; j++) {
366 F1[j] += F12[j] + f1 * R12[j] / r1;
367 F2[j] -= F12[j] + f1 * R12[j] / r1;
368 };
369}
370
387template <class P>
388void ZhuPhilpott<P>::anisotropic(const double distance[], double force[],
389 double &energy, double const epsilon,
390 double const sigma, double const alpha) {
391 double z2, z, a, b, A, B, dE_da, dE_db, rho, rho2, alpha2;
392 // WARNING: F1 and F2 are incremented.
393 alpha2 = alpha * alpha;
394 double f;
395 rho2 = distance[0] * distance[0] + distance[1] * distance[1];
396 rho = std::sqrt(rho2);
397 z = distance[2];
398 z2 = z * z;
399 double sigma2 = sigma * sigma;
400 a = sigma2 / (rho2 * alpha2 + z2);
401 b = sigma2 / (rho2 / alpha2 + z2);
402 double a2 = a * a, a3 = a2 * a;
403 double b2 = b * b;
404 A = a3 * a3; // a^6
405 B = b * b2; // b^3
406 energy += 4.0 * epsilon * (A - B);
407 dE_da = 24.0 * epsilon * a2 * a3; // a^5
408 dE_db = -12.0 * epsilon * b2; // b^2
409 double invSigma2 = 1.0 / sigma2;
410 f = 2.0 * (dE_da * a2 * alpha2 + dE_db * b2 / alpha2) * invSigma2;
411 force[0] += f * distance[0];
412 force[1] += f * distance[1];
413 f = 2.0 * (dE_da * a2 + dE_db * b2) * invSigma2;
414 force[2] += f * distance[2];
415}
416
429template <class P>
430void ZhuPhilpott<P>::isotropic10(double const distance, double &force,
431 double &energy, double const epsilon,
432 double const sigma, double const C10) {
433 double const s = sigma / distance;
434 double s10 = s * s * s * s * s;
435 s10 *= s10;
436 energy += -4.0 * epsilon * C10 * s10;
437 force -= 40.0 * epsilon * C10 * s10 / distance;
438}
439
448template <class P>
450 // f is used to store the force on the atoms' images. These forces have no use
451 // and are at the end discarded. we use the same vector to store of the forces
452 // even when they are on different atoms' images.
453 double r[4][3], f[3][3] = {{0}};
454 for (int k = 0; k < 2; k++) {
455 r[0][k] = w2.rh1_[k];
456 r[1][k] = w2.rh2_[k];
457 r[2][k] = w2.ro_[k];
458 r[3][k] = w2.rc_[k];
459 };
460 // mirror about plane at z=0.
461 r[0][2] = -w2.rh1_[2];
462 r[1][2] = -w2.rh2_[2];
463 r[2][2] = -w2.ro_[2];
464 r[3][2] = -w2.rc_[2];
465 Water w2m(r[0], r[1], r[2], r[3], f[0], f[1], f[2]);
466 // -2 is given as the relative permittivity. This value is not the
467 // permittivity of the metal which is infinite in the case of a perfect metal.
468 // It is the value (1+epsilon_r)/(1-epsilon_r) which is equal to -1 for a
469 // perfect metal. This value is the 'effective' permittivity for the
470 // interaction of a charge with a mirror image.
471 coulombWithCutoff(w1, w2m, U, -2.0);
472 for (int k = 0; k < 2; k++) {
473 w2.fh1_[k] += f[0][k];
474 w2.fh2_[k] += f[1][k];
475 w2.fo_[k] += f[2][k];
476 };
477 // mirror about plane
478 w2.fh1_[2] -= f[0][2];
479 w2.fh2_[2] -= f[1][2];
480 w2.fo_[2] -= f[2][2];
481}
482
495template <class P>
497 double const relativePermittivity) {
498 double z[3], z1, z2;
499 distance(w1.rc_, w2.rc_, z, z1, z2);
500 if (z1 <= cutoff_ - switchingWidth_) {
501 coulombFull(w1, w2, U, relativePermittivity);
502 } else if (z1 < cutoff_) {
503 double f1[3][3] = {{0}}, f2[3][3] = {{0}};
504 Water v1(w1, f1[0], f1[1], f1[2]);
505 Water v2(w2, f2[0], f2[1], f2[2]);
506 double energy = 0.0;
507 coulombFull(v1, v2, energy, relativePermittivity);
508 ChargeGroup<3> g1 = {v1.rc_, 0, f1};
509 ChargeGroup<3> g2 = {v2.rc_, 0, f2};
510 switching(g1, g2, energy, cutoff_, switchingWidth_);
511 U += energy;
512 w1.addForces(v1);
513 w2.addForces(v2);
514 };
515}
516
524template <class P>
525void ZhuPhilpott<P>::coulombFull(Water &w1, Water &w2, double &U,
526 double const relativePermittivity) {
527 // q^2/relativePermittivity;
528 const double qq2overEr = charge2_ / relativePermittivity;
529 // Coulomb interactions between hydrogens
530 coulomb(w1.rh1_, w2.rh1_, w1.fh1_, w2.fh1_, U, qq2overEr);
531 coulomb(w1.rh1_, w2.rh2_, w1.fh1_, w2.fh2_, U, qq2overEr);
532 coulomb(w1.rh2_, w2.rh1_, w1.fh2_, w2.fh1_, U, qq2overEr);
533 coulomb(w1.rh2_, w2.rh2_, w1.fh2_, w2.fh2_, U, qq2overEr);
534 // interactions between H and O.
535 coulomb(w1.ro_, w2.rh1_, w1.fo_, w2.fh1_, U, -2.0 * qq2overEr);
536 coulomb(w1.ro_, w2.rh2_, w1.fo_, w2.fh2_, U, -2.0 * qq2overEr);
537 coulomb(w1.rh1_, w2.ro_, w1.fh1_, w2.fo_, U, -2.0 * qq2overEr);
538 coulomb(w1.rh2_, w2.ro_, w1.fh2_, w2.fo_, U, -2.0 * qq2overEr);
539 // interactions between O1, O2
540 coulomb(w1.ro_, w2.ro_, w1.fo_, w2.fo_, U, 4.0 * qq2overEr);
541}
static double sigma(double const A, double const B)
Conversion for Lennard-Jones.
void switching(ChargeGroup< N, R, F > &g1, ChargeGroup< N, R, F > &g2, double &energy, double cutoff, double switchingWidth)
void coulomb(const double r1[], const double r2[], double f1[], double f2[], double &u, double const qq)
Compute Coulomb interaction between two charges.
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.
static double epsilon(double const A, double const B)
Conversion for Lennard-Jones.
void intramolecular(Water &water, double &U)
Interactions within a molecules.
Definition spce_ccl.cpp:159
void lennardJonesWithCutoff(Water &w1, Water &w2, double &U)
Interactions between two molecules.
Definition spce_ccl.cpp:86
static const double charge2_
Square of # charge_.
Definition spce_ccl.hpp:157
std::vector< double > positions_
void computeHH_O_(const int nWater, const double r[], double f[], double &energy, double const b[], bool const fixed[])
Compute water-platinum interactions, call with water's positions only.
void operator=(ZhuPhilpott const &)
void coulombFull(Water &w1, Water &w2, double &U, double const relativePermittivity)
Coulomb interaction between two molecules with cutoff.
int nPlatinum() const
Number of platinum atoms.
void interactWithCorePt(Water &water, int const nPt, double const rPt[][3], double fPt[][3], double &energy)
Interaction of one molecule of water with the whole platinum.
void anisotropic(const double distance[], double force[], double &energy, double const epsilon, double const sigma, double const alpha)
Anisotropic interaction between water and platinum.
void computeTemplate(const int nWater, const double(*const rh1)[H3], const double(*const rh2)[H3], const double(*const ro)[O3], double(*const fh1)[H3], double(*const fh2)[H3], double(*const fo)[O3], const int nPt, const double rPt[][3], double fPt[][3], double &energy, double const b[], bool const (*const xh1)[H]=0, bool const (*const xh2)[H]=0, bool const (*const xo)[O]=0, bool const *xPt=0)
void interactWithImage(Water &w1, Water &w2, double &U)
Interactions of a molecule with an image.
void setPlatinum(int nPlatinum, double const positions[])
Initialises the positions of the atoms of platinum.
void coulombWithCutoff(Water &w1, Water &w2, double &u, double const relativePermittivity)
Coulomb interaction between two molecules within the swithcing zone.
void isotropic10(double const distance, double &force, double &energy, double const epsilon, double const sigma, double const C10)
Isotropic interaction between water and platinum.
void interactionPtH(double const R1[], double const R2[], double F1[], double F2[], double &energy)
static char const * getName()
Name of the potential.
void interactionPtO(double const R1[], double const R2[], double F1[], double F2[], double &energy)
void computeHH_O_Pt_(const int nWater, const int nPt, const double r[], double f[], double &energy, double const b[], bool const fixed[])
Compute water-platinum forcefield.
std::vector< double > forces_
Pointers to molecule of water.
Definition spce_ccl.hpp:78
const double *const rh1_
Definition spce_ccl.hpp:103
void addForces(Water const &w)
Definition spce_ccl.hpp:96
const double *const ro_
Definition spce_ccl.hpp:105
const double *const rc_
Definition spce_ccl.hpp:106
const double *const rh2_
Definition spce_ccl.hpp:104
#define DEBUG_LEVEL_RETURN(x)
Potential for water and Platinum.