Skip to main content

hoomd_md/
lib.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#![doc(
5    html_favicon_url = "https://raw.githubusercontent.com/glotzerlab/hoomd-rs/7352214172a490cc716492e9724ff42720a0018a/doc/theme/favicon.svg"
6)]
7#![doc(
8    html_logo_url = "https://raw.githubusercontent.com/glotzerlab/hoomd-rs/7352214172a490cc716492e9724ff42720a0018a/doc/theme/favicon.svg"
9)]
10
11//! Apply the molecular dynamics simulation method to systems of bodies.
12//!
13//! `hoomd-md` provides building blocks that you can use to create a molecular dynamics
14//! simulation model. Start with a [`Microstate`] to represent the properties of all the
15//! bodies and sites. Form an interaction model using types from [`hoomd_interaction`]
16//! that implement [`NetBodyForceAndVirial`] or [`NetBodyForceVirialAndTorque`] and set the macrostate
17//! using one of the types from [`hoomd_simulation`].
18//!
19//! [`Microstate`]: hoomd_microstate::Microstate
20//! [`NetBodyForceAndVirial`]: hoomd_interaction::NetBodyForceAndVirial
21//! [`NetBodyForceVirialAndTorque`]: hoomd_interaction::NetBodyForceVirialAndTorque
22//! [`DeltaEnergyRemove`]: hoomd_interaction::DeltaEnergyRemove
23//! [`TotalEnergy`]: hoomd_interaction::TotalEnergy
24//!
25//! # Integration methods
26//!
27//! The [`TranslationalMotion`] and [`RotationalMotion`] traits describe types that
28//! can integrate the translational and/or rotational degrees of freedom in the
29//! microstate, respectively. Most users will call [`integrate_translation`]
30//! or [`integrate_translation_and_rotation`] to advance all bodies in the microstate
31//! forward one time step. See the trait documentation for details on how to pin some bodies
32//! in place and/or apply different integration methods to different bodies.
33//!
34//! [`integrate_translation`]: TranslationalMotion::integrate_translation
35//! [`integrate_translation_and_rotation`]: RotationalMotion::integrate_translation_and_rotation
36//!
37//! The [`ConstantVolume`] method integrates the equations of motion for the model
38//! while keeping the volume of the simulation boundary fixed. [`ConstantVolume`]
39//! can sample the microcanonical (NVE) or canonical (NVT) ensembles based on the
40//! choice of thermostat (see below).
41//!
42//! [`ConstantVolume`]: crate::method::ConstantVolume
43//!
44//! ## Body and site properties
45//!
46//! Currently, *hoomd-rs* implements [`TranslationalMotion`] for any [`InnerProduct`] vector
47//! space for bodies with [`Mass`], [`Momentum`], and [`NetForce`] properties in the same
48//! vector space as [`Position`]. For systems with only translational degrees of freedom,
49//! most users will choose [`DynamicPoint<Cartesian<N>>`] body properties and
50//! [`Point<Cartesian<N>>`] site properties.
51//!
52//! [`InnerProduct`]: hoomd_vector::InnerProduct
53//! [`Mass`]: hoomd_microstate::property::Mass
54//! [`Momentum`]: hoomd_microstate::property::Momentum
55//! [`NetForce`]: hoomd_microstate::property::NetForce
56//! [`Position`]: hoomd_microstate::property::Position
57//! [`DynamicPoint<Cartesian<N>>`]: hoomd_microstate::property::DynamicPoint
58//! [`Point<Cartesian<N>>`]: hoomd_microstate::property::Point
59//!
60//! Due to the mathematical nature of rotational degrees of freedom, *hoomd-rs* implements
61//! [`RotationalMotion`] specifically for [`DynamicOrientedPoint<Cartesian<2>, Angle>`] for
62//! 2D simulations and [`DynamicOrientedPoint<Cartesian<3>, Versor>`] for 3D. You must use
63//! one of these two types for body properties to integrate rotational degrees of freedom.
64//! There are fewer restrictions on the site properties type. Most users will choose
65//! [`Point<Cartesian<N>>`] or [`OrientedPoint<Cartesian<N>>`] site properties for
66//! models with rotational degrees of freedom, while some will need custom types.
67//! The choice for site properties is driven by the interaction model, not the integration
68//! method.
69//!
70//! [`DynamicOrientedPoint<Cartesian<2>, Angle>`]: hoomd_microstate::property::DynamicOrientedPoint
71//! [`DynamicOrientedPoint<Cartesian<3>, Versor>`]: hoomd_microstate::property::DynamicOrientedPoint
72//! [`OrientedPoint<Cartesian<N>>`]: hoomd_microstate::property::OrientedPoint
73//!
74//! ## Thermostats
75//!
76//! Some of the integration methods sample constant temperature ensembles using velocity
77//! rescaling thermostats. There are many algorithms to choose from. Find them in the
78//! [`thermostat`] module. Use [`NoThermostat`] to sample constant energy (or enthalpy)
79//! ensembles.
80//!
81//! [`NoThermostat`]: thermostat::NoThermostat
82//!
83//! # The `Rigid` interaction model
84//!
85//! All integration methods in *hoomd-rs* model bodies as rigid bodies. The net force and
86//! torque on each body results from the forces and torques applied to its sites.
87//! The [`Rigid`] type implements [`NetBodyForceAndVirial`] and [`NetBodyForceVirialAndTorque`] when
88//! it wraps a type that computes forces ([`NetSiteForceAndVirial`]) and torques
89//! ([`NetSiteForceVirialAndTorque`]) on sites. For example:
90//! `Rigid<PairwiseCutoff<Isotropic<LennardJones>>>` is a valid interaction model
91//! for use with molecular dynamics integration methods.
92//!
93//! [`Rigid`]: hoomd_interaction::Rigid
94//! [`NetSiteForceAndVirial`]: hoomd_interaction::NetSiteForceAndVirial
95//! [`NetSiteForceVirialAndTorque`]: hoomd_interaction::NetSiteForceVirialAndTorque
96//!
97//! Most differentiable interaction models implement both [`NetSiteForceAndVirial`] and all the
98//! Hamiltonian traits needed for Monte Carlo simulations in *hoomd-mc*. With these
99//! interaction models, you can freely swap between MD and MC simulation steps.
100//! Non-differentiable energies, such as [`Boxcar`] implement energy traits,
101//! but not forces and can therefore only be used with MC. Others, like active forces,
102//! might implement the force traits but not energy and can only be used with MD.
103//! Rust will validate the trait bounds and issue a compile error for invalid
104//! combinations.
105//!
106//! [`Boxcar`]: hoomd_interaction::univariate::Boxcar
107//!
108//! # Microstate modifiers
109//!
110//! Use [`ThermalizeMomentum`] and [`ThermalizeAngularMomentum`] to sample random
111//! momenta from a thermal distribution. Use [`ZeroCenterMomentum`] and
112//! [`ZeroCenterAngularMomentum`] to remove motion of the center of mass.
113//!
114//! All of these modifier traits are implemented for [`Microstate`] itself:
115//! e.g. `microstate.zero_center_momentum()`.
116//!
117//! # Compute properties of the microstate
118//!
119//! Use [`TranslationalKineticEnergy`] to compute the translational kinetic
120//! energy and count the corresponding translational degrees of freedom
121//! in the microstate. [`RotationalKineticEnergy`] does the same for
122//! rotational degrees of freedom.
123//!
124//! As with the modifies, the compute traits are implemented for [`Microstate`].
125
126use rand::Rng;
127
128use hoomd_microstate::{Body, Microstate, Tagged};
129
130pub mod method;
131pub mod thermostat;
132
133mod compute;
134pub use compute::{RotationalKineticEnergy, TranslationalKineticEnergy};
135
136mod modify;
137pub use modify::{
138    ThermalizeAngularMomentum, ThermalizeMomentum, ZeroCenterAngularMomentum, ZeroCenterMomentum,
139};
140
141mod update_net_force;
142pub use update_net_force::{UpdateNetForceAndVirial, UpdateNetForceVirialAndTorque};
143
144/// Scale momenta to hold the system at constant temperature.
145///
146/// Use any of the thermostats in the [`thermostat`] module along with the
147/// integration method of your choice.
148///
149/// The [`ConstantVolume`] integration method rescales every momentum in the
150/// system following the given [`Thermostat`] to sample trajectories from the
151/// canonical ensemble.
152///
153/// [`ConstantVolume`]: crate::method::ConstantVolume
154pub trait Thermostat<M> {
155    /// Integrate the thermostat one half step forward in time.
156    ///
157    /// Returns the momentum scaling factor to use during the first half step.
158    fn integrate_half_step_one<R: Rng + ?Sized>(
159        &mut self,
160        rng: &mut R,
161        macrostate: &M,
162        delta_t: f64,
163        kinetic_energy: f64,
164        degrees_of_freedom: usize,
165    ) -> f64;
166
167    /// Integrate the thermostat one half step forward in time.
168    ///
169    /// Returns the momentum scaling factor to use during the second half step.
170    fn integrate_half_step_two<R: Rng + ?Sized>(
171        &mut self,
172        rng: &mut R,
173        macrostate: &M,
174        delta_t: f64,
175        kinetic_energy: f64,
176        degrees_of_freedom: usize,
177    ) -> f64;
178}
179
180/// Integrate translational degrees of freedom.
181///
182/// [`TranslationalMotion`] integrates the [`Position`] and [`Momentum`] degrees of
183/// freedom for selected bodies.
184///
185/// To integrate the whole system forward one step, call [`integrate_translation`]:
186/// ```
187/// # use hoomd_microstate::{Body, Microstate, property::{DynamicPoint, Point}};
188/// # use hoomd_vector::Cartesian;
189/// # use hoomd_md::{ThermalizeMomentum, TranslationalMotion, method::ConstantVolume};
190/// # use hoomd_interaction::{Rigid, Zero};
191/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
192/// # let mut microstate = Microstate::builder()
193/// #     .bodies([
194/// #         Body::single_site(DynamicPoint {
195/// #           position: Cartesian::from([1.0, 2.0]),
196/// #           ..Default::default()
197/// #           },
198/// #           Point::default(),
199/// #           ),
200/// #         Body::single_site(DynamicPoint {
201/// #           position: Cartesian::from([-2.0, 3.0]),
202/// #           ..Default::default()
203/// #           },
204/// #           Point::default(),
205/// #           ),
206/// #     ])
207/// #     .try_build()?;
208/// # microstate.thermalize_momentum(1.5);
209/// # let mut integration_method = ConstantVolume::builder(0.001).build();
210/// # let interaction_model = Rigid(Zero);
211/// # let macrostate = ();
212/// integration_method.integrate_translation(&mut microstate, &macrostate, &interaction_model);
213/// microstate.increment_step();
214/// # Ok(())
215/// # }
216/// ```
217///
218/// To integrate only some bodies, call [`integrate_translation_with_filter`]:
219/// ```
220/// # use hoomd_microstate::{Body, Microstate, property::{DynamicPoint, Point}};
221/// # use hoomd_vector::Cartesian;
222/// # use hoomd_md::{ThermalizeMomentum, TranslationalMotion, method::ConstantVolume};
223/// # use hoomd_interaction::{Rigid, Zero};
224/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
225/// # let mut microstate = Microstate::builder()
226/// #     .bodies([
227/// #         Body::single_site(DynamicPoint {
228/// #           position: Cartesian::from([1.0, 2.0]),
229/// #           ..Default::default()
230/// #           },
231/// #           Point::default(),
232/// #           ),
233/// #         Body::single_site(DynamicPoint {
234/// #           position: Cartesian::from([-2.0, 3.0]),
235/// #           ..Default::default()
236/// #           },
237/// #           Point::default(),
238/// #           ),
239/// #     ])
240/// #     .try_build()?;
241/// # microstate.thermalize_momentum(1.5);
242/// # let mut integration_method = ConstantVolume::builder(0.001).build();
243/// # let interaction_model = Rigid(Zero);
244/// # let macrostate = ();
245/// integration_method.integrate_translation_with_filter(&mut microstate, &macrostate, &interaction_model, |b| b.tag < 2);
246/// microstate.increment_step();
247/// # Ok(())
248/// # }
249/// ```
250///
251/// To integrate some bodies with one integration method and other bodies with another,
252/// call [`integrate_translation_half_step_one_with_filter`] for all methods, then call
253/// `update_net_force`, and finish with [`integrate_translation_half_step_one_with_filter`].
254/// The filters must select distinct subsets of bodies. The filters must also select
255/// the same bodies in half step one and half step two.
256/// ```
257/// # use hoomd_microstate::{Body, Microstate, property::{DynamicPoint, Point}};
258/// # use hoomd_vector::Cartesian;
259/// # use hoomd_md::{UpdateNetForceAndVirial, ThermalizeMomentum, TranslationalMotion, method::ConstantVolume};
260/// # use hoomd_interaction::{Rigid, Zero};
261/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
262/// # let mut microstate = Microstate::builder()
263/// #     .bodies([
264/// #         Body::single_site(DynamicPoint {
265/// #           position: Cartesian::from([1.0, 2.0]),
266/// #           ..Default::default()
267/// #           },
268/// #           Point::default(),
269/// #           ),
270/// #         Body::single_site(DynamicPoint {
271/// #           position: Cartesian::from([-2.0, 3.0]),
272/// #           ..Default::default()
273/// #           },
274/// #           Point::default(),
275/// #           ),
276/// #     ])
277/// #     .try_build()?;
278/// # microstate.thermalize_momentum(1.5);
279/// # let mut integration_method_1 = ConstantVolume::builder(0.001).build();
280/// # let mut integration_method_2 = ConstantVolume::builder(0.001).build();
281/// # let interaction_model = Rigid(Zero);
282/// # let macrostate = ();
283/// integration_method_1.integrate_translation_half_step_one_with_filter(&mut microstate, &macrostate, |b| b.tag < 2);
284/// integration_method_2.integrate_translation_half_step_one_with_filter(&mut microstate, &macrostate, |b| b.tag >= 2);
285/// microstate.update_net_force_and_virial(&interaction_model);
286/// integration_method_1.integrate_translation_half_step_two_with_filter(&mut microstate, &macrostate, |b| b.tag < 2);
287/// integration_method_2.integrate_translation_half_step_two_with_filter(&mut microstate, &macrostate, |b| b.tag >= 2);
288/// microstate.increment_step();
289/// # Ok(())
290/// # }
291/// ```
292///
293/// The generic type names are:
294/// * `B`: The [`Body::properties`](hoomd_microstate::Body) type.
295/// * `S`: The [`Site::properties`](hoomd_microstate::Site) type.
296/// * `X`: The spatial data structure type.
297/// * `C`: The [`boundary`](hoomd_microstate::boundary) condition type.
298/// * `M`: The [`macrostate`](hoomd_simulation::macrostate) type.
299///
300/// [`integrate_translation`]: Self::integrate_translation
301/// [`integrate_translation_with_filter`]: Self::integrate_translation_with_filter
302/// [`integrate_translation_half_step_one_with_filter`]: Self::integrate_translation_half_step_one_with_filter
303/// [`integrate_translation_half_step_two_with_filter`]: Self::integrate_translation_half_step_two_with_filter
304/// [`Position`]: hoomd_microstate::property::Position
305/// [`Momentum`]: hoomd_microstate::property::Momentum
306pub trait TranslationalMotion<B, S, X, C, M> {
307    /// Integrate all body positions forward a full step and the momenta forward a half step.
308    #[inline]
309    fn integrate_translation_half_step_one(
310        &mut self,
311        microstate: &mut Microstate<B, S, X, C>,
312        macrostate: &M,
313    ) {
314        self.integrate_translation_half_step_one_with_filter(microstate, macrostate, |_| true);
315    }
316
317    /// Integrate selected body positions forward a full step and the momenta forward a half step.
318    fn integrate_translation_half_step_one_with_filter<F: Fn(&Tagged<Body<B, S>>) -> bool>(
319        &mut self,
320        microstate: &mut Microstate<B, S, X, C>,
321        macrostate: &M,
322        should_integrate_body: F,
323    );
324
325    /// Integrate all body momenta forward a half step.
326    #[inline]
327    fn integrate_translation_half_step_two(
328        &mut self,
329        microstate: &mut Microstate<B, S, X, C>,
330        macrostate: &M,
331    ) {
332        self.integrate_translation_half_step_two_with_filter(microstate, macrostate, |_| true);
333    }
334
335    /// Integrate selected body momenta forward a half step.
336    fn integrate_translation_half_step_two_with_filter<F: Fn(&Tagged<Body<B, S>>) -> bool>(
337        &mut self,
338        microstate: &mut Microstate<B, S, X, C>,
339        macrostate: &M,
340        should_integrate_body: F,
341    );
342
343    /// Integrate selected body translational degrees of freedom forward one step.
344    #[inline]
345    fn integrate_translation_with_filter<E, F>(
346        &mut self,
347        microstate: &mut Microstate<B, S, X, C>,
348        macrostate: &M,
349        interaction_model: &E,
350        should_integrate_body: F,
351    ) where
352        F: Fn(&Tagged<Body<B, S>>) -> bool,
353        Microstate<B, S, X, C>: UpdateNetForceAndVirial<E>,
354    {
355        self.integrate_translation_half_step_one_with_filter(
356            microstate,
357            macrostate,
358            &should_integrate_body,
359        );
360        microstate.update_net_force_and_virial(interaction_model);
361        self.integrate_translation_half_step_two_with_filter(
362            microstate,
363            macrostate,
364            &should_integrate_body,
365        );
366    }
367
368    /// Integrate all body translational degrees of freedom forward one step.
369    #[inline]
370    fn integrate_translation<E>(
371        &mut self,
372        microstate: &mut Microstate<B, S, X, C>,
373        macrostate: &M,
374        interaction_model: &E,
375    ) where
376        Microstate<B, S, X, C>: UpdateNetForceAndVirial<E>,
377    {
378        self.integrate_translation_half_step_one_with_filter(microstate, macrostate, |_| true);
379        microstate.update_net_force_and_virial(interaction_model);
380        self.integrate_translation_half_step_two_with_filter(microstate, macrostate, |_| true);
381    }
382}
383
384/// Integrate rotational degrees of freedom.
385///
386/// [`RotationalMotion`] integrates the [`Orientation`] and [`AngularMomentum`] degrees of
387/// freedom for selected bodies.
388///
389/// The generic type names are:
390/// * `B`: The [`Body::properties`](hoomd_microstate::Body) type.
391/// * `S`: The [`Site::properties`](hoomd_microstate::Site) type.
392/// * `X`: The spatial data structure type.
393/// * `C`: The [`boundary`](hoomd_microstate::boundary) condition type.
394/// * `M`: The [`macrostate`](hoomd_simulation::macrostate) type.
395///
396/// To integrate the whole system forward one step, call [`integrate_translation_and_rotation`]:
397/// ```
398/// # use hoomd_microstate::{Body, Microstate, property::{DynamicOrientedPoint, Point}};
399/// # use hoomd_vector::Cartesian;
400/// # use hoomd_md::{ThermalizeMomentum, RotationalMotion, TranslationalMotion, method::ConstantVolume};
401/// # use hoomd_interaction::{Rigid, Zero};
402/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
403/// # let mut microstate = Microstate::builder()
404/// #     .bodies([
405/// #         Body::single_site(DynamicOrientedPoint {
406/// #           position: Cartesian::from([1.0, 2.0]),
407/// #           ..Default::default()
408/// #           },
409/// #           Point::default(),
410/// #           ),
411/// #         Body::single_site(DynamicOrientedPoint {
412/// #           position: Cartesian::from([-2.0, 3.0]),
413/// #           ..Default::default()
414/// #           },
415/// #           Point::default(),
416/// #           ),
417/// #     ])
418/// #     .try_build()?;
419/// # microstate.thermalize_momentum(1.5);
420/// # let mut integration_method = ConstantVolume::builder(0.001).build();
421/// # let interaction_model = Rigid(Zero);
422/// # let macrostate = ();
423/// integration_method.integrate_translation_and_rotation(&mut microstate, &macrostate, &interaction_model);
424/// microstate.increment_step();
425/// # Ok(())
426/// # }
427/// ```
428///
429/// To integrate only some bodies, call [`integrate_translation_and_rotation_with_filter`]:
430/// ```
431/// # use hoomd_microstate::{Body, Microstate, property::{DynamicOrientedPoint, Point}};
432/// # use hoomd_vector::{Angle, Cartesian};
433/// # use hoomd_md::{ThermalizeMomentum, RotationalMotion, TranslationalMotion, method::ConstantVolume};
434/// # use hoomd_interaction::{Rigid, Zero};
435/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
436/// # let mut microstate = Microstate::builder()
437/// #     .bodies([
438/// #         Body::single_site(DynamicOrientedPoint {
439/// #           position: Cartesian::from([1.0, 2.0]),
440/// #           orientation: Angle::default(),
441/// #           ..Default::default()
442/// #           },
443/// #           Point::default(),
444/// #           ),
445/// #         Body::single_site(DynamicOrientedPoint {
446/// #           position: Cartesian::from([-2.0, 3.0]),
447/// #           ..Default::default()
448/// #           },
449/// #           Point::default(),
450/// #           ),
451/// #     ])
452/// #     .try_build()?;
453/// # microstate.thermalize_momentum(1.5);
454/// # let mut integration_method = ConstantVolume::builder(0.001).build();
455/// # let interaction_model = Rigid(Zero);
456/// # let macrostate = ();
457/// integration_method.integrate_translation_with_filter(&mut microstate, &macrostate, &interaction_model, |b| b.tag < 2);
458/// microstate.increment_step();
459/// # Ok(())
460/// # }
461/// ```
462///
463/// To integrate some bodies with one integration method and other bodies with another,
464/// call `integrate_translation_half_step_one_with_filter`
465/// [`integrate_rotation_half_step_one_with_filter`] for all methods, then call
466/// `update_net_force_and_torque`, and finish with `integrate_translation_half_step_one_with_filter`
467/// [`integrate_rotation_half_step_one_with_filter`].
468/// The filters must select distinct subsets of bodies. The filters must also select
469/// the same bodies in half step one and half step two.
470/// ```
471/// # use hoomd_microstate::{Body, Microstate, property::{DynamicOrientedPoint, Point}};
472/// # use hoomd_vector::Cartesian;
473/// # use hoomd_md::{UpdateNetForceVirialAndTorque, ThermalizeMomentum, RotationalMotion, TranslationalMotion, method::ConstantVolume};
474/// # use hoomd_interaction::{Rigid, Zero};
475/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
476/// # let mut microstate = Microstate::builder()
477/// #     .bodies([
478/// #         Body::single_site(DynamicOrientedPoint {
479/// #           position: Cartesian::from([1.0, 2.0]),
480/// #           ..Default::default()
481/// #           },
482/// #           Point::default(),
483/// #           ),
484/// #         Body::single_site(DynamicOrientedPoint {
485/// #           position: Cartesian::from([-2.0, 3.0]),
486/// #           ..Default::default()
487/// #           },
488/// #           Point::default(),
489/// #           ),
490/// #     ])
491/// #     .try_build()?;
492/// # microstate.thermalize_momentum(1.5);
493/// # let mut integration_method_1 = ConstantVolume::builder(0.001).build();
494/// # let mut integration_method_2 = ConstantVolume::builder(0.001).build();
495/// # let interaction_model = Rigid(Zero);
496/// # let macrostate = ();
497/// integration_method_1.integrate_translation_half_step_one_with_filter(&mut microstate, &macrostate, |b| b.tag < 2);
498/// integration_method_1.integrate_rotation_half_step_one_with_filter(&mut microstate, &macrostate, |b| b.tag < 2);
499/// integration_method_2.integrate_translation_half_step_one_with_filter(&mut microstate, &macrostate, |b| b.tag >= 2);
500/// integration_method_2.integrate_rotation_half_step_one_with_filter(&mut microstate, &macrostate, |b| b.tag >= 2);
501/// microstate.update_net_force_virial_and_torque(&interaction_model);
502/// integration_method_1.integrate_translation_half_step_two_with_filter(&mut microstate, &macrostate, |b| b.tag < 2);
503/// integration_method_1.integrate_rotation_half_step_two_with_filter(&mut microstate, &macrostate, |b| b.tag < 2);
504/// integration_method_2.integrate_translation_half_step_two_with_filter(&mut microstate, &macrostate, |b| b.tag >= 2);
505/// integration_method_2.integrate_rotation_half_step_two_with_filter(&mut microstate, &macrostate, |b| b.tag >= 2);
506/// microstate.increment_step();
507/// # Ok(())
508/// # }
509/// ```
510///
511/// [`integrate_translation_and_rotation`]: Self::integrate_translation_and_rotation
512/// [`integrate_translation_and_rotation_with_filter`]: Self::integrate_translation_and_rotation_with_filter
513/// [`integrate_rotation_half_step_one_with_filter`]: Self::integrate_rotation_half_step_one_with_filter
514/// [`integrate_rotation_half_step_two_with_filter`]: Self::integrate_rotation_half_step_two_with_filter
515/// [`Orientation`]: hoomd_microstate::property::Orientation
516/// [`AngularMomentum`]: hoomd_microstate::property::AngularMomentum
517pub trait RotationalMotion<B, S, X, C, M> {
518    /// Integrate all body orientations forward a full step and their angular momenta forward a half step.
519    #[inline]
520    fn integrate_rotation_half_step_one(
521        &mut self,
522        microstate: &mut Microstate<B, S, X, C>,
523        macrostate: &M,
524    ) {
525        self.integrate_rotation_half_step_one_with_filter(microstate, macrostate, |_| true);
526    }
527
528    /// Integrate selected body orientations forward a full step and their angular momenta forward a half step.
529    fn integrate_rotation_half_step_one_with_filter<F: Fn(&Tagged<Body<B, S>>) -> bool>(
530        &mut self,
531        microstate: &mut Microstate<B, S, X, C>,
532        macrostate: &M,
533        should_integrate_body: F,
534    );
535
536    /// Integrate all body angular momenta forward a half step.
537    #[inline]
538    fn integrate_rotation_half_step_two(
539        &mut self,
540        microstate: &mut Microstate<B, S, X, C>,
541        macrostate: &M,
542    ) {
543        self.integrate_rotation_half_step_two_with_filter(microstate, macrostate, |_| true);
544    }
545
546    /// Integrate selected body angular momenta forward a half step.
547    fn integrate_rotation_half_step_two_with_filter<F: Fn(&Tagged<Body<B, S>>) -> bool>(
548        &mut self,
549        microstate: &mut Microstate<B, S, X, C>,
550        macrostate: &M,
551        should_integrate_body: F,
552    );
553
554    /// Integrate selected body translational and rotational degrees of freedom forward one step.
555    #[inline]
556    fn integrate_translation_and_rotation_with_filter<E, F>(
557        &mut self,
558        microstate: &mut Microstate<B, S, X, C>,
559        macrostate: &M,
560        interaction_model: &E,
561        should_integrate_body: F,
562    ) where
563        F: Fn(&Tagged<Body<B, S>>) -> bool,
564        Microstate<B, S, X, C>: UpdateNetForceVirialAndTorque<E>,
565        Self: TranslationalMotion<B, S, X, C, M>,
566    {
567        self.integrate_translation_half_step_one_with_filter(
568            microstate,
569            macrostate,
570            &should_integrate_body,
571        );
572        self.integrate_rotation_half_step_one_with_filter(
573            microstate,
574            macrostate,
575            &should_integrate_body,
576        );
577        microstate.update_net_force_virial_and_torque(interaction_model);
578        self.integrate_translation_half_step_two_with_filter(
579            microstate,
580            macrostate,
581            &should_integrate_body,
582        );
583        self.integrate_rotation_half_step_two_with_filter(
584            microstate,
585            macrostate,
586            &should_integrate_body,
587        );
588    }
589
590    /// Integrate all body translational and rotational degrees of freedom forward one step.
591    #[inline]
592    fn integrate_translation_and_rotation<E>(
593        &mut self,
594        microstate: &mut Microstate<B, S, X, C>,
595        macrostate: &M,
596        interaction_model: &E,
597    ) where
598        Microstate<B, S, X, C>: UpdateNetForceVirialAndTorque<E>,
599        Self: TranslationalMotion<B, S, X, C, M>,
600    {
601        self.integrate_translation_half_step_one_with_filter(microstate, macrostate, |_| true);
602        self.integrate_rotation_half_step_one_with_filter(microstate, macrostate, |_| true);
603        microstate.update_net_force_virial_and_torque(interaction_model);
604        self.integrate_translation_half_step_two_with_filter(microstate, macrostate, |_| true);
605        self.integrate_rotation_half_step_two_with_filter(microstate, macrostate, |_| true);
606    }
607}