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