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}