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}