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 rand::{
7 Rng, RngExt,
8 distr::{Distribution, StandardUniform},
9};
10use serde::{Deserialize, Serialize};
11
12use hoomd_microstate::{
13 Body,
14 property::{OrientedPoint, Point},
15};
16
17use crate::BodyDistribution;
18
19/// Generate bodies uniformly in the given boundary condition.
20///
21/// Give [`UniformIn`] a template vector of sites and it will randomly generate
22/// bodies uniformly distributed in the given boundary. Each generated body will
23/// have the same sites (cloned from `template_sites`) and random body properties
24/// sampled in the given `boundary`.
25///
26/// # Example
27///
28/// Place points at random locations in the boundary:
29/// ```
30/// use hoomd_geometry::{IsPointInside, shape::Rectangle};
31/// use hoomd_mc::{BodyDistribution, UniformIn};
32/// use hoomd_microstate::{Body, boundary::Closed, property::Point};
33/// use hoomd_vector::Cartesian;
34///
35/// use rand::{SeedableRng, distr::Distribution, rngs::StdRng};
36///
37/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
38/// let rectangle = Closed(Rectangle::with_equal_edges(5.0.try_into()?));
39/// let mut rng = StdRng::seed_from_u64(1);
40///
41/// let uniform_in = UniformIn {
42/// boundary: rectangle,
43/// template_sites: vec![Point::new(Cartesian::from([0.0, 0.0]))],
44/// };
45///
46/// let body: Body<Point<Cartesian<2>>, Point<Cartesian<2>>> =
47/// uniform_in.sample(0, &mut rng);
48/// assert!(
49/// uniform_in
50/// .boundary
51/// .0
52/// .is_point_inside(&body.properties.position)
53/// );
54/// # Ok(())
55/// # }
56/// ```
57///
58/// Place oriented bodies at random locations in the boundary and give them random
59/// orientations:
60/// ```
61/// use rand::{SeedableRng, rngs::StdRng};
62/// use std::f64::consts::PI;
63///
64/// use hoomd_geometry::{IsPointInside, shape::Rectangle};
65/// use hoomd_mc::{BodyDistribution, UniformIn};
66/// use hoomd_microstate::{
67/// Body,
68/// boundary::Closed,
69/// property::{OrientedPoint, Point},
70/// };
71/// use hoomd_vector::{Angle, Cartesian};
72///
73/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
74/// let rectangle = Closed(Rectangle::with_equal_edges(5.0.try_into()?));
75/// let mut rng = StdRng::seed_from_u64(1);
76///
77/// let uniform_in = UniformIn {
78/// boundary: rectangle,
79/// template_sites: vec![
80/// Point::new(Cartesian::from([-1.0, 0.0])),
81/// Point::new(Cartesian::from([1.0, 0.0])),
82/// ],
83/// };
84///
85/// let body: Body<OrientedPoint<Cartesian<2>, Angle>, Point<Cartesian<2>>> =
86/// uniform_in.sample(0, &mut rng);
87/// assert!(
88/// uniform_in
89/// .boundary
90/// .0
91/// .is_point_inside(&body.properties.position)
92/// );
93/// assert!(body.properties.orientation.theta < 2.0 * PI);
94/// # Ok(())
95/// # }
96/// ```
97#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
98pub struct UniformIn<S, C> {
99 /// Generate bodies inside this boundary.
100 pub boundary: C,
101
102 /// Give each generated body these sites.
103 pub template_sites: Vec<S>,
104}
105
106/// Randomly place point bodies in a given boundary.
107///
108/// `sample` chooses the *body's* position randomly in the given boundary. Sites,
109/// therefore, may be placed outside the boundary. Callers should reject insertions
110/// appropriately when `add_body` fails.
111impl<V, S, C> BodyDistribution<Body<Point<V>, S>> for UniformIn<S, C>
112where
113 S: Clone,
114 C: Distribution<V>,
115{
116 #[inline]
117 fn sample<R: Rng + ?Sized>(&self, _index: usize, rng: &mut R) -> Body<Point<V>, S> {
118 let properties = Point {
119 position: self.boundary.sample(rng),
120 };
121 let sites = self.template_sites.clone();
122 Body { properties, sites }
123 }
124}
125
126/// Randomly place oriented bodies in a given boundary.
127///
128/// `sample` chooses the *body's* position randomly in the given boundary and also
129/// assigns a *uniform random orientation*. Sites, therefore, may be placed outside
130/// the boundary. Callers should reject insertions appropriately when `add_body`
131/// fails.
132impl<V, O, S, C> BodyDistribution<Body<OrientedPoint<V, O>, S>> for UniformIn<S, C>
133where
134 S: Clone,
135 C: Distribution<V>,
136 StandardUniform: Distribution<O>,
137{
138 #[inline]
139 fn sample<R: Rng + ?Sized>(&self, _index: usize, rng: &mut R) -> Body<OrientedPoint<V, O>, S> {
140 let properties = OrientedPoint {
141 position: self.boundary.sample(rng),
142 orientation: rng.random(),
143 };
144 let sites = self.template_sites.clone();
145 Body { properties, sites }
146 }
147}