use serde::{Deserialize, Serialize};
use hoomd_microstate::property::Position;
use hoomd_vector::{InnerProduct, Unit};
use super::super::SiteEnergy;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Linear<V> {
pub alpha: f64,
pub plane_origin: V,
pub plane_normal: Unit<V>,
}
impl<V> Linear<V>
where
V: InnerProduct,
{
#[inline]
#[must_use]
pub fn energy(&self, r: &V) -> f64 {
self.alpha * self.plane_normal.get().dot(&(*r - self.plane_origin))
}
}
impl<S, P> SiteEnergy<S> for Linear<P>
where
S: Position<Position = P>,
P: InnerProduct,
{
#[inline]
fn site_energy(&self, site_properties: &S) -> f64 where {
self.energy(site_properties.position())
}
}
#[cfg(test)]
mod tests {
use hoomd_vector::Cartesian;
use super::*;
use approxim::assert_relative_eq;
use rstest::*;
#[rstest]
fn energy_2d(
#[values(1.0, 0.0, -2.0)] alpha: f64,
#[values([0.0, 0.0], [-10.0, 15.0], [16.0, 3.0])] plane_origin: [f64; 2],
#[values([1.0, 1.0], [-1.0, 0.2], [-5.0, -1.0])] plane_normal: [f64; 2],
) {
let n = Unit::<Cartesian<2>>::try_from(plane_normal)
.expect("hard-coded vector should have non-zero length");
let linear = Linear {
plane_origin: plane_origin.into(),
plane_normal: n,
alpha,
};
let p = linear.plane_origin + *n.get() * 5.0;
assert_relative_eq!(linear.energy(&p), 5.0 * alpha, epsilon = 1e-6);
}
}