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}