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}