use crate::base::Potential3;
use crate::math::Vector;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Cross<T> {
k: T,
r_ij0: T,
r_jk0: T,
}
impl<T: Vector> Cross<T> {
#[inline]
pub fn new(k: f64, r_ij0: f64, r_jk0: f64) -> Self {
Self {
k: T::splat(k),
r_ij0: T::splat(r_ij0),
r_jk0: T::splat(r_jk0),
}
}
#[inline]
pub fn symmetric(k: f64, r0: f64) -> Self {
Self::new(k, r0, r0)
}
#[inline]
pub fn k(&self) -> T {
self.k
}
}
impl<T: Vector> Potential3<T> for Cross<T> {
#[inline(always)]
fn energy(&self, r_ij_sq: T, r_jk_sq: T, _cos_theta: T) -> T {
let r_ij = r_ij_sq.sqrt();
let r_jk = r_jk_sq.sqrt();
let dr_ij = r_ij - self.r_ij0;
let dr_jk = r_jk - self.r_jk0;
self.k * dr_ij * dr_jk
}
#[inline(always)]
fn derivative(&self, _r_ij_sq: T, _r_jk_sq: T, _cos_theta: T) -> T {
T::zero()
}
#[inline(always)]
fn energy_derivative(&self, r_ij_sq: T, r_jk_sq: T, cos_theta: T) -> (T, T) {
(self.energy(r_ij_sq, r_jk_sq, cos_theta), T::zero())
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_cross_at_equilibrium() {
let r_ij0 = 1.5;
let r_jk0 = 1.6;
let cross: Cross<f64> = Cross::new(100.0, r_ij0, r_jk0);
let energy = cross.energy(r_ij0 * r_ij0, r_jk0 * r_jk0, 0.5);
assert_relative_eq!(energy, 0.0, epsilon = 1e-10);
}
#[test]
fn test_cross_derivative_zero() {
let cross: Cross<f64> = Cross::new(100.0, 1.5, 1.6);
let deriv = cross.derivative(2.0, 3.0, 0.5);
assert_relative_eq!(deriv, 0.0, epsilon = 1e-10);
}
#[test]
fn test_cross_sign() {
let k = 100.0;
let r0 = 1.0;
let cross: Cross<f64> = Cross::symmetric(k, r0);
let e_both_stretched = cross.energy(1.5 * 1.5, 1.5 * 1.5, 0.5);
assert!(e_both_stretched > 0.0);
let e_both_compressed = cross.energy(0.5 * 0.5, 0.5 * 0.5, 0.5);
assert!(e_both_compressed > 0.0);
let e_mixed = cross.energy(1.5 * 1.5, 0.5 * 0.5, 0.5);
assert!(e_mixed < 0.0);
}
#[test]
fn test_cross_value() {
let k = 50.0;
let r_ij0 = 1.0;
let r_jk0 = 1.0;
let cross: Cross<f64> = Cross::new(k, r_ij0, r_jk0);
let energy = cross.energy(1.5 * 1.5, 1.2 * 1.2, 0.5);
assert_relative_eq!(energy, 5.0, epsilon = 1e-10);
}
}