potentials/meta/
softcore.rs1use crate::base::Potential2;
30use crate::math::Vector;
31
32#[derive(Clone, Copy, Debug)]
39pub struct Softcore<P, T> {
40 inner: P,
41 alpha_sigma_sq: T, lambda: T,
43 lambda_n: T, lambda_m: T, }
46
47impl<P, T: Vector> Softcore<P, T> {
48 #[inline]
68 pub fn new(inner: P, alpha: f64, sigma: f64, lambda: f64, n: u32, m: u32) -> Self {
69 let lambda_n = lambda.powi(n as i32);
70 let lambda_m = lambda.powi(m as i32);
71
72 Self {
73 inner,
74 alpha_sigma_sq: T::splat(alpha * sigma * sigma),
75 lambda: T::splat(lambda),
76 lambda_n: T::splat(lambda_n),
77 lambda_m: T::splat(lambda_m),
78 }
79 }
80
81 #[inline]
85 pub fn set_lambda(&mut self, lambda: f64, n: u32, m: u32) {
86 self.lambda = T::splat(lambda);
87 self.lambda_n = T::splat(lambda.powi(n as i32));
88 self.lambda_m = T::splat(lambda.powi(m as i32));
89 }
90
91 #[inline(always)]
93 fn effective_r_sq(&self, r_sq: T) -> T {
94 self.alpha_sigma_sq * self.lambda_n + r_sq
95 }
96}
97
98impl<P: Potential2<T>, T: Vector> Potential2<T> for Softcore<P, T> {
99 #[inline(always)]
100 fn energy(&self, r_sq: T) -> T {
101 let r_eff_sq = self.effective_r_sq(r_sq);
102 self.lambda_m * self.inner.energy(r_eff_sq)
103 }
104
105 #[inline(always)]
106 fn force_factor(&self, r_sq: T) -> T {
107 let r_eff_sq = self.effective_r_sq(r_sq);
108 self.lambda_m * self.inner.force_factor(r_eff_sq)
109 }
110
111 #[inline(always)]
112 fn energy_force(&self, r_sq: T) -> (T, T) {
113 let r_eff_sq = self.effective_r_sq(r_sq);
114 let (e, f) = self.inner.energy_force(r_eff_sq);
115 (self.lambda_m * e, self.lambda_m * f)
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::pair::Lj;
123 use approx::assert_relative_eq;
124
125 #[test]
126 fn test_softcore_lambda_one() {
127 let lj: Lj<f64> = Lj::new(1.0, 3.4);
128 let lj_soft = Softcore::new(lj, 0.5, 3.4, 1.0, 1, 1);
129
130 let r_sq = 16.0;
131
132 let (e, f) = lj_soft.energy_force(r_sq);
133 let r_eff_sq = 0.5 * 3.4 * 3.4 + r_sq;
134 let (e_expected, f_expected) = lj.energy_force(r_eff_sq);
135
136 assert_relative_eq!(e, e_expected, epsilon = 1e-10);
137 assert_relative_eq!(f, f_expected, epsilon = 1e-10);
138 }
139
140 #[test]
141 fn test_softcore_lambda_zero() {
142 let lj: Lj<f64> = Lj::new(1.0, 3.4);
143 let lj_soft = Softcore::new(lj, 0.5, 3.4, 0.0, 1, 1);
144
145 let r_sq = 16.0;
146 let e = lj_soft.energy(r_sq);
147
148 assert_relative_eq!(e, 0.0, epsilon = 1e-10);
149 }
150
151 #[test]
152 fn test_softcore_finite_at_zero() {
153 let lj: Lj<f64> = Lj::new(1.0, 3.4);
154 let lj_soft = Softcore::new(lj, 0.5, 3.4, 0.5, 1, 1);
155
156 let e = lj_soft.energy(0.0);
157
158 assert!(e.is_finite());
159 assert!(e.abs() < 1e10);
160 }
161
162 #[test]
163 fn test_softcore_numerical_derivative() {
164 let lj: Lj<f64> = Lj::new(1.0, 3.4);
165 let lj_soft = Softcore::new(lj, 0.5, 3.4, 0.5, 1, 1);
166
167 let r = 4.0;
168
169 let h = 1e-7;
170 let e_plus = lj_soft.energy((r + h) * (r + h));
171 let e_minus = lj_soft.energy((r - h) * (r - h));
172 let dv_dr_numerical = (e_plus - e_minus) / (2.0 * h);
173
174 let f = lj_soft.force_factor(r * r);
175 let dv_dr_analytical = -f * r;
176
177 assert_relative_eq!(dv_dr_analytical, dv_dr_numerical, epsilon = 1e-6);
178 }
179}