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}