hoomd_interaction/univariate/weeks_chandler_anderson.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 [`WeeksChandlerAnderson`]
5
6use serde::{Deserialize, Serialize};
7
8use super::{LennardJones, UnivariateEnergy, UnivariateForce};
9
10/// Potential with a steep repulsive core.
11///
12/// ```math
13/// U(r) = \begin{cases}
14/// 4 \varepsilon \left[ \left( \frac{\sigma}{r} \right)^{12} - \left( \frac{\sigma}{r} \right)^{6} \right] + \varepsilon & r \lt 2^{1/6} \sigma \\
15///
16/// 0 & r \ge 2^{1/6} \sigma
17/// \end{cases}
18/// ```
19///
20/// Compute the Weeks-Chandler-Anderson (WCA) potential and force as a function of `r`.
21///
22/// # Examples
23///
24/// Basic usage:
25///
26/// ```
27/// use approxim::{assert_abs_diff_eq, assert_relative_eq};
28/// use hoomd_interaction::univariate::{
29/// UnivariateEnergy, UnivariateForce, WeeksChandlerAnderson,
30/// };
31///
32/// let epsilon = 1.5;
33/// let sigma = 2.5;
34///
35/// let wca = WeeksChandlerAnderson { epsilon, sigma };
36/// assert_relative_eq!(wca.energy(sigma), epsilon);
37/// assert_abs_diff_eq!(wca.energy(2.0 * sigma), 0.0);
38/// assert_relative_eq!(wca.energy(2.0_f64.powf(1.0 / 6.0) * sigma), 0.0);
39/// assert_abs_diff_eq!(
40/// wca.force(2.0_f64.powf(1.0 / 6.0) * sigma),
41/// 0.0,
42/// epsilon = 1e-12
43/// );
44/// ```
45///
46/// The parameters are public fields and may be accessed directly:
47///
48/// ```
49/// use hoomd_interaction::univariate::WeeksChandlerAnderson;
50///
51/// let mut wca = WeeksChandlerAnderson::default();
52/// wca.epsilon = 1.5;
53/// wca.sigma = 3.0;
54/// ```
55#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
56pub struct WeeksChandlerAnderson {
57 /// Energy scale *(\[energy\])*.
58 pub epsilon: f64,
59 /// Interaction width *(\[length\])*.
60 pub sigma: f64,
61}
62
63impl Default for WeeksChandlerAnderson {
64 /// Construct a [`WeeksChandlerAnderson`] with default parameters (epsilon=1.0, sigma=1.0)
65 ///
66 /// # Example
67 ///
68 /// ```
69 /// use hoomd_interaction::univariate::WeeksChandlerAnderson;
70 ///
71 /// let wca = WeeksChandlerAnderson::default();
72 /// ```
73 #[inline]
74 fn default() -> Self {
75 WeeksChandlerAnderson {
76 epsilon: 1.0,
77 sigma: 1.0,
78 }
79 }
80}
81
82impl UnivariateEnergy for WeeksChandlerAnderson {
83 #[inline]
84 fn energy(&self, r: f64) -> f64 {
85 if r < 2.0_f64.powf(1.0 / 6.0) * self.sigma {
86 let lj = LennardJones::<12, 6> {
87 epsilon: self.epsilon,
88 sigma: self.sigma,
89 };
90 lj.energy(r) + self.epsilon
91 } else {
92 0.0
93 }
94 }
95}
96
97impl UnivariateForce for WeeksChandlerAnderson {
98 #[inline]
99 fn force(&self, r: f64) -> f64 {
100 if r < 2.0_f64.powf(1.0 / 6.0) * self.sigma {
101 let lj = LennardJones::<12, 6> {
102 epsilon: self.epsilon,
103 sigma: self.sigma,
104 };
105 lj.force(r)
106 } else {
107 0.0
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use approxim::{assert_abs_diff_eq, assert_relative_eq};
116 use rstest::*;
117
118 #[rstest]
119 fn special_points_12_6(
120 #[values(1.0, 2.0, 12.125, 0.25)] epsilon: f64,
121 #[values(1.0, 2.0, 0.5)] sigma: f64,
122 ) {
123 let wca = WeeksChandlerAnderson { epsilon, sigma };
124
125 assert_eq!(wca.epsilon, epsilon);
126 assert_eq!(wca.sigma, sigma);
127
128 // Zero crossing (shifted by epsilon)
129 assert_relative_eq!(wca.energy(sigma), epsilon);
130 assert_relative_eq!(wca.force(sigma), 24.0 * epsilon / sigma);
131
132 // Bottom of the well
133 assert_abs_diff_eq!(wca.energy(2.0_f64.powf(1.0 / 6.0) * sigma), 0.0);
134 assert_abs_diff_eq!(
135 wca.force(2.0_f64.powf(1.0 / 6.0) * sigma),
136 0.0,
137 epsilon = 1e-12
138 );
139
140 // r = 2 sigma
141 assert_eq!(wca.energy(2.0 * sigma), 0.0);
142 assert_eq!(wca.force(2.0 * sigma), 0.0);
143 }
144}