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 PeriodicDihedral {
#[cfg_attr(feature = "serde", serde(rename = "phi"))]
phase_angle: f64,
#[cfg_attr(feature = "serde", serde(rename = "k"))]
spring_constant: f64,
#[cfg_attr(feature = "serde", serde(rename = "n"))]
periodicity: f64,
}
impl PeriodicDihedral {
pub const fn new(phase_angle: f64, spring_constant: f64, periodicity: f64) -> Self {
Self {
phase_angle,
spring_constant,
periodicity,
}
}
pub const fn phase_angle(&self) -> f64 {
self.phase_angle
}
pub const fn spring_constant(&self) -> f64 {
self.spring_constant
}
pub const fn periodicity(&self) -> f64 {
self.periodicity
}
}
impl FourbodyAngleEnergy for PeriodicDihedral {
#[inline(always)]
fn fourbody_angle_energy(&self, angle: f64) -> f64 {
let angle_rad = angle.to_radians();
let phase_rad = self.phase_angle.to_radians();
self.spring_constant * (1.0 + (self.periodicity * angle_rad - phase_rad).cos())
}
#[inline(always)]
fn fourbody_angle_force(&self, angle: f64) -> f64 {
let angle_rad = angle.to_radians();
let phase_rad = self.phase_angle.to_radians();
self.spring_constant
* self.periodicity
* (self.periodicity * angle_rad - phase_rad).sin()
* std::f64::consts::PI
/ 180.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn periodic_dihedral_energy() {
let pot = PeriodicDihedral::new(180.0, 10.0, 2.0);
assert_relative_eq!(pot.fourbody_angle_energy(0.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(pot.fourbody_angle_energy(90.0), 20.0, epsilon = 1e-10);
let pot2 = PeriodicDihedral::new(0.0, 5.0, 3.0);
assert_relative_eq!(pot2.fourbody_angle_energy(60.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(pot2.fourbody_angle_energy(0.0), 10.0, epsilon = 1e-10);
let pot3 = PeriodicDihedral::new(0.0, 1.0, 1.0);
assert_relative_eq!(pot3.fourbody_angle_energy(180.0), 0.0, epsilon = 1e-10);
}
#[test]
fn test_periodic_dihedral_force() {
let pot = PeriodicDihedral::new(0.0, 100.0, 3.0);
assert_relative_eq!(pot.fourbody_angle_energy(120.0), 200.0, epsilon = 1e-10);
assert_relative_eq!(pot.fourbody_angle_energy(60.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(pot.fourbody_angle_energy(90.0), 100.0, epsilon = 1e-10);
assert_relative_eq!(
pot.fourbody_angle_force(90.0),
-300.0 * std::f64::consts::PI / 180.0,
epsilon = 1e-10
);
assert_relative_eq!(pot.fourbody_angle_force(60.0), 0.0, epsilon = 1e-10);
assert_relative_eq!(pot.fourbody_angle_force(120.0), 0.0, epsilon = 1e-10);
}
}