Skip to main content

hoomd_interaction/univariate/
boxcar.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 [`Boxcar`]
5
6use serde::{Deserialize, Serialize};
7
8use super::UnivariateEnergy;
9
10/// Constant valued potential in a given range of `r` (_not differentiable_).
11///
12/// ```math
13/// U(r) = \begin{cases}
14/// 0 & r \lt a \\
15/// \varepsilon & a \le r \lt b \\
16/// 0 & r \ge b
17/// \end{cases}
18/// ```
19///
20/// Compute boxcar potential function. Some uses of this in the literature call it
21/// the "square well" potential.
22///
23/// # Examples
24///
25/// Basic usage:
26///
27/// ```
28/// use hoomd_interaction::univariate::{Boxcar, UnivariateEnergy};
29///
30/// let epsilon = 1.5;
31/// let (left, right) = (1.0, 2.5);
32///
33/// let boxcar = Boxcar {
34///     epsilon,
35///     left,
36///     right,
37/// };
38/// assert_eq!(boxcar.energy(0.0), 0.0);
39/// assert_eq!(boxcar.energy(1.0), 1.5);
40/// assert_eq!(boxcar.energy(2.0), 1.5);
41/// assert_eq!(boxcar.energy(2.5), 0.0);
42/// assert_eq!(boxcar.energy(1000.0), 0.0);
43/// ```
44///
45/// The parameters are public fields and may be accessed directly:
46///
47/// ```
48/// use hoomd_interaction::univariate::{Boxcar, UnivariateEnergy};
49///
50/// let mut boxcar = Boxcar {
51///     epsilon: 1.5,
52///     left: 1.0,
53///     right: 2.5,
54/// };
55/// boxcar.epsilon = -2.0;
56/// boxcar.left = 0.0;
57/// boxcar.right = 1.0;
58/// ```
59#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
60pub struct Boxcar {
61    /// Energy scale *(\[energy\])*.
62    pub epsilon: f64,
63    /// Left side of the boxcar *(\[length\])*.
64    pub left: f64,
65    /// Right side of the boxcar *(\[length\])*.
66    pub right: f64,
67}
68
69impl UnivariateEnergy for Boxcar {
70    #[inline]
71    fn energy(&self, r: f64) -> f64 {
72        match r {
73            x if x < self.left => 0.0,
74            x if x < self.right => self.epsilon,
75            _ => 0.0,
76        }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use rstest::*;
84
85    #[rstest]
86    fn general_case(
87        #[values(1.0, -2.0, 12.125, 0.25)] epsilon: f64,
88        #[values(1.0, 2.0, 0.5)] left: f64,
89        #[values(0.5, 0.125)] w: f64,
90    ) {
91        let right = left + w;
92        let boxcar = Boxcar {
93            epsilon,
94            left,
95            right,
96        };
97
98        assert_eq!(boxcar.epsilon, epsilon);
99        assert_eq!(boxcar.left, left);
100        assert_eq!(boxcar.right, right);
101
102        // Left
103        assert_eq!(boxcar.energy(0.0), 0.0);
104        assert_eq!(boxcar.energy(left.next_down()), 0.0);
105
106        // Center
107        assert_eq!(boxcar.energy(left), epsilon);
108        assert_eq!(boxcar.energy(left.next_up()), epsilon);
109        assert_eq!(boxcar.energy(left + w / 2.0), epsilon);
110        assert_eq!(boxcar.energy(right.next_down()), epsilon);
111
112        // Right
113        assert_eq!(boxcar.energy(right), 0.0);
114        assert_eq!(boxcar.energy(right * 10.0), 0.0);
115    }
116}