Skip to main content

hoomd_microstate/property/
dynamic_oriented_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 `DynamicOrientedPoint`
5
6use serde::{Deserialize, Serialize};
7
8use super::{
9    AngularMomentum, Mass, MomentOfInertia, Momentum, NetForce, NetTorque, Orientation,
10    OrientedPoint, Point, Position, RotationalMotionTypes,
11};
12use crate::{Transform, property::NetVirial};
13use hoomd_vector::{Angle, Cartesian, Outer, Rotate, Rotation, Vector, Versor, Wedge};
14
15impl RotationalMotionTypes for Angle {
16    type MomentOfInertia = f64;
17    type AngularMomentum = f64;
18}
19
20impl RotationalMotionTypes for Versor {
21    type MomentOfInertia = [f64; 3];
22    type AngularMomentum = Cartesian<3>;
23}
24
25/// A position in space with the properties necessary for translational and
26/// rotational motion in MD.
27///
28/// Use [`DynamicOrientedPoint`] as a [`Body`](crate::Body) property type.
29///
30/// A default [`DynamicOrientedPoint`] has a mass of 1.0 and position, momentum,
31/// and net force of $` \vec{0} `$, and a zero-tensor for net virial.
32/// Orientation defaults to the identity. `DynamicOrientedPoint<_, Angle>` has a
33/// default moment of inertia of 1.0. `DynamicOrientedPoint<_, Versor>` has a
34/// default moment of inertia of `[1.0, 1.0, 1.0]`.
35///
36/// # Example
37///
38/// ```
39/// use hoomd_microstate::property::DynamicOrientedPoint;
40/// use hoomd_vector::{Angle, Cartesian};
41/// use std::f64::consts::PI;
42///
43/// let oriented_dynamic_point = DynamicOrientedPoint {
44///     position: Cartesian::from([1.0, -3.0]),
45///     orientation: Angle::from(PI / 4.0),
46///     ..Default::default()
47/// };
48/// ```
49#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
50pub struct DynamicOrientedPoint<V, R>
51where
52    V: Wedge + Outer,
53    R: RotationalMotionTypes,
54{
55    /// The location of the extended body in space $`[\mathrm{length}]`$.
56    pub position: V,
57
58    /// Rotate from the body's reference frame to the system frame.
59    pub orientation: R,
60
61    /// The mass of the extended body $` [\mathrm{mass}] `$.
62    pub mass: f64,
63
64    /// The translational momentum of the extended body $`[ \mathrm{energy}^{1/2} \cdot \mathrm{mass}^{1/2}]`$.
65    pub momentum: V,
66
67    /// The net force applied to the body in a [`Microstate`](crate::Microstate) $`[ \mathrm{energy}^{1/2} \cdot \mathrm{mass}^{1/2}]`$.
68    pub net_force: V,
69
70    /// The net virial applied to the body in a [`Microstate`](crate::Microstate) $`[\mathrm{energy}]`$.
71    pub net_virial: V::Tensor,
72
73    /// The moment of inertia of the extended body $` [\mathrm{mass} \cdot \mathrm{length}^2] `$.
74    pub moment_of_inertia: R::MomentOfInertia,
75
76    /// The angular momentum of the extended body $` [\mathrm{mass} \cdot \mathrm{length}^2] `$.
77    pub angular_momentum: R::AngularMomentum,
78
79    /// The net torque applied to the body by others in a [`Microstate`](crate::Microstate) $` [\mathrm{energy}] `$.
80    pub net_torque: V::Bivector,
81}
82
83impl<V> Default for DynamicOrientedPoint<V, Angle>
84where
85    V: Default + Wedge + Outer,
86    V::Tensor: Default,
87    V::Bivector: Default,
88{
89    /// Construct a [`DynamicOrientedPoint`] with mass 1.0 and moment of inertia 1.0.
90    /// Position, orientation, momentum, angular momentum, net force, net virial, and net torque are set to 0.
91    ///
92    /// # Example
93    ///
94    /// ```
95    /// use hoomd_linear_algebra::{GeneralMatrix, matrix::Matrix};
96    /// use hoomd_microstate::property::DynamicOrientedPoint;
97    /// use hoomd_vector::{Angle, Cartesian};
98    ///
99    /// let dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
100    /// assert_eq!(dynamic_point.mass, 1.0);
101    /// assert_eq!(dynamic_point.moment_of_inertia, 1.0);
102    /// assert_eq!(dynamic_point.position, [0.0, 0.0].into());
103    /// assert_eq!(dynamic_point.orientation, 0.0.into());
104    /// assert_eq!(dynamic_point.momentum, [0.0, 0.0].into());
105    /// assert_eq!(dynamic_point.angular_momentum, 0.0.into());
106    /// assert_eq!(dynamic_point.net_force, [0.0, 0.0].into());
107    /// assert_eq!(dynamic_point.net_virial, Matrix::zeros());
108    /// assert_eq!(dynamic_point.net_torque, 0.0);
109    /// ```
110    #[inline]
111    fn default() -> Self {
112        Self {
113            position: Default::default(),
114            orientation: Angle::default(),
115            mass: 1.0,
116            moment_of_inertia: 1.0,
117            momentum: Default::default(),
118            angular_momentum: Default::default(),
119            net_force: Default::default(),
120            net_virial: V::Tensor::default(),
121            net_torque: Default::default(),
122        }
123    }
124}
125
126impl<V> Default for DynamicOrientedPoint<V, Versor>
127where
128    V: Default + Wedge + Outer,
129    V::Bivector: Default,
130{
131    /// Construct a [`DynamicOrientedPoint`] with mass 1.0 and moment of inertia 1.0 on all axes.
132    /// Position, momentum, angular momentum, net force, and net torque are set to 0.
133    /// Orientation is set to the identity versor.
134    ///
135    /// # Example
136    ///
137    /// ```
138    /// use hoomd_microstate::property::DynamicOrientedPoint;
139    /// use hoomd_vector::{Cartesian, Versor};
140    ///
141    /// let dynamic_point = DynamicOrientedPoint::<Cartesian<3>, Versor>::default();
142    /// assert_eq!(dynamic_point.mass, 1.0);
143    /// assert_eq!(dynamic_point.moment_of_inertia, [1.0, 1.0, 1.0]);
144    /// assert_eq!(dynamic_point.position, [0.0, 0.0, 0.0].into());
145    /// assert_eq!(dynamic_point.orientation, Versor::default());
146    /// assert_eq!(dynamic_point.momentum, [0.0, 0.0, 0.0].into());
147    /// assert_eq!(dynamic_point.angular_momentum, [0.0, 0.0, 0.0].into());
148    /// assert_eq!(dynamic_point.net_force, [0.0, 0.0, 0.0].into());
149    /// assert_eq!(dynamic_point.net_torque, [0.0, 0.0, 0.0].into());
150    /// ```
151    #[inline]
152    fn default() -> Self {
153        Self {
154            position: Default::default(),
155            orientation: Versor::default(),
156            mass: 1.0,
157            moment_of_inertia: [1.0, 1.0, 1.0],
158            momentum: Default::default(),
159            angular_momentum: Cartesian::default(),
160            net_force: Default::default(),
161            net_virial: V::default().outer(&V::default()),
162            net_torque: Default::default(),
163        }
164    }
165}
166
167impl<V, R> Transform<Point<V>> for DynamicOrientedPoint<V, R>
168where
169    V: Vector + Wedge + Outer,
170    R: Rotate<V> + RotationalMotionTypes,
171{
172    /// Move [`Point`] properties from the local body frame to the system frame.
173    ///
174    /// ```math
175    /// \vec{r} = \vec{r}_\mathrm{body} + R_\mathrm{body}(\vec{r}_\mathrm{site})
176    /// ```
177    ///
178    /// ```
179    /// use approxim::assert_relative_eq;
180    /// use hoomd_microstate::{
181    ///     Transform,
182    ///     property::{DynamicOrientedPoint, Point},
183    /// };
184    /// use hoomd_vector::{Angle, Cartesian};
185    /// use std::f64::consts::PI;
186    ///
187    /// let body_properties = DynamicOrientedPoint {
188    ///     position: Cartesian::from([1.0, -2.0]),
189    ///     orientation: Angle::from(PI / 2.0),
190    ///     ..Default::default()
191    /// };
192    /// let site_properties = Point::new(Cartesian::from([-1.0, 0.0]));
193    ///
194    /// let system_site = body_properties.transform(&site_properties);
195    /// assert_relative_eq!(system_site.position, [1.0, -3.0].into());
196    /// ```
197    #[inline]
198    fn transform(&self, site_properties: &Point<V>) -> Point<V> {
199        Point {
200            position: self.position + self.orientation.rotate(&site_properties.position),
201        }
202    }
203}
204
205impl<V, R> Transform<OrientedPoint<V, R>> for DynamicOrientedPoint<V, R>
206where
207    V: Vector + Wedge + Outer,
208    R: Rotate<V> + Rotation + RotationalMotionTypes,
209{
210    /// Move [`OrientedPoint`] site properties from the local body frame to the system frame.
211    ///
212    /// ```math
213    /// \vec{r} = \vec{r}_\mathrm{body} + R_\mathrm{body}(\vec{r}_\mathrm{site})
214    /// ```
215    /// ```math
216    /// R = R_\mathrm{body}(R_\mathrm{site})
217    /// ```
218    ///
219    /// ```
220    /// use approxim::assert_relative_eq;
221    /// use hoomd_microstate::{
222    ///     Transform,
223    ///     property::{DynamicOrientedPoint, OrientedPoint},
224    /// };
225    /// use hoomd_vector::{Angle, Cartesian};
226    /// use std::f64::consts::PI;
227    ///
228    /// let body_properties = DynamicOrientedPoint {
229    ///     position: Cartesian::from([1.0, -2.0]),
230    ///     orientation: Angle::from(PI / 2.0),
231    ///     ..Default::default()
232    /// };
233    /// let site_properties = OrientedPoint {
234    ///     position: Cartesian::from([-1.0, 0.0]),
235    ///     orientation: Angle::from(PI / 4.0),
236    /// };
237    ///
238    /// let system_site = body_properties.transform(&site_properties);
239    /// assert_relative_eq!(system_site.position, [1.0, -3.0].into());
240    /// assert_relative_eq!(system_site.orientation.theta, 3.0 * PI / 4.0);
241    /// ```
242    #[inline]
243    fn transform(&self, site_properties: &OrientedPoint<V, R>) -> OrientedPoint<V, R> {
244        OrientedPoint {
245            position: self.position + self.orientation.rotate(&site_properties.position),
246            orientation: self.orientation.combine(&site_properties.orientation),
247        }
248    }
249}
250
251impl<V, R> Position for DynamicOrientedPoint<V, R>
252where
253    V: Wedge + Outer,
254    R: RotationalMotionTypes,
255{
256    type Position = V;
257
258    #[inline]
259    fn position(&self) -> &V {
260        &self.position
261    }
262
263    #[inline]
264    fn position_mut(&mut self) -> &mut V {
265        &mut self.position
266    }
267}
268
269impl<V, R> Orientation for DynamicOrientedPoint<V, R>
270where
271    V: Wedge + Outer,
272    R: RotationalMotionTypes,
273{
274    type Rotation = R;
275
276    #[inline]
277    fn orientation(&self) -> &Self::Rotation {
278        &self.orientation
279    }
280
281    #[inline]
282    fn orientation_mut(&mut self) -> &mut Self::Rotation {
283        &mut self.orientation
284    }
285}
286
287impl<V, R> Momentum for DynamicOrientedPoint<V, R>
288where
289    V: std::ops::Mul<f64, Output = V> + std::ops::Div<f64, Output = V> + Copy + Wedge + Outer,
290    R: RotationalMotionTypes,
291{
292    type Momentum = V;
293
294    #[inline]
295    fn momentum(&self) -> &V {
296        &self.momentum
297    }
298
299    #[inline]
300    fn momentum_mut(&mut self) -> &mut V {
301        &mut self.momentum
302    }
303
304    #[inline]
305    fn velocity(&self) -> Self::Momentum {
306        self.momentum / self.mass()
307    }
308
309    #[inline]
310    fn set_velocity(&mut self, velocity: Self::Momentum) {
311        *self.momentum_mut() = velocity * self.mass();
312    }
313}
314
315impl<V, R> Mass for DynamicOrientedPoint<V, R>
316where
317    V: Wedge + Outer,
318    R: RotationalMotionTypes,
319{
320    #[inline]
321    fn mass(&self) -> f64 {
322        self.mass
323    }
324}
325
326impl<V, R> NetForce for DynamicOrientedPoint<V, R>
327where
328    V: Wedge + Outer,
329    R: RotationalMotionTypes,
330{
331    type NetForce = V;
332
333    #[inline]
334    fn net_force(&self) -> &Self::NetForce {
335        &self.net_force
336    }
337
338    #[inline]
339    fn net_force_mut(&mut self) -> &mut Self::NetForce {
340        &mut self.net_force
341    }
342}
343
344impl<V, R> NetVirial for DynamicOrientedPoint<V, R>
345where
346    V: Wedge + Outer,
347    R: RotationalMotionTypes,
348{
349    type NetVirial = V::Tensor;
350
351    #[inline]
352    fn net_virial(&self) -> &Self::NetVirial {
353        &self.net_virial
354    }
355
356    #[inline]
357    fn net_virial_mut(&mut self) -> &mut Self::NetVirial {
358        &mut self.net_virial
359    }
360}
361
362impl<V, R> MomentOfInertia for DynamicOrientedPoint<V, R>
363where
364    V: Wedge + Outer,
365    R: RotationalMotionTypes,
366{
367    type MomentOfInertia = R::MomentOfInertia;
368
369    #[inline]
370    fn moment_of_inertia(&self) -> &R::MomentOfInertia {
371        &self.moment_of_inertia
372    }
373
374    #[inline]
375    fn moment_of_inertia_mut(&mut self) -> &mut R::MomentOfInertia {
376        &mut self.moment_of_inertia
377    }
378}
379
380impl<V, R> AngularMomentum for DynamicOrientedPoint<V, R>
381where
382    V: Wedge + Outer,
383    R: RotationalMotionTypes,
384{
385    type AngularMomentum = R::AngularMomentum;
386
387    #[inline]
388    fn angular_momentum(&self) -> &R::AngularMomentum {
389        &self.angular_momentum
390    }
391
392    #[inline]
393    fn angular_momentum_mut(&mut self) -> &mut R::AngularMomentum {
394        &mut self.angular_momentum
395    }
396}
397
398impl<V, R> NetTorque for DynamicOrientedPoint<V, R>
399where
400    V: Wedge + Outer,
401    R: RotationalMotionTypes,
402{
403    type NetTorque = V::Bivector;
404
405    #[inline]
406    fn net_torque(&self) -> &V::Bivector {
407        &self.net_torque
408    }
409
410    #[inline]
411    fn net_torque_mut(&mut self) -> &mut V::Bivector {
412        &mut self.net_torque
413    }
414}
415
416#[cfg(test)]
417mod test {
418    use super::*;
419
420    use hoomd_vector::Cartesian;
421
422    #[test]
423    fn position() {
424        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
425
426        *dynamic_point.position_mut() = [1.0, 2.0].into();
427        assert_eq!(dynamic_point.position, [1.0, 2.0].into());
428        assert_eq!(dynamic_point.position(), &[1.0, 2.0].into());
429    }
430
431    #[test]
432    fn orientation() {
433        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
434
435        *dynamic_point.orientation_mut() = 1.0.into();
436        assert_eq!(dynamic_point.orientation, 1.0.into());
437        assert_eq!(dynamic_point.orientation(), &1.0.into());
438    }
439
440    #[test]
441    fn mass() {
442        let dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle> {
443            mass: 3.0,
444            ..Default::default()
445        };
446
447        assert_eq!(dynamic_point.mass(), 3.0);
448    }
449
450    #[test]
451    fn momentum() {
452        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
453
454        *dynamic_point.momentum_mut() = [1.0, 2.0].into();
455        assert_eq!(dynamic_point.momentum, [1.0, 2.0].into());
456        assert_eq!(dynamic_point.momentum(), &[1.0, 2.0].into());
457    }
458
459    #[test]
460    fn net_force() {
461        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
462
463        *dynamic_point.net_force_mut() = [1.0, 2.0].into();
464        assert_eq!(dynamic_point.net_force, [1.0, 2.0].into());
465        assert_eq!(dynamic_point.net_force(), &[1.0, 2.0].into());
466    }
467
468    #[test]
469    fn moment_of_inertia() {
470        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
471
472        *dynamic_point.moment_of_inertia_mut() = 2.0;
473        assert_eq!(dynamic_point.moment_of_inertia, 2.0);
474        assert_eq!(dynamic_point.moment_of_inertia(), &2.0);
475    }
476
477    #[test]
478    fn angular_momentum() {
479        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
480
481        *dynamic_point.angular_momentum_mut() = 2.0;
482        assert_eq!(dynamic_point.angular_momentum, 2.0);
483        assert_eq!(dynamic_point.angular_momentum(), &2.0);
484    }
485
486    #[test]
487    fn net_torque() {
488        let mut dynamic_point = DynamicOrientedPoint::<Cartesian<2>, Angle>::default();
489
490        *dynamic_point.net_torque_mut() = 2.0;
491        assert_eq!(dynamic_point.net_torque, 2.0);
492        assert_eq!(dynamic_point.net_torque(), &2.0);
493    }
494}