1use crate::base::Potential2;
33use crate::math::Vector;
34
35#[derive(Clone, Copy, Debug, PartialEq)]
53pub struct Mie<T, const N: u32, const M: u32> {
54 cn: T,
56 cm: T,
58}
59
60impl<T: Vector, const N: u32, const M: u32> Mie<T, N, M> {
61 #[inline]
84 pub fn new(epsilon: f64, sigma: f64) -> Self {
85 assert!(N > M, "Mie potential requires N > M, got N={}, M={}", N, M);
86 assert!(M > 0, "Mie potential requires M > 0, got M={}", M);
87
88 let n = N as f64;
89 let m = M as f64;
90
91 let n_minus_m = n - m;
93 let prefactor = (n / n_minus_m) * (n / m).powf(m / n_minus_m);
94 let c_eps = prefactor * epsilon;
95
96 let sigma_n = int_pow(sigma, N);
98 let sigma_m = int_pow(sigma, M);
99
100 Self {
101 cn: T::splat(c_eps * sigma_n),
102 cm: T::splat(c_eps * sigma_m),
103 }
104 }
105
106 #[inline]
115 pub fn from_coefficients(cn: f64, cm: f64) -> Self {
116 Self {
117 cn: T::splat(cn),
118 cm: T::splat(cm),
119 }
120 }
121
122 #[inline]
124 pub fn cn(&self) -> T {
125 self.cn
126 }
127
128 #[inline]
130 pub fn cm(&self) -> T {
131 self.cm
132 }
133
134 #[inline]
136 pub const fn n(&self) -> u32 {
137 N
138 }
139
140 #[inline]
142 pub const fn m(&self) -> u32 {
143 M
144 }
145}
146
147impl<T: Vector, const N: u32, const M: u32> Potential2<T> for Mie<T, N, M> {
148 #[inline(always)]
154 fn energy(&self, r_sq: T) -> T {
155 let r_inv = r_sq.rsqrt();
156
157 let r_neg_n = int_pow_vec::<T, N>(r_inv);
159 let r_neg_m = int_pow_vec::<T, M>(r_inv);
160
161 self.cn * r_neg_n - self.cm * r_neg_m
162 }
163
164 #[inline(always)]
171 fn force_factor(&self, r_sq: T) -> T {
172 let r_inv = r_sq.rsqrt();
173 let r_sq_inv = r_sq.recip();
174
175 let r_neg_n = int_pow_vec::<T, N>(r_inv);
176 let r_neg_m = int_pow_vec::<T, M>(r_inv);
177
178 let n = T::splat(N as f64);
179 let m = T::splat(M as f64);
180
181 (n * self.cn * r_neg_n - m * self.cm * r_neg_m) * r_sq_inv
182 }
183
184 #[inline(always)]
188 fn energy_force(&self, r_sq: T) -> (T, T) {
189 let r_inv = r_sq.rsqrt();
190 let r_sq_inv = r_sq.recip();
191
192 let r_neg_n = int_pow_vec::<T, N>(r_inv);
193 let r_neg_m = int_pow_vec::<T, M>(r_inv);
194
195 let cn_rn = self.cn * r_neg_n;
196 let cm_rm = self.cm * r_neg_m;
197
198 let energy = cn_rn - cm_rm;
199
200 let n = T::splat(N as f64);
201 let m = T::splat(M as f64);
202 let force = (n * cn_rn - m * cm_rm) * r_sq_inv;
203
204 (energy, force)
205 }
206}
207
208#[inline]
212fn int_pow(x: f64, n: u32) -> f64 {
213 match n {
214 0 => 1.0,
215 1 => x,
216 2 => x * x,
217 3 => x * x * x,
218 4 => {
219 let x2 = x * x;
220 x2 * x2
221 }
222 5 => {
223 let x2 = x * x;
224 x2 * x2 * x
225 }
226 6 => {
227 let x2 = x * x;
228 x2 * x2 * x2
229 }
230 7 => {
231 let x2 = x * x;
232 let x4 = x2 * x2;
233 x4 * x2 * x
234 }
235 8 => {
236 let x2 = x * x;
237 let x4 = x2 * x2;
238 x4 * x4
239 }
240 9 => {
241 let x2 = x * x;
242 let x4 = x2 * x2;
243 let x8 = x4 * x4;
244 x8 * x
245 }
246 10 => {
247 let x2 = x * x;
248 let x4 = x2 * x2;
249 let x8 = x4 * x4;
250 x8 * x2
251 }
252 11 => {
253 let x2 = x * x;
254 let x4 = x2 * x2;
255 let x8 = x4 * x4;
256 x8 * x2 * x
257 }
258 12 => {
259 let x2 = x * x;
260 let x4 = x2 * x2;
261 x4 * x4 * x4
262 }
263 13 => {
264 let x2 = x * x;
265 let x4 = x2 * x2;
266 x4 * x4 * x4 * x
267 }
268 14 => {
269 let x2 = x * x;
270 let x4 = x2 * x2;
271 let x8 = x4 * x4;
272 x8 * x4 * x2
273 }
274 _ => {
275 let mut result = 1.0;
277 let mut base = x;
278 let mut exp = n;
279 while exp > 0 {
280 if exp & 1 == 1 {
281 result *= base;
282 }
283 base *= base;
284 exp >>= 1;
285 }
286 result
287 }
288 }
289}
290
291#[inline(always)]
296fn int_pow_vec<T: Vector, const N: u32>(x: T) -> T {
297 match N {
298 0 => T::splat(1.0),
299 1 => x,
300 2 => x * x,
301 3 => x * x * x,
302 4 => {
303 let x2 = x * x;
304 x2 * x2
305 }
306 5 => {
307 let x2 = x * x;
308 x2 * x2 * x
309 }
310 6 => {
311 let x2 = x * x;
312 x2 * x2 * x2
313 }
314 7 => {
315 let x2 = x * x;
316 let x4 = x2 * x2;
317 x4 * x2 * x
318 }
319 8 => {
320 let x2 = x * x;
321 let x4 = x2 * x2;
322 x4 * x4
323 }
324 9 => {
325 let x2 = x * x;
326 let x4 = x2 * x2;
327 let x8 = x4 * x4;
328 x8 * x
329 }
330 10 => {
331 let x2 = x * x;
332 let x4 = x2 * x2;
333 let x8 = x4 * x4;
334 x8 * x2
335 }
336 11 => {
337 let x2 = x * x;
338 let x4 = x2 * x2;
339 let x8 = x4 * x4;
340 x8 * x2 * x
341 }
342 12 => {
343 let x2 = x * x;
344 let x4 = x2 * x2;
345 x4 * x4 * x4
346 }
347 13 => {
348 let x2 = x * x;
349 let x4 = x2 * x2;
350 x4 * x4 * x4 * x
351 }
352 14 => {
353 let x2 = x * x;
354 let x4 = x2 * x2;
355 let x8 = x4 * x4;
356 x8 * x4 * x2
357 }
358 _ => {
359 let mut result = T::splat(1.0);
361 let mut base = x;
362 let mut exp = N;
363 while exp > 0 {
364 if exp & 1 == 1 {
365 result = result * base;
366 }
367 base = base * base;
368 exp >>= 1;
369 }
370 result
371 }
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use approx::assert_relative_eq;
379
380 #[test]
381 fn test_mie_reduces_to_lj() {
382 let mie: Mie<f64, 12, 6> = Mie::new(1.0, 1.0);
383 let lj = crate::pair::Lj::<f64>::new(1.0, 1.0);
384
385 let r_sq = 1.5 * 1.5;
386 let e_mie = mie.energy(r_sq);
387 let e_lj = lj.energy(r_sq);
388
389 assert_relative_eq!(e_mie, e_lj, epsilon = 1e-10);
390 }
391
392 #[test]
393 fn test_mie_at_minimum() {
394 let epsilon = 1.5;
395 let sigma = 2.0;
396 let mie: Mie<f64, 10, 5> = Mie::new(epsilon, sigma);
397
398 let r_min = 2.0_f64.powf(1.0 / 5.0) * sigma;
399 let r_sq = r_min * r_min;
400 let energy = mie.energy(r_sq);
401
402 assert_relative_eq!(energy, -epsilon, epsilon = 1e-10);
403 }
404
405 #[test]
406 fn test_mie_force_at_minimum() {
407 let mie: Mie<f64, 12, 6> = Mie::new(1.0, 1.0);
408
409 let r_min = 2.0_f64.powf(1.0 / 6.0);
410 let r_sq = r_min * r_min;
411 let force = mie.force_factor(r_sq);
412
413 assert_relative_eq!(force, 0.0, epsilon = 1e-10);
414 }
415
416 #[test]
417 fn test_mie_energy_force_consistency() {
418 let mie: Mie<f64, 9, 6> = Mie::new(0.5, 2.0);
419 let r_sq = 6.25;
420
421 let (e1, f1) = mie.energy_force(r_sq);
422 let e2 = mie.energy(r_sq);
423 let f2 = mie.force_factor(r_sq);
424
425 assert_relative_eq!(e1, e2, epsilon = 1e-12);
426 assert_relative_eq!(f1, f2, epsilon = 1e-12);
427 }
428
429 #[test]
430 fn test_mie_numerical_derivative() {
431 let mie: Mie<f64, 9, 6> = Mie::new(1.0, 1.0);
432 let r = 1.2;
433 let r_sq = r * r;
434
435 let h = 1e-6;
436 let v_plus = mie.energy((r + h) * (r + h));
437 let v_minus = mie.energy((r - h) * (r - h));
438 let dv_dr_numerical = (v_plus - v_minus) / (2.0 * h);
439
440 let s_numerical = -dv_dr_numerical / r;
441 let s_analytical = mie.force_factor(r_sq);
442
443 assert_relative_eq!(s_analytical, s_numerical, epsilon = 1e-6);
444 }
445}