Skip to main content

hoomd_interaction/pairwise/
anisotropic.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 `Anisotropic`
5
6use serde::{Deserialize, Serialize};
7
8use super::AnisotropicEnergy;
9use crate::{MaximumInteractionRange, SitePairEnergy};
10use hoomd_microstate::property::{Orientation, Position};
11use hoomd_vector::{Rotate, Rotation, Vector};
12
13/// Compute anisotropic properties from a pair of sites.
14///
15/// [`Anisotropic`] provides a single implementation that computes pairwise
16/// interactions that are a function of the sites' positions and orientations.
17/// It fills the gap between traits like [`SitePairEnergy`] which operates on
18/// site properties and [`AnisotropicEnergy`] which is a function only of the
19/// relative position and orientation.
20///
21/// Use [`Anisotropic`] with [`PairwiseCutoff`] in MD and MC simulations.
22///
23/// [`PairwiseCutoff`]: crate::PairwiseCutoff
24///
25/// # Example
26///
27/// ```
28/// use hoomd_interaction::{
29///     pairwise::{AngularMask, Anisotropic, angular_mask::Patch},
30///     univariate::Boxcar,
31/// };
32/// use hoomd_vector::Angle;
33/// use std::f64::consts::PI;
34///
35/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
36/// let boxcar = Boxcar {
37///     epsilon: -1.0,
38///     left: 1.0,
39///     right: 1.5,
40/// };
41/// let masks = [Patch {
42///     director: [1.0, 0.0].try_into()?,
43///     cos_delta: (PI / 8.0).cos(),
44/// }];
45///
46/// let angular_mask = Anisotropic {
47///     interaction: AngularMask::new(boxcar, masks),
48///     r_cut: 1.5,
49/// };
50/// # Ok(())
51/// # }
52/// ```
53#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
54pub struct Anisotropic<E> {
55    /// The site-site interaction.
56    pub interaction: E,
57    /// Maximum distance between two interacting sites.
58    pub r_cut: f64,
59}
60
61impl<P, R, S, E> SitePairEnergy<S> for Anisotropic<E>
62where
63    S: Position<Position = P> + Orientation<Rotation = R>,
64    P: Vector,
65    R: Rotation + Rotate<P>,
66    E: AnisotropicEnergy<P, R>,
67{
68    /// Compute the pair energy between two sites.
69    ///
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// use hoomd_interaction::{
75    ///     SitePairEnergy,
76    ///     pairwise::{AngularMask, Anisotropic, angular_mask::Patch},
77    ///     univariate::Boxcar,
78    /// };
79    /// use hoomd_microstate::property::OrientedPoint;
80    /// use hoomd_vector::{Angle, Cartesian};
81    /// use std::f64::consts::PI;
82    ///
83    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
84    /// let boxcar = Boxcar {
85    ///     epsilon: -1.0,
86    ///     left: 1.0,
87    ///     right: 1.5,
88    /// };
89    /// let masks = [Patch {
90    ///     director: [1.0, 0.0].try_into()?,
91    ///     cos_delta: (PI / 8.0).cos(),
92    /// }];
93    ///
94    /// let angular_mask = Anisotropic {
95    ///     interaction: AngularMask::new(boxcar, masks),
96    ///     r_cut: 1.5,
97    /// };
98    ///
99    /// let a = OrientedPoint {
100    ///     position: Cartesian::from([0.0, 0.0]),
101    ///     orientation: Angle::from(0.0),
102    /// };
103    /// let b = OrientedPoint {
104    ///     position: Cartesian::from([1.0, 0.0]),
105    ///     orientation: Angle::from(0.0),
106    /// };
107    /// let energy = angular_mask.site_pair_energy(&a, &b);
108    /// assert_eq!(energy, 0.0);
109    ///
110    /// let c = OrientedPoint {
111    ///     position: Cartesian::from([1.0, 0.0]),
112    ///     orientation: Angle::from(PI),
113    /// };
114    /// let energy = angular_mask.site_pair_energy(&a, &c);
115    /// assert_eq!(energy, -1.0);
116    /// # Ok(())
117    /// # }
118    /// ```
119    #[inline]
120    fn site_pair_energy(&self, site_properties_i: &S, site_properties_j: &S) -> f64 {
121        let r = site_properties_i
122            .position()
123            .distance(site_properties_j.position());
124        if r >= self.r_cut {
125            return 0.0;
126        }
127
128        let (r_ab, o_ab) = hoomd_vector::pair_system_to_local(
129            site_properties_i.position(),
130            site_properties_i.orientation(),
131            site_properties_j.position(),
132            site_properties_j.orientation(),
133        );
134        self.interaction.energy(&r_ab, &o_ab)
135    }
136}
137
138impl<E> MaximumInteractionRange for Anisotropic<E> {
139    #[inline]
140    fn maximum_interaction_range(&self) -> f64 {
141        self.r_cut
142    }
143}