Skip to main content

hoomd_interaction/external/
constant_force.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 [`ConstantForce`]
5
6use serde::{Deserialize, Serialize};
7
8use hoomd_microstate::property::Position;
9use hoomd_vector::{InnerProduct, Outer, Wedge};
10
11use crate::{SiteForceAndVirial, SiteForceVirialAndTorque};
12
13use super::super::SiteEnergy;
14
15/// Apply the same force to every site, independent of the site's properties.
16///
17/// The field `force` sets the force vector $` \vec{F} `$. The corresponding
18/// potential energy $` U `$ is:
19/// ```math
20/// U = - \vec{F} \cdot ( \vec{r} - \vec{r}_0 )
21/// ```
22/// The vector $` \vec{r}_0 `$ sets the reference plane where $` U = 0 `$.
23///
24/// # Generics
25///
26/// * `V`: The type used to represent the position and force vectors.
27///
28/// # Example
29///
30/// Basic usage:
31///
32/// ```
33/// use hoomd_interaction::external::ConstantForce;
34/// use hoomd_vector::{Cartesian, Unit};
35///
36/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
37/// let constant_force = ConstantForce {
38///     force: Cartesian::from([0.0, -2.0]),
39///     r_0: Cartesian::from([0.0, -10.0]),
40/// };
41/// # Ok(())
42/// # }
43/// ```
44#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
45pub struct ConstantForce<V> {
46    /// Force vector $`[\mathrm{energy}] \cdot [\mathrm{length}]^{-1}`$.
47    pub force: V,
48
49    ///  $` \vec{r}_0 `$ $`[\mathrm{length}]`$: A point on the plane where $` U = 0 `$.
50    pub r_0: V,
51}
52
53impl<V> ConstantForce<V>
54where
55    V: InnerProduct,
56{
57    /// Compute the energy of a point in a constant force field.
58    ///
59    /// # Example
60    ///
61    /// ```
62    /// use hoomd_interaction::external::ConstantForce;
63    /// use hoomd_vector::Cartesian;
64    ///
65    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
66    /// let constant_force = ConstantForce {
67    ///     force: Cartesian::from([0.0, -2.0]),
68    ///     r_0: Cartesian::from([0.0, -10.0]),
69    /// };
70    ///
71    /// let energy = constant_force.energy(&[0.0, 0.0].into());
72    /// assert_eq!(energy, 20.0);
73    /// # Ok(())
74    /// # }
75    /// ```
76    #[inline]
77    #[must_use]
78    pub fn energy(&self, r: &V) -> f64 {
79        let magnitude = self.force.norm();
80
81        if magnitude == 0.0 {
82            return 0.0;
83        }
84
85        let direction = self.force / magnitude;
86        -magnitude * direction.dot(&(*r - self.r_0))
87    }
88
89    /// The force vector that acts on all sites.
90    ///
91    /// # Example
92    ///
93    /// ```
94    /// use hoomd_interaction::external::ConstantForce;
95    /// use hoomd_vector::Cartesian;
96    ///
97    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
98    /// let constant_force = ConstantForce {
99    ///     force: Cartesian::from([0.0, -2.0]),
100    ///     r_0: Cartesian::from([0.0, -10.0]),
101    /// };
102    ///
103    /// let force = constant_force.force();
104    /// assert_eq!(force, [0.0, -2.0].into());
105    /// # Ok(())
106    /// # }
107    /// ```
108    #[inline]
109    #[must_use]
110    pub fn force(&self) -> V {
111        self.force
112    }
113}
114
115impl<S, P> SiteEnergy<S> for ConstantForce<P>
116where
117    S: Position<Position = P>,
118    P: InnerProduct,
119{
120    /// Evaluate the energy contribution of a single site.
121    ///
122    /// # Example
123    ///
124    /// ```
125    /// use hoomd_interaction::{external::ConstantForce, SiteEnergy};
126    /// use hoomd_vector::Cartesian;
127    /// use hoomd_microstate::property::Point;
128    ///
129    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
130    /// let constant_force = ConstantForce {
131    ///     force: Cartesian::from([0.0, -2.0]),
132    ///     r_0: Cartesian::from([0.0, -10.0]),
133    /// };
134    ///
135    /// let a = Point { position: Cartesian::from([0.0, 0.0]) };
136    /// let b = Point { position: Cartesian::from([0.0, 3.0]) };
137    ///
138    /// let energy_0 = constant_force.site_energy(&a);
139    /// assert_eq!(energy_0, 20.0);
140    //
141    /// let energy_1 = constant_force.site_energy(&b);
142    /// assert_eq!(energy_1, 26.0);
143    /// # Ok(())
144    /// # }
145    /// ```
146    #[inline]
147    fn site_energy(&self, site_properties: &S) -> f64 where {
148        self.energy(site_properties.position())
149    }
150}
151
152impl<S, V> SiteForceAndVirial<S> for ConstantForce<V>
153where
154    V: InnerProduct + Outer,
155    S: Position<Position = V>,
156{
157    type Force = V;
158
159    /// Evaluate the force and virial as a function of a single site's properties.
160    ///
161    /// # Example
162    ///
163    /// ```
164    /// use hoomd_interaction::{SiteForceAndVirial, external::ConstantForce};
165    /// use hoomd_linear_algebra::matrix::Matrix;
166    /// use hoomd_microstate::property::Point;
167    /// use hoomd_vector::Cartesian;
168    ///
169    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
170    /// let constant_force = ConstantForce {
171    ///     force: Cartesian::from([0.0, -2.0]),
172    ///     r_0: Cartesian::from([0.0, -10.0]),
173    /// };
174    ///
175    /// let a = Point {
176    ///     position: Cartesian::from([0.0, 0.0]),
177    /// };
178    /// let b = Point {
179    ///     position: Cartesian::from([0.0, 3.0]),
180    /// };
181    ///
182    /// let (force_0, virial_0) = constant_force.site_force_and_virial(&a);
183    /// assert_eq!(force_0, [0.0, -2.0].into());
184    /// assert_eq!(
185    ///     virial_0,
186    ///     Matrix {
187    ///         rows: [[0.0, 0.0], [0.0, 0.0]]
188    ///     }
189    /// );
190    ///
191    /// let (force_1, virial_1) = constant_force.site_force_and_virial(&b);
192    /// assert_eq!(force_1, [0.0, -2.0].into());
193    /// assert_eq!(
194    ///     virial_1,
195    ///     Matrix {
196    ///         rows: [[0.0, 0.0], [0.0, -6.0]]
197    ///     }
198    /// );
199    /// # Ok(())
200    /// # }
201    /// ```
202    #[inline]
203    fn site_force_and_virial(
204        &self,
205        site_properties: &S,
206    ) -> (Self::Force, <Self::Force as Outer>::Tensor) {
207        let force = self.force();
208        let virial = force.outer(site_properties.position());
209        (force, virial)
210    }
211}
212
213impl<S, V> SiteForceVirialAndTorque<S> for ConstantForce<V>
214where
215    V: InnerProduct + Wedge + Outer,
216    V::Bivector: Default,
217    S: Position<Position = V>,
218{
219    type Force = V;
220
221    /// Evaluate the force, virial, and torque as a function of a single site's properties.
222    ///
223    /// # Example
224    ///
225    /// ```
226    /// use hoomd_interaction::{
227    ///     SiteForceVirialAndTorque, external::ConstantForce,
228    /// };
229    /// use hoomd_linear_algebra::matrix::Matrix;
230    /// use hoomd_microstate::property::Point;
231    /// use hoomd_vector::Cartesian;
232    ///
233    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
234    /// let constant_force = ConstantForce {
235    ///     force: Cartesian::from([0.0, -2.0]),
236    ///     r_0: Cartesian::from([0.0, -10.0]),
237    /// };
238    ///
239    /// let a = Point {
240    ///     position: Cartesian::from([0.0, 0.0]),
241    /// };
242    /// let b = Point {
243    ///     position: Cartesian::from([0.0, 3.0]),
244    /// };
245    ///
246    /// let (force_0, virial_0, torque_0) =
247    ///     constant_force.site_force_virial_and_torque(&a);
248    /// assert_eq!(force_0, [0.0, -2.0].into());
249    /// assert_eq!(torque_0, 0.0);
250    /// assert_eq!(
251    ///     virial_0,
252    ///     Matrix {
253    ///         rows: [[0.0, 0.0], [0.0, 0.0]]
254    ///     }
255    /// );
256    ///
257    /// let (force_1, virial_1, torque_1) =
258    ///     constant_force.site_force_virial_and_torque(&b);
259    /// assert_eq!(force_1, [0.0, -2.0].into());
260    /// assert_eq!(torque_1, 0.0);
261    /// assert_eq!(
262    ///     virial_1,
263    ///     Matrix {
264    ///         rows: [[0.0, 0.0], [0.0, -6.0]]
265    ///     }
266    /// );
267    /// # Ok(())
268    /// # }
269    /// ```
270    #[inline]
271    fn site_force_virial_and_torque(
272        &self,
273        site_properties: &S,
274    ) -> (V, <Self::Force as Outer>::Tensor, V::Bivector) {
275        let force = self.force();
276        let virial = force.outer(site_properties.position());
277        (force, virial, V::Bivector::default())
278    }
279}