use serde::{Deserialize, Serialize};
use super::{UnivariateEnergy, UnivariateForce};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Xplor<F> {
pub f: F,
pub r_cut: f64,
pub r_smooth: f64,
}
impl<F> Xplor<F> {
#[inline]
fn s(&self, r: f64) -> f64 {
if r < self.r_smooth {
1.0
} else if r > self.r_cut {
0.0
} else {
let r_sq = r.powi(2);
let r_cut_sq = self.r_cut.powi(2);
let r_smooth_sq = self.r_smooth.powi(2);
(r_cut_sq - r_sq).powi(2) * (r_cut_sq + 2.0 * r_sq - 3.0 * r_smooth_sq)
/ (r_cut_sq - r_smooth_sq).powi(3)
}
}
#[inline]
fn ds_dr(&self, r: f64) -> f64 {
let r_sq = r.powi(2);
let r_cut_sq = self.r_cut.powi(2);
let r_smooth_sq = self.r_smooth.powi(2);
12.0 * r * ((r_cut_sq - r_sq) * (r_smooth_sq - r_sq)) / (r_cut_sq - r_smooth_sq).powi(3)
}
}
impl<F: UnivariateEnergy> UnivariateEnergy for Xplor<F> {
#[inline]
fn energy(&self, r: f64) -> f64 {
self.s(r) * self.f.energy(r)
}
}
impl<F: UnivariateForce + UnivariateEnergy> UnivariateForce for Xplor<F> {
#[inline]
fn force(&self, r: f64) -> f64 {
if r < self.r_smooth {
self.f.force(r)
} else if r > self.r_cut {
0.0
} else {
self.s(r) * self.f.force(r) - self.ds_dr(r) * self.f.energy(r)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use approxim::{assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq};
use rstest::*;
use crate::univariate::LennardJones;
#[rstest]
fn special_points_12_6(
#[values(1.0, 2.0, 12.125, 0.25)] epsilon: f64,
#[values(1.0, 2.0, 0.5)] sigma: f64,
) {
let lj: LennardJones = LennardJones { epsilon, sigma };
let r_smooth = 1.0; let r_cut = 2.5 * sigma;
let xplor_lj = Xplor {
f: lj.clone(),
r_cut,
r_smooth,
};
assert_eq!(xplor_lj.f.epsilon, epsilon);
assert_eq!(xplor_lj.f.sigma, sigma);
assert_eq!(xplor_lj.r_smooth, r_smooth);
assert_eq!(xplor_lj.r_cut, r_cut);
assert_abs_diff_eq!(xplor_lj.energy(r_smooth / 2.0), lj.energy(r_smooth / 2.0));
assert_abs_diff_eq!(
xplor_lj.energy(r_smooth.next_down()),
lj.energy(r_smooth.next_down())
);
assert_abs_diff_eq!(xplor_lj.force(r_smooth / 2.0), lj.force(r_smooth / 2.0));
assert_abs_diff_eq!(
xplor_lj.force(r_smooth.next_down()),
lj.force(r_smooth.next_down())
);
if sigma < r_smooth {
assert_abs_diff_eq!(xplor_lj.energy(sigma), 0.0);
assert_relative_eq!(xplor_lj.force(sigma), 24.0 * epsilon / sigma);
}
assert_abs_diff_eq!(xplor_lj.energy(r_cut), 0.0);
assert_abs_diff_eq!(xplor_lj.energy(r_cut.next_up()), 0.0);
assert_abs_diff_eq!(xplor_lj.energy(r_cut * 2.0), 0.0);
assert_abs_diff_eq!(xplor_lj.force(r_cut), 0.0);
assert_abs_diff_eq!(xplor_lj.force(r_cut.next_up()), 0.0);
assert_abs_diff_eq!(xplor_lj.force(r_cut * 2.0), 0.0);
assert_abs_diff_ne!(
xplor_lj.energy(f64::midpoint(r_smooth, r_cut)),
lj.energy(f64::midpoint(r_smooth, r_cut))
);
assert_abs_diff_ne!(
xplor_lj.force(f64::midpoint(r_smooth, r_cut)),
lj.force(f64::midpoint(r_smooth, r_cut))
);
let r_min = 2.0_f64.powf(1.0 / 6.0) * sigma;
assert_relative_eq!(xplor_lj.energy(r_min), -epsilon * xplor_lj.s(r_min));
if sigma < r_smooth {
assert_abs_diff_eq!(xplor_lj.force(r_min), 0.0, epsilon = 1e-12);
}
assert_relative_eq!(
xplor_lj.energy(2.0 * sigma),
-63.0 / 1024.0 * epsilon * xplor_lj.s(2.0 * sigma)
);
assert_relative_eq!(
xplor_lj.force(2.0 * sigma),
-(93.0 * r_cut.powi(6) * epsilon - 279.0 * r_cut.powi(4) * r_smooth.powi(2) * epsilon
+ 1476.0 * r_cut.powi(2) * r_smooth.powi(2) * sigma.powi(2) * epsilon
- 1440.0 * r_cut.powi(2) * sigma.powi(4) * epsilon
- 1440.0 * r_smooth.powi(2) * sigma.powi(4) * epsilon
- 192.0 * sigma.powi(6) * epsilon)
/ (512.0 * r_cut.powi(6) * sigma
- 1536.0 * r_cut.powi(4) * r_smooth.powi(2) * sigma
+ 1536.0 * r_cut.powi(2) * r_smooth.powi(4) * sigma
- 512.0 * r_smooth.powi(6) * sigma)
);
}
}