Skip to main content

hoomd_microstate/property/
dynamic_point.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 `DynamicPoint`
5
6use serde::{Deserialize, Serialize};
7
8use super::{Mass, Momentum, NetForce, OrientedPoint, Point, Position};
9use crate::{Transform, property::NetVirial};
10use hoomd_vector::{Outer, Vector};
11
12/// A position in space with the properties necessary for translational motion in MD.
13///
14/// Use [`DynamicPoint`] as a [`Body`](crate::Body) property type.
15///
16/// A default [`DynamicPoint`] has a mass of 1.0. Position, momentum, and net force
17/// of $` \vec{0} `$, and a zero-tensor for net virial.
18///
19/// # Example
20///
21/// ```
22/// use hoomd_microstate::property::DynamicPoint;
23/// use hoomd_vector::Cartesian;
24///
25/// let dynamic_point = DynamicPoint {
26///     position: Cartesian::from([1.0, -3.0]),
27///     mass: 1.0,
28///     ..Default::default()
29/// };
30/// ```
31#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
32pub struct DynamicPoint<V>
33where
34    V: Outer,
35{
36    /// The location of the extended body in space $`[\mathrm{length}]`$.
37    pub position: V,
38
39    /// The mass of the extended body $` [\mathrm{mass}] `$.
40    pub mass: f64,
41
42    /// The translational momentum of the extended body $`[\mathrm{mass} \cdot \mathrm{length}] \cdot \mathrm{time}^{-1}]`$.
43    pub momentum: V,
44
45    /// The net force applied to the body in a [`Microstate`](crate::Microstate) $`[\mathrm{mass} \cdot \mathrm{length}] \cdot \mathrm{time}^{-2}]`$.
46    pub net_force: V,
47
48    /// The net virial applied to the body in a [`Microstate`](crate::Microstate) $`[\mathrm{energy}]`$.
49    pub net_virial: V::Tensor,
50}
51
52impl<V> Default for DynamicPoint<V>
53where
54    V: Default + Outer,
55    V::Tensor: Default,
56{
57    /// Construct a [`DynamicPoint`] with mass 1.0. Position, momentum, and net force are set
58    /// to the 0 vector.
59    ///
60    /// # Example
61    ///
62    /// ```
63    /// use hoomd_linear_algebra::{GeneralMatrix, matrix::Matrix};
64    /// use hoomd_microstate::property::DynamicPoint;
65    /// use hoomd_vector::Cartesian;
66    ///
67    /// let dynamic_point = DynamicPoint::<Cartesian<3>>::default();
68    /// assert_eq!(dynamic_point.mass, 1.0);
69    /// assert_eq!(dynamic_point.position, [0.0, 0.0, 0.0].into());
70    /// assert_eq!(dynamic_point.momentum, [0.0, 0.0, 0.0].into());
71    /// assert_eq!(dynamic_point.net_force, [0.0, 0.0, 0.0].into());
72    /// assert_eq!(dynamic_point.net_virial, Matrix::zeros());
73    /// ```
74    #[inline]
75    fn default() -> Self {
76        Self {
77            position: Default::default(),
78            mass: 1.0,
79            momentum: Default::default(),
80            net_force: Default::default(),
81            net_virial: V::Tensor::default(),
82        }
83    }
84}
85
86impl<V: Vector + Outer> Transform<Point<V>> for DynamicPoint<V> {
87    /// [`DynamicPoint`] transforms [`Point`] by vector addition.
88    ///
89    /// ```math
90    /// \vec{r} = \vec{r}_\mathrm{body} + \vec{r}_\mathrm{site}
91    /// ```
92    ///
93    /// ```
94    /// use approxim::assert_relative_eq;
95    /// use hoomd_microstate::{
96    ///     Transform,
97    ///     property::{DynamicPoint, Point},
98    /// };
99    /// use hoomd_vector::Cartesian;
100    ///
101    /// let body_properties = DynamicPoint {
102    ///     position: Cartesian::from([1.0, -2.0, 3.0]),
103    ///     ..Default::default()
104    /// };
105    /// let site_properties = Point::new(Cartesian::from([-3.0, 2.0, 1.0]));
106    ///
107    /// let system_site = body_properties.transform(&site_properties);
108    /// assert_relative_eq!(system_site.position, [-2.0, 0.0, 4.0].into());
109    /// ```
110    #[inline]
111    fn transform(&self, site_properties: &Point<V>) -> Point<V> {
112        Point {
113            position: self.position + site_properties.position,
114        }
115    }
116}
117
118impl<V, R> Transform<OrientedPoint<V, R>> for DynamicPoint<V>
119where
120    V: Vector + Outer,
121    R: Copy,
122{
123    /// [`DynamicPoint`] transforms [`OrientedPoint`] by vector addition.
124    ///
125    /// ```math
126    /// \vec{r} = \vec{r}_\mathrm{body} + \vec{r}_\mathrm{site}
127    /// ```
128    ///
129    /// ```
130    /// use approxim::assert_relative_eq;
131    /// use hoomd_microstate::{
132    ///     Transform,
133    ///     property::{DynamicPoint, OrientedPoint},
134    /// };
135    /// use hoomd_vector::{Cartesian, Versor};
136    ///
137    /// let body_properties = DynamicPoint {
138    ///     position: Cartesian::from([1.0, -2.0, 3.0]),
139    ///     ..Default::default()
140    /// };
141    /// let site_properties = OrientedPoint {
142    ///     position: Cartesian::from([-3.0, 2.0, 1.0]),
143    ///     orientation: Versor::default(),
144    /// };
145    ///
146    /// let system_site = body_properties.transform(&site_properties);
147    /// assert_relative_eq!(system_site.position, [-2.0, 0.0, 4.0].into());
148    /// ```
149    #[inline]
150    fn transform(&self, site_properties: &OrientedPoint<V, R>) -> OrientedPoint<V, R> {
151        OrientedPoint {
152            position: self.position + site_properties.position,
153            ..*site_properties
154        }
155    }
156}
157
158impl<P: Outer> Position for DynamicPoint<P> {
159    type Position = P;
160
161    #[inline]
162    fn position(&self) -> &P {
163        &self.position
164    }
165
166    #[inline]
167    fn position_mut(&mut self) -> &mut P {
168        &mut self.position
169    }
170}
171
172impl<V> Momentum for DynamicPoint<V>
173where
174    V: std::ops::Mul<f64, Output = V> + std::ops::Div<f64, Output = V> + Copy + Outer,
175{
176    type Momentum = V;
177
178    #[inline]
179    fn momentum(&self) -> &V {
180        &self.momentum
181    }
182
183    #[inline]
184    fn momentum_mut(&mut self) -> &mut V {
185        &mut self.momentum
186    }
187
188    #[inline]
189    fn velocity(&self) -> Self::Momentum {
190        self.momentum / self.mass()
191    }
192
193    #[inline]
194    fn set_velocity(&mut self, velocity: Self::Momentum) {
195        *self.momentum_mut() = velocity * self.mass();
196    }
197}
198
199impl<V: Outer> Mass for DynamicPoint<V> {
200    #[inline]
201    fn mass(&self) -> f64 {
202        self.mass
203    }
204}
205
206impl<V: Outer> NetForce for DynamicPoint<V> {
207    type NetForce = V;
208
209    #[inline]
210    fn net_force(&self) -> &Self::NetForce {
211        &self.net_force
212    }
213
214    #[inline]
215    fn net_force_mut(&mut self) -> &mut Self::NetForce {
216        &mut self.net_force
217    }
218}
219
220impl<V: Outer> NetVirial for DynamicPoint<V> {
221    type NetVirial = V::Tensor;
222
223    #[inline]
224    fn net_virial(&self) -> &Self::NetVirial {
225        &self.net_virial
226    }
227
228    #[inline]
229    fn net_virial_mut(&mut self) -> &mut Self::NetVirial {
230        &mut self.net_virial
231    }
232}
233
234#[cfg(test)]
235mod test {
236    use super::*;
237
238    use hoomd_vector::Cartesian;
239
240    #[test]
241    fn position() {
242        let mut dynamic_point = DynamicPoint::<Cartesian<2>>::default();
243
244        *dynamic_point.position_mut() = [1.0, 2.0].into();
245        assert_eq!(dynamic_point.position, [1.0, 2.0].into());
246        assert_eq!(dynamic_point.position(), &[1.0, 2.0].into());
247    }
248
249    #[test]
250    fn mass() {
251        let dynamic_point = DynamicPoint::<Cartesian<2>> {
252            mass: 3.0,
253            ..Default::default()
254        };
255
256        assert_eq!(dynamic_point.mass(), 3.0);
257    }
258
259    #[test]
260    fn momentum() {
261        let mut dynamic_point = DynamicPoint::<Cartesian<2>>::default();
262
263        *dynamic_point.momentum_mut() = [1.0, 2.0].into();
264        assert_eq!(dynamic_point.momentum, [1.0, 2.0].into());
265        assert_eq!(dynamic_point.momentum(), &[1.0, 2.0].into());
266    }
267
268    #[test]
269    fn net_force() {
270        let mut dynamic_point = DynamicPoint::<Cartesian<2>>::default();
271
272        *dynamic_point.net_force_mut() = [1.0, 2.0].into();
273        assert_eq!(dynamic_point.net_force, [1.0, 2.0].into());
274        assert_eq!(dynamic_point.net_force(), &[1.0, 2.0].into());
275    }
276}