use crate::twobody::IsotropicTwobodyEnergy;
use crate::{
twobody::{LennardJones, WeeksChandlerAndersen},
Cutoff,
};
use std::fmt;
use std::fmt::{Display, Formatter};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(
feature = "serde",
derive(Deserialize, Serialize),
serde(deny_unknown_fields)
)]
pub struct AshbaughHatch {
#[cfg_attr(feature = "serde", serde(flatten))]
pub(crate) lennard_jones: LennardJones,
#[cfg_attr(feature = "serde", serde(alias = "λ", default))]
lambda: f64,
#[cfg_attr(feature = "serde", serde(alias = "rc", default))]
cutoff: f64,
}
impl AshbaughHatch {
pub const fn new(lennard_jones: LennardJones, lambda: f64, cutoff: f64) -> Self {
Self {
lennard_jones,
lambda,
cutoff,
}
}
}
impl Cutoff for AshbaughHatch {
fn cutoff_squared(&self) -> f64 {
self.cutoff * self.cutoff
}
fn cutoff(&self) -> f64 {
self.cutoff
}
fn lower_cutoff(&self) -> f64 {
self.lennard_jones.lower_cutoff()
}
}
impl IsotropicTwobodyEnergy for AshbaughHatch {
#[inline(always)]
fn isotropic_twobody_energy(&self, distance_squared: f64) -> f64 {
if distance_squared > self.cutoff_squared() {
return 0.0;
}
let lj = self
.lennard_jones
.isotropic_twobody_energy(distance_squared);
let lj_rc = self
.lennard_jones
.isotropic_twobody_energy(self.cutoff_squared());
if distance_squared
<= self.lennard_jones.sigma_squared * WeeksChandlerAndersen::TWOTOTWOSIXTH
{
self.lennard_jones
.epsilon()
.mul_add(1.0 - self.lambda, self.lambda.mul_add(-lj_rc, lj))
} else {
self.lambda * (lj - lj_rc)
}
}
#[inline(always)]
fn isotropic_twobody_force(&self, distance_squared: f64) -> f64 {
if distance_squared > self.cutoff_squared() {
return 0.0;
}
self.lambda * self.lennard_jones.isotropic_twobody_force(distance_squared)
}
}
impl Display for AshbaughHatch {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(
f,
"Ashbaugh-Hatch with λ = {:.3}, cutoff = {:.3}, ε = {:.3}, σ = {:.3}",
self.lambda,
self.cutoff,
self.lennard_jones.epsilon(),
self.lennard_jones.sigma()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_ashbaugh_hatch_force() {
let lj = LennardJones::new(1.5, 2.0);
let cutoff = 5.0;
let ah1 = AshbaughHatch::new(lj.clone(), 1.0, cutoff);
assert_relative_eq!(
ah1.isotropic_twobody_force(6.25),
lj.isotropic_twobody_force(6.25)
);
let ah05 = AshbaughHatch::new(lj.clone(), 0.5, cutoff);
assert_relative_eq!(
ah05.isotropic_twobody_force(6.25),
0.5 * lj.isotropic_twobody_force(6.25)
);
assert_relative_eq!(ah05.isotropic_twobody_force(30.0), 0.0);
}
}