use super::IsotropicTwobodyEnergy;
use crate::Cutoff;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(Deserialize, Serialize),
serde(deny_unknown_fields)
)]
pub struct Harmonic {
#[cfg_attr(feature = "serde", serde(rename = "req"))]
eq_distance: f64,
#[cfg_attr(feature = "serde", serde(rename = "k"))]
spring_constant: f64,
}
impl Harmonic {
pub const fn new(eq_distance: f64, spring_constant: f64) -> Self {
Self {
eq_distance,
spring_constant,
}
}
pub const fn eq_distance(&self) -> f64 {
self.eq_distance
}
pub const fn spring_constant(&self) -> f64 {
self.spring_constant
}
}
impl IsotropicTwobodyEnergy for Harmonic {
#[inline(always)]
fn isotropic_twobody_energy(&self, distance_squared: f64) -> f64 {
0.5 * self.spring_constant * (distance_squared.sqrt() - self.eq_distance).powi(2)
}
#[inline(always)]
fn isotropic_twobody_force(&self, distance_squared: f64) -> f64 {
let r = distance_squared.sqrt();
-self.spring_constant * (r - self.eq_distance) / (2.0 * r)
}
}
impl Cutoff for Harmonic {
fn cutoff(&self) -> f64 {
f64::INFINITY
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_harmonic_force() {
let h = Harmonic::new(2.0, 100.0);
assert_relative_eq!(h.isotropic_twobody_force(4.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(
h.isotropic_twobody_force(9.0),
-100.0 / 6.0,
epsilon = 1e-10
);
assert_relative_eq!(h.isotropic_twobody_force(1.0), 50.0, epsilon = 1e-10);
}
}