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}