use crate::base::Potential2;
use crate::math::Vector;
#[derive(Clone, Copy, Debug)]
pub struct Softcore<P, T> {
inner: P,
alpha_sigma_sq: T, lambda: T,
lambda_n: T, lambda_m: T, }
impl<P, T: Vector> Softcore<P, T> {
#[inline]
pub fn new(inner: P, alpha: f64, sigma: f64, lambda: f64, n: u32, m: u32) -> Self {
let lambda_n = lambda.powi(n as i32);
let lambda_m = lambda.powi(m as i32);
Self {
inner,
alpha_sigma_sq: T::splat(alpha * sigma * sigma),
lambda: T::splat(lambda),
lambda_n: T::splat(lambda_n),
lambda_m: T::splat(lambda_m),
}
}
#[inline]
pub fn set_lambda(&mut self, lambda: f64, n: u32, m: u32) {
self.lambda = T::splat(lambda);
self.lambda_n = T::splat(lambda.powi(n as i32));
self.lambda_m = T::splat(lambda.powi(m as i32));
}
#[inline(always)]
fn effective_r_sq(&self, r_sq: T) -> T {
self.alpha_sigma_sq * self.lambda_n + r_sq
}
}
impl<P: Potential2<T>, T: Vector> Potential2<T> for Softcore<P, T> {
#[inline(always)]
fn energy(&self, r_sq: T) -> T {
let r_eff_sq = self.effective_r_sq(r_sq);
self.lambda_m * self.inner.energy(r_eff_sq)
}
#[inline(always)]
fn force_factor(&self, r_sq: T) -> T {
let r_eff_sq = self.effective_r_sq(r_sq);
self.lambda_m * self.inner.force_factor(r_eff_sq)
}
#[inline(always)]
fn energy_force(&self, r_sq: T) -> (T, T) {
let r_eff_sq = self.effective_r_sq(r_sq);
let (e, f) = self.inner.energy_force(r_eff_sq);
(self.lambda_m * e, self.lambda_m * f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pair::Lj;
use approx::assert_relative_eq;
#[test]
fn test_softcore_lambda_one() {
let lj: Lj<f64> = Lj::new(1.0, 3.4);
let lj_soft = Softcore::new(lj, 0.5, 3.4, 1.0, 1, 1);
let r_sq = 16.0;
let (e, f) = lj_soft.energy_force(r_sq);
let r_eff_sq = 0.5 * 3.4 * 3.4 + r_sq;
let (e_expected, f_expected) = lj.energy_force(r_eff_sq);
assert_relative_eq!(e, e_expected, epsilon = 1e-10);
assert_relative_eq!(f, f_expected, epsilon = 1e-10);
}
#[test]
fn test_softcore_lambda_zero() {
let lj: Lj<f64> = Lj::new(1.0, 3.4);
let lj_soft = Softcore::new(lj, 0.5, 3.4, 0.0, 1, 1);
let r_sq = 16.0;
let e = lj_soft.energy(r_sq);
assert_relative_eq!(e, 0.0, epsilon = 1e-10);
}
#[test]
fn test_softcore_finite_at_zero() {
let lj: Lj<f64> = Lj::new(1.0, 3.4);
let lj_soft = Softcore::new(lj, 0.5, 3.4, 0.5, 1, 1);
let e = lj_soft.energy(0.0);
assert!(e.is_finite());
assert!(e.abs() < 1e10);
}
#[test]
fn test_softcore_numerical_derivative() {
let lj: Lj<f64> = Lj::new(1.0, 3.4);
let lj_soft = Softcore::new(lj, 0.5, 3.4, 0.5, 1, 1);
let r = 4.0;
let h = 1e-7;
let e_plus = lj_soft.energy((r + h) * (r + h));
let e_minus = lj_soft.energy((r - h) * (r - h));
let dv_dr_numerical = (e_plus - e_minus) / (2.0 * h);
let f = lj_soft.force_factor(r * r);
let dv_dr_analytical = -f * r;
assert_relative_eq!(dv_dr_analytical, dv_dr_numerical, epsilon = 1e-6);
}
}