Skip to main content

hoomd_md/compute/
mod.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//! Methods that compute properties of microstates.
5
6use hoomd_microstate::{Body, Tagged};
7
8mod rotational_kinetic_energy;
9mod translational_kinetic_energy;
10
11/// Compute the translational kinetic energy of bodies in a microstate.
12///
13/// `TranslationalKineticEnergy` is implemented for `Microstate`. Call
14/// `microstate.translational_kinetic_energy` to compute the total translational
15/// kinetic energy (and degrees of freedom) of all bodies in the microstate.
16/// Energy can be calculated for a subset of bodies using the companion method
17/// `translational_kinetic_energy_with_filter`.
18///
19/// Sum the per-body kinetic energies:
20/// ```math
21/// K = \sum_{i \in \mathrm{selection}} \frac{\vec{p}_i \cdot \vec{p}_i}{2m_i}
22/// ```
23///
24/// Count the degrees of freedom of each selected body:
25/// ```math
26/// \mathrm{degrees\_of\_freedom} = \sum_{i \in \mathrm{selection}} D
27/// ```
28/// where `D` is the dimensionality of momentum vector space.
29///
30/// # Example
31///
32/// ```
33/// use hoomd_md::TranslationalKineticEnergy;
34/// use hoomd_microstate::{
35///     Body, Microstate,
36///     property::{DynamicPoint, Point},
37/// };
38/// use hoomd_vector::Cartesian;
39///
40/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
41/// let microstate = Microstate::builder()
42///     .bodies([
43///         Body::single_site(
44///             DynamicPoint {
45///                 mass: 2.0,
46///                 momentum: Cartesian::<2>::from([2.0, 0.0]),
47///                 ..Default::default()
48///             },
49///             Point::default(),
50///         ),
51///         Body::single_site(
52///             DynamicPoint {
53///                 mass: 4.0,
54///                 momentum: Cartesian::<2>::from([1.0, 1.0]),
55///                 ..Default::default()
56///             },
57///             Point::default(),
58///         ),
59///         Body::single_site(
60///             DynamicPoint {
61///                 mass: 3.0,
62///                 momentum: Cartesian::<2>::from([-4.0, -2.0]),
63///                 ..Default::default()
64///             },
65///             Point::default(),
66///         ),
67///     ])
68///     .try_build()?;
69/// let (translational_kinetic_energy, translational_degrees_of_freedom) =
70///     microstate.translational_kinetic_energy();
71/// # Ok(())
72/// # }
73/// ```
74pub trait TranslationalKineticEnergy<B, S> {
75    /// Compute the total translational kinetic energy and degrees of freedom over all bodies
76    /// in the microstate.
77    #[inline]
78    fn translational_kinetic_energy(&self) -> (f64, usize) {
79        self.translational_kinetic_energy_with_filter(|_| true)
80    }
81
82    /// Compute the total translational kinetic energy and degrees of freedom over selected
83    /// bodies in the microstate.
84    fn translational_kinetic_energy_with_filter<F: Fn(&Tagged<Body<B, S>>) -> bool>(
85        &self,
86        should_sum_body: F,
87    ) -> (f64, usize);
88}
89
90/// Compute the rotational kinetic energy of bodies in a microstate.
91///
92/// `RotationalKineticEnergy` is implemented for `Microstate`. Call
93/// `microstate.rotational_kinetic_energy` to compute the total rotational
94/// kinetic energy (and degrees of freedom) of all bodies in the microstate.
95/// Energy can be calculated for a subset of bodies using the companion method
96/// `rotational_kinetic_energy_with_filter`.
97///
98/// # 2D
99///
100/// In 2D, each body has only 0 or 1 rotational degree of freedom. Set $` L = 0 `$ to deactivate
101/// rotations for a body. The total number of degrees of freedom is then:
102/// ```math
103/// \mathrm{degrees\_of\_freedom} = \sum_{i \in \mathrm{selection}} \left| L_i \ne 0 \right|
104/// ```
105/// where $` \left| \right| `$ is the Iverson bracket.
106///
107/// The kinetic energy is
108/// ```math
109/// K = \sum_{i \in \mathrm{selection}} \frac{L_i^2}{2I}
110/// ```
111/// (ignoring terms where the moment of inertia is zero).
112///
113/// # 3D
114///
115/// In 3D, there are 0 to 3 degrees of freedom per body.
116/// Set $` I_{xx}=0 `$, $` I_{yy}=0 `$, and/or $` I_{zz}=0 `$ to deactivate
117/// rotations one or more axes. The total number of degrees of freedom is then:
118/// ```math
119/// \mathrm{degrees\_of\_freedom} = \sum_{i \in \mathrm{selection}} \left| I_{xx,i} \ne 0 \right| + \left| I_{yy,i} \ne 0 \right| + \left| I_{zz,i} \ne 0 \right|
120/// ```
121///
122/// The kinetic energy is
123/// ```math
124/// K = \sum_{i \in \mathrm{selection}}\frac{L_{x,i}(t)^2}{2I_{xx,i}} + \frac{L_{y,i}(t)^2}{2I_{yy,i}} + \frac{L_{z,i}(t)^2}{2I_{zz,i}}
125/// ```
126/// (ignoring terms where the moment of inertia is zero).
127///
128/// # Example
129///
130/// ```
131/// use hoomd_md::RotationalKineticEnergy;
132/// use hoomd_microstate::{
133///     Body, Microstate,
134///     property::{DynamicOrientedPoint, Point},
135/// };
136/// use hoomd_vector::{Angle, Cartesian};
137///
138/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
139/// let microstate: Microstate<
140///     DynamicOrientedPoint<Cartesian<2>, Angle>,
141///     _,
142///     _,
143///     _,
144/// > = Microstate::builder()
145///     .bodies([
146///         Body::single_site(
147///             DynamicOrientedPoint {
148///                 moment_of_inertia: 0.0,
149///                 ..Default::default()
150///             },
151///             Point::default(),
152///         ),
153///         Body::single_site(
154///             DynamicOrientedPoint {
155///                 moment_of_inertia: 2.0,
156///                 angular_momentum: 8.0,
157///                 ..Default::default()
158///             },
159///             Point::default(),
160///         ),
161///         Body::single_site(
162///             DynamicOrientedPoint {
163///                 moment_of_inertia: 4.0,
164///                 angular_momentum: 3.0,
165///                 ..Default::default()
166///             },
167///             Point::default(),
168///         ),
169///     ])
170///     .try_build()?;
171///
172/// let (rotational_kinetic_energy, rotational_degrees_of_freedom) =
173///     microstate.rotational_kinetic_energy();
174/// # Ok(())
175/// # }
176/// ```
177pub trait RotationalKineticEnergy<B, S> {
178    /// Compute the total rotational kinetic energy and degrees of freedom over all bodies
179    /// in the microstate.
180    #[inline]
181    fn rotational_kinetic_energy(&self) -> (f64, usize) {
182        self.rotational_kinetic_energy_with_filter(|_| true)
183    }
184
185    /// Compute the total rotational kinetic energy and degrees of freedom over selected
186    /// bodies in the microstate.
187    fn rotational_kinetic_energy_with_filter<F: Fn(&Tagged<Body<B, S>>) -> bool>(
188        &self,
189        should_sum_body: F,
190    ) -> (f64, usize);
191}