Skip to main content

hoomd_interaction/external/
linear.rs

1// Copyright (c) 2024-2026 The Regents of the University of Michigan.
2// Part of hoomd-rs, released under the BSD 3-Clause License.
3
4//! Implement [`Linear`]
5
6use serde::{Deserialize, Serialize};
7
8use hoomd_microstate::property::Position;
9use hoomd_vector::{InnerProduct, Unit};
10
11use super::super::SiteEnergy;
12
13/// Linear potential based on position.
14///
15/// ```math
16/// U = \alpha \cdot \vec{n} \cdot ( \vec{r} - \vec{p} )
17/// ```
18///
19/// Computes a linear external potential at a point in space relative to the plane
20/// origin `p`, plane normal `n`, and the interaction strength `alpha`.
21///
22/// # Example
23///
24/// Basic usage:
25///
26/// ```
27/// use hoomd_interaction::external::Linear;
28/// use hoomd_vector::{Cartesian, Unit};
29///
30/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
31/// let linear = Linear {
32///     alpha: 2.0,
33///     plane_origin: [0.0, -10.0].into(),
34///     plane_normal: [0.0, 1.0].try_into()?,
35/// };
36/// # Ok(())
37/// # }
38/// ```
39#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
40pub struct Linear<V> {
41    /// Interaction strength *(\[energy\] \[length\]^(-1))*.
42    pub alpha: f64,
43    /// Point on the plane where U=0 *(\[length\])*.
44    pub plane_origin: V,
45    /// Vector normal to the plane *(unitless)*.
46    pub plane_normal: Unit<V>,
47}
48
49impl<V> Linear<V>
50where
51    V: InnerProduct,
52{
53    /// Compute the energy of a point in the linear field.
54    ///
55    /// # Example
56    ///
57    /// ```
58    /// use hoomd_interaction::external::Linear;
59    /// use hoomd_vector::{Cartesian, Unit};
60    ///
61    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
62    /// let linear = Linear {
63    ///     alpha: 2.0,
64    ///     plane_origin: [0.0, -10.0].into(),
65    ///     plane_normal: [0.0, 1.0].try_into()?,
66    /// };
67    ///
68    /// let energy = linear.energy(&[0.0, 0.0].into());
69    /// assert_eq!(energy, 20.0);
70    /// # Ok(())
71    /// # }
72    /// ```
73    #[inline]
74    #[must_use]
75    pub fn energy(&self, r: &V) -> f64 {
76        self.alpha * self.plane_normal.get().dot(&(*r - self.plane_origin))
77    }
78}
79
80impl<S, P> SiteEnergy<S> for Linear<P>
81where
82    S: Position<Position = P>,
83    P: InnerProduct,
84{
85    #[inline]
86    fn site_energy(&self, site_properties: &S) -> f64 where {
87        self.energy(site_properties.position())
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use hoomd_vector::Cartesian;
94
95    use super::*;
96    use approxim::assert_relative_eq;
97    use rstest::*;
98
99    #[rstest]
100    fn energy_2d(
101        #[values(1.0, 0.0, -2.0)] alpha: f64,
102        #[values([0.0, 0.0], [-10.0, 15.0], [16.0, 3.0])] plane_origin: [f64; 2],
103        #[values([1.0, 1.0], [-1.0, 0.2], [-5.0, -1.0])] plane_normal: [f64; 2],
104    ) {
105        let n = Unit::<Cartesian<2>>::try_from(plane_normal)
106            .expect("hard-coded vector should have non-zero length");
107
108        let linear = Linear {
109            plane_origin: plane_origin.into(),
110            plane_normal: n,
111            alpha,
112        };
113
114        let p = linear.plane_origin + *n.get() * 5.0;
115        assert_relative_eq!(linear.energy(&p), 5.0 * alpha, epsilon = 1e-6);
116    }
117}