Skip to main content

hoomd_mc/
uniform_in.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 `UniformIn`
5
6use hoomd_vector::{Outer, Wedge};
7use rand::{
8    Rng, RngExt,
9    distr::{Distribution, StandardUniform},
10};
11use serde::{Deserialize, Serialize};
12
13use hoomd_microstate::{
14    Body,
15    property::{DynamicOrientedPoint, DynamicPoint, OrientedPoint, Point, RotationalMotionTypes},
16};
17
18use crate::BodyDistribution;
19
20/// Generate bodies uniformly in the given boundary condition.
21///
22/// Give [`UniformIn`] a template vector of sites and it will randomly generate
23/// bodies uniformly distributed in the given boundary. Each generated body will
24/// have the same sites (cloned from `template_sites`) and random body properties
25/// sampled in the given `boundary`.
26///
27/// # Example
28///
29/// Place points at random locations in the boundary:
30/// ```
31/// use hoomd_geometry::{IsPointInside, shape::Rectangle};
32/// use hoomd_mc::{BodyDistribution, UniformIn};
33/// use hoomd_microstate::{Body, boundary::Closed, property::Point};
34/// use hoomd_vector::Cartesian;
35///
36/// use rand::{SeedableRng, distr::Distribution, rngs::StdRng};
37///
38/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
39/// let rectangle = Closed(Rectangle::with_equal_edges(5.0.try_into()?));
40/// let mut rng = StdRng::seed_from_u64(1);
41///
42/// let uniform_in = UniformIn {
43///     boundary: rectangle,
44///     template_sites: vec![Point::new(Cartesian::from([0.0, 0.0]))],
45/// };
46///
47/// let body: Body<Point<Cartesian<2>>, Point<Cartesian<2>>> =
48///     uniform_in.sample(0, &mut rng);
49/// assert!(
50///     uniform_in
51///         .boundary
52///         .0
53///         .is_point_inside(&body.properties.position)
54/// );
55/// # Ok(())
56/// # }
57/// ```
58///
59/// Place oriented bodies at random locations in the boundary and give them random
60/// orientations:
61/// ```
62/// use rand::{SeedableRng, rngs::StdRng};
63/// use std::f64::consts::PI;
64///
65/// use hoomd_geometry::{IsPointInside, shape::Rectangle};
66/// use hoomd_mc::{BodyDistribution, UniformIn};
67/// use hoomd_microstate::{
68///     Body,
69///     boundary::Closed,
70///     property::{OrientedPoint, Point},
71/// };
72/// use hoomd_vector::{Angle, Cartesian};
73///
74/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
75/// let rectangle = Closed(Rectangle::with_equal_edges(5.0.try_into()?));
76/// let mut rng = StdRng::seed_from_u64(1);
77///
78/// let uniform_in = UniformIn {
79///     boundary: rectangle,
80///     template_sites: vec![
81///         Point::new(Cartesian::from([-1.0, 0.0])),
82///         Point::new(Cartesian::from([1.0, 0.0])),
83///     ],
84/// };
85///
86/// let body: Body<OrientedPoint<Cartesian<2>, Angle>, Point<Cartesian<2>>> =
87///     uniform_in.sample(0, &mut rng);
88/// assert!(
89///     uniform_in
90///         .boundary
91///         .0
92///         .is_point_inside(&body.properties.position)
93/// );
94/// assert!(body.properties.orientation.theta < 2.0 * PI);
95/// # Ok(())
96/// # }
97/// ```
98#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
99pub struct UniformIn<S, C> {
100    /// Generate bodies inside this boundary.
101    pub boundary: C,
102
103    /// Give each generated body these sites.
104    pub template_sites: Vec<S>,
105}
106
107/// Randomly place point bodies in a given boundary.
108///
109/// `sample` chooses the *body's* position randomly in the given boundary. Sites,
110/// therefore, may be placed outside the boundary. Callers should reject insertions
111/// appropriately when `add_body` fails.
112impl<V, S, C> BodyDistribution<Body<Point<V>, S>> for UniformIn<S, C>
113where
114    S: Clone,
115    C: Distribution<V>,
116{
117    #[inline]
118    fn sample<R: Rng + ?Sized>(&self, _index: usize, rng: &mut R) -> Body<Point<V>, S> {
119        let properties = Point {
120            position: self.boundary.sample(rng),
121        };
122        let sites = self.template_sites.clone();
123        Body { properties, sites }
124    }
125}
126
127/// Randomly place dynamic point bodies in a given boundary.
128///
129/// `sample` chooses the *body's* position randomly in the given boundary. Sites,
130/// therefore, may be placed outside the boundary. Callers should reject insertions
131/// appropriately when `add_body` fails.
132///
133/// Mass, momentum, and `net_force` are set to defaults.
134impl<V, S, C> BodyDistribution<Body<DynamicPoint<V>, S>> for UniformIn<S, C>
135where
136    S: Clone,
137    C: Distribution<V>,
138    V: Default + Outer,
139    V::Tensor: Default,
140{
141    #[inline]
142    fn sample<R: Rng + ?Sized>(&self, _index: usize, rng: &mut R) -> Body<DynamicPoint<V>, S> {
143        let properties = DynamicPoint {
144            position: self.boundary.sample(rng),
145            ..Default::default()
146        };
147        let sites = self.template_sites.clone();
148        Body { properties, sites }
149    }
150}
151
152/// Randomly place oriented bodies in a given boundary.
153///
154/// `sample` chooses the *body's* position randomly in the given boundary and also
155/// assigns a *uniform random orientation*. Sites, therefore, may be placed outside
156/// the boundary. Callers should reject insertions appropriately when `add_body`
157/// fails.
158impl<V, O, S, C> BodyDistribution<Body<OrientedPoint<V, O>, S>> for UniformIn<S, C>
159where
160    S: Clone,
161    C: Distribution<V>,
162    StandardUniform: Distribution<O>,
163{
164    #[inline]
165    fn sample<R: Rng + ?Sized>(&self, _index: usize, rng: &mut R) -> Body<OrientedPoint<V, O>, S> {
166        let properties = OrientedPoint {
167            position: self.boundary.sample(rng),
168            orientation: rng.random(),
169        };
170        let sites = self.template_sites.clone();
171        Body { properties, sites }
172    }
173}
174
175/// Randomly place dynamic oriented bodies in a given boundary.
176///
177/// `sample` chooses the *body's* position randomly in the given boundary and also
178/// assigns a *uniform random orientation*. Sites, therefore, may be placed outside
179/// the boundary. Callers should reject insertions appropriately when `add_body`
180/// fails.
181///
182/// Mass, moment of inertia, momentum, angular momentum, `net_force`, and `net_torque`
183/// are set to defaults.
184impl<V, O, S, C> BodyDistribution<Body<DynamicOrientedPoint<V, O>, S>> for UniformIn<S, C>
185where
186    S: Clone,
187    C: Distribution<V>,
188    StandardUniform: Distribution<O>,
189    V: Default + Wedge + Outer,
190    O: RotationalMotionTypes,
191    DynamicOrientedPoint<V, O>: Default,
192{
193    #[inline]
194    fn sample<R: Rng + ?Sized>(
195        &self,
196        _index: usize,
197        rng: &mut R,
198    ) -> Body<DynamicOrientedPoint<V, O>, S> {
199        let properties = DynamicOrientedPoint {
200            position: self.boundary.sample(rng),
201            orientation: rng.random(),
202            ..Default::default()
203        };
204        let sites = self.template_sites.clone();
205        Body { properties, sites }
206    }
207}