use crate::twobody::IsotropicTwobodyEnergy;
use crate::Cutoff;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Copy)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Mie<const N: u32, const M: u32> {
#[cfg_attr(feature = "serde", serde(alias = "eps", alias = "ε"))]
epsilon: f64,
#[cfg_attr(feature = "serde", serde(alias = "σ"))]
sigma: f64,
}
impl<const N: u32, const M: u32> Mie<N, M> {
const C: f64 = (N / (N - M) * (N / M).pow(M / (N - M))) as f64;
const OPTIMIZE: bool = N.is_multiple_of(2) && M.is_multiple_of(2);
const N_OVER_M: i32 = (N / M) as i32;
const M_HALF: i32 = (M / 2) as i32;
pub const fn new(epsilon: f64, sigma: f64) -> Self {
assert!(M > 0);
assert!(N > M);
Self { epsilon, sigma }
}
}
impl<const N: u32, const M: u32> IsotropicTwobodyEnergy for Mie<N, M> {
#[inline]
fn isotropic_twobody_energy(&self, distance_squared: f64) -> f64 {
if Self::OPTIMIZE {
let mth_power = (self.sigma * self.sigma / distance_squared).powi(Self::M_HALF); return Self::C * self.epsilon * (mth_power.powi(Self::N_OVER_M) - mth_power);
}
let s_over_r = self.sigma / distance_squared.sqrt(); Self::C * self.epsilon * (s_over_r.powi(N as i32) - s_over_r.powi(M as i32))
}
#[inline]
fn isotropic_twobody_force(&self, distance_squared: f64) -> f64 {
if Self::OPTIMIZE {
let x = (self.sigma * self.sigma / distance_squared).powi(Self::M_HALF);
let xn = x.powi(Self::N_OVER_M);
return Self::C * self.epsilon * (N as f64 * xn - M as f64 * x)
/ (2.0 * distance_squared);
}
let s_over_r = self.sigma / distance_squared.sqrt();
Self::C
* self.epsilon
* (N as f64 * s_over_r.powi(N as i32) - M as f64 * s_over_r.powi(M as i32))
/ (2.0 * distance_squared)
}
}
impl<const N: u32, const M: u32> Cutoff for Mie<N, M> {
fn cutoff(&self) -> f64 {
f64::INFINITY
}
fn cutoff_squared(&self) -> f64 {
f64::INFINITY
}
fn lower_cutoff(&self) -> f64 {
self.sigma * 0.6
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::twobody::LennardJones;
use approx::assert_relative_eq;
#[test]
fn test_mie_force() {
let (eps, sigma) = (1.5, 2.0);
let mie = Mie::<12, 6>::new(eps, sigma);
let lj = LennardJones::new(eps, sigma);
for r in [1.5, 2.0, 2.5, 3.0, 5.0] {
let r2 = r * r;
assert_relative_eq!(
mie.isotropic_twobody_force(r2),
lj.isotropic_twobody_force(r2),
epsilon = 1e-10
);
}
}
}