hoomd_mc/translate/
sphere.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 Translation moves on curved surfaces
5
6use rand::{Rng, distr::Distribution};
7
8use crate::{LocalTrial, Translate};
9use hoomd_manifold::{Spherical, SphericalDisk};
10use hoomd_microstate::property::{Point, Position};
11use hoomd_vector::InnerProduct;
12
13impl LocalTrial<Point<Spherical<3>>> for Translate<Point<Spherical<3>>> {
14    /// Propose local trial moves for a body on the surface of a sphere
15    ///
16    /// # Example
17    /// ```
18    /// use approxim::assert_relative_eq;
19    /// use hoomd_manifold::{Spherical, SphericalDisk};
20    /// use hoomd_mc::{LocalTrial, Translate};
21    /// use hoomd_microstate::property::{Point, Position};
22    /// use hoomd_vector::{Cartesian, InnerProduct, Metric, Vector};
23    /// use rand::{Rng, SeedableRng, rngs::StdRng};
24    ///
25    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
26    /// let mut rng = StdRng::seed_from_u64(14);
27    /// let initial_point = Point::new(Spherical::from_cartesian_coordinates(
28    ///     [0.5_f64.sqrt(), 0.5_f64.sqrt(), 0.0].into(),
29    /// ));
30    /// let d = 0.1;
31    /// let translate = Translate::with_maximum_distance(d.try_into()?);
32    ///
33    /// let new_body_properties = translate.propose(&mut rng, initial_point);
34    ///
35    /// // Translation move keeps point on the surface of the sphere
36    /// let new_body_radius = new_body_properties.position.point().norm();
37    /// assert_eq!(new_body_radius, 1.0);
38    ///
39    /// // Translation move does not translate the point more than a distance d away
40    /// assert!(
41    ///     d > new_body_properties
42    ///         .position()
43    ///         .distance(&initial_point.position())
44    /// );
45    /// # Ok(())
46    /// # }
47    /// ```
48    #[inline]
49    fn propose<R: Rng>(
50        &self,
51        rng: &mut R,
52        body_properties: Point<Spherical<3>>,
53    ) -> Point<Spherical<3>> {
54        let mut trial = body_properties;
55        let disk = SphericalDisk {
56            disk_radius: *self.maximum_distance(),
57            point: *trial.position_mut(),
58        };
59        *trial.position_mut() = disk.sample(rng);
60        let rescale = 1.0 / trial.position().point().norm();
61        *trial.position_mut() =
62            Spherical::from_cartesian_coordinates(*trial.position().point() * rescale);
63        trial
64    }
65}
66
67// TODO: test