hoomd_interaction/univariate/
xplor.rs1use serde::{Deserialize, Serialize};
7
8use super::{UnivariateEnergy, UnivariateForce};
9
10#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
39pub struct Xplor<F> {
40 pub f: F,
42 pub r_cut: f64,
44 pub r_smooth: f64,
46}
47
48impl<F> Xplor<F> {
49 #[inline]
51 fn s(&self, r: f64) -> f64 {
52 if r < self.r_smooth {
53 1.0
54 } else if r > self.r_cut {
55 0.0
56 } else {
57 let r_sq = r.powi(2);
58 let r_cut_sq = self.r_cut.powi(2);
59 let r_smooth_sq = self.r_smooth.powi(2);
60 (r_cut_sq - r_sq).powi(2) * (r_cut_sq + 2.0 * r_sq - 3.0 * r_smooth_sq)
61 / (r_cut_sq - r_smooth_sq).powi(3)
62 }
63 }
64 #[inline]
66 fn ds_dr(&self, r: f64) -> f64 {
67 let r_sq = r.powi(2);
68 let r_cut_sq = self.r_cut.powi(2);
69 let r_smooth_sq = self.r_smooth.powi(2);
70 12.0 * r * ((r_cut_sq - r_sq) * (r_smooth_sq - r_sq)) / (r_cut_sq - r_smooth_sq).powi(3)
71 }
72}
73
74impl<F: UnivariateEnergy> UnivariateEnergy for Xplor<F> {
75 #[inline]
76 fn energy(&self, r: f64) -> f64 {
77 self.s(r) * self.f.energy(r)
78 }
79}
80
81impl<F: UnivariateForce + UnivariateEnergy> UnivariateForce for Xplor<F> {
82 #[inline]
83 fn force(&self, r: f64) -> f64 {
84 if r < self.r_smooth {
85 self.f.force(r)
86 } else if r > self.r_cut {
87 0.0
88 } else {
89 self.s(r) * self.f.force(r) - self.ds_dr(r) * self.f.energy(r)
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use approxim::{assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq};
99 use rstest::*;
100
101 use crate::univariate::LennardJones;
102
103 #[rstest]
104 fn special_points_12_6(
105 #[values(1.0, 2.0, 12.125, 0.25)] epsilon: f64,
106 #[values(1.0, 2.0, 0.5)] sigma: f64,
107 ) {
108 let lj: LennardJones = LennardJones { epsilon, sigma };
109 let r_smooth = 1.0; let r_cut = 2.5 * sigma;
111 let xplor_lj = Xplor {
112 f: lj.clone(),
113 r_cut,
114 r_smooth,
115 };
116
117 assert_eq!(xplor_lj.f.epsilon, epsilon);
118 assert_eq!(xplor_lj.f.sigma, sigma);
119 assert_eq!(xplor_lj.r_smooth, r_smooth);
120 assert_eq!(xplor_lj.r_cut, r_cut);
121
122 assert_abs_diff_eq!(xplor_lj.energy(r_smooth / 2.0), lj.energy(r_smooth / 2.0));
124 assert_abs_diff_eq!(
125 xplor_lj.energy(r_smooth.next_down()),
126 lj.energy(r_smooth.next_down())
127 );
128 assert_abs_diff_eq!(xplor_lj.force(r_smooth / 2.0), lj.force(r_smooth / 2.0));
129 assert_abs_diff_eq!(
130 xplor_lj.force(r_smooth.next_down()),
131 lj.force(r_smooth.next_down())
132 );
133
134 if sigma < r_smooth {
135 assert_abs_diff_eq!(xplor_lj.energy(sigma), 0.0);
136 assert_relative_eq!(xplor_lj.force(sigma), 24.0 * epsilon / sigma);
137 }
138
139 assert_abs_diff_eq!(xplor_lj.energy(r_cut), 0.0);
141 assert_abs_diff_eq!(xplor_lj.energy(r_cut.next_up()), 0.0);
142 assert_abs_diff_eq!(xplor_lj.energy(r_cut * 2.0), 0.0);
143
144 assert_abs_diff_eq!(xplor_lj.force(r_cut), 0.0);
145 assert_abs_diff_eq!(xplor_lj.force(r_cut.next_up()), 0.0);
146 assert_abs_diff_eq!(xplor_lj.force(r_cut * 2.0), 0.0);
147
148 assert_abs_diff_ne!(
150 xplor_lj.energy(f64::midpoint(r_smooth, r_cut)),
151 lj.energy(f64::midpoint(r_smooth, r_cut))
152 );
153 assert_abs_diff_ne!(
154 xplor_lj.force(f64::midpoint(r_smooth, r_cut)),
155 lj.force(f64::midpoint(r_smooth, r_cut))
156 );
157
158 let r_min = 2.0_f64.powf(1.0 / 6.0) * sigma;
160 assert_relative_eq!(xplor_lj.energy(r_min), -epsilon * xplor_lj.s(r_min));
161 if sigma < r_smooth {
162 assert_abs_diff_eq!(xplor_lj.force(r_min), 0.0, epsilon = 1e-12);
163 }
164
165 assert_relative_eq!(
167 xplor_lj.energy(2.0 * sigma),
168 -63.0 / 1024.0 * epsilon * xplor_lj.s(2.0 * sigma)
169 );
170 assert_relative_eq!(
171 xplor_lj.force(2.0 * sigma),
172 -(93.0 * r_cut.powi(6) * epsilon - 279.0 * r_cut.powi(4) * r_smooth.powi(2) * epsilon
173 + 1476.0 * r_cut.powi(2) * r_smooth.powi(2) * sigma.powi(2) * epsilon
174 - 1440.0 * r_cut.powi(2) * sigma.powi(4) * epsilon
175 - 1440.0 * r_smooth.powi(2) * sigma.powi(4) * epsilon
176 - 192.0 * sigma.powi(6) * epsilon)
177 / (512.0 * r_cut.powi(6) * sigma
178 - 1536.0 * r_cut.powi(4) * r_smooth.powi(2) * sigma
179 + 1536.0 * r_cut.powi(2) * r_smooth.powi(4) * sigma
180 - 512.0 * r_smooth.powi(6) * sigma)
181 );
182 }
183}