use super::FourbodyAngleEnergy;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(Deserialize, Serialize),
serde(deny_unknown_fields)
)]
pub struct HarmonicDihedral {
#[cfg_attr(feature = "serde", serde(rename = "aeq"))]
eq_angle: f64,
#[cfg_attr(feature = "serde", serde(rename = "k"))]
spring_constant: f64,
}
impl HarmonicDihedral {
pub const fn new(eq_angle: f64, spring_constant: f64) -> Self {
Self {
eq_angle,
spring_constant,
}
}
pub const fn eq_angle(&self) -> f64 {
self.eq_angle
}
pub const fn spring_constant(&self) -> f64 {
self.spring_constant
}
}
impl FourbodyAngleEnergy for HarmonicDihedral {
#[inline(always)]
fn fourbody_angle_energy(&self, angle: f64) -> f64 {
0.5 * self.spring_constant * (angle - self.eq_angle).powi(2)
}
#[inline(always)]
fn fourbody_angle_force(&self, angle: f64) -> f64 {
-self.spring_constant * (angle - self.eq_angle)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::consts::{FRAC_PI_2, FRAC_PI_6};
#[test]
fn test_harmonic_dihedral_force() {
let dihedral = HarmonicDihedral::new(FRAC_PI_2, 100.0);
let phi = 2.0 * std::f64::consts::PI / 3.0;
assert_relative_eq!(
dihedral.fourbody_angle_energy(phi),
13.7077838904,
epsilon = 1e-6
);
assert_relative_eq!(
dihedral.fourbody_angle_force(phi),
-100.0 * FRAC_PI_6,
epsilon = 1e-6
);
assert_relative_eq!(
dihedral.fourbody_angle_force(FRAC_PI_2),
0.0,
epsilon = 1e-10
);
}
}