use super::ShortRangeFunction;
use crate::{Cutoff, Matrix3, NalgebraMatrix3, NalgebraVector3, Vector3};
pub trait MultipolePotential: ShortRangeFunction + Cutoff {
#[inline]
fn ion_potential(&self, charge: f64, distance: f64) -> f64 {
if distance >= self.cutoff() {
return 0.0;
}
let q = distance / self.cutoff();
charge / distance
* self.short_range_f0(q)
* self.kappa().map_or(1.0, |kappa| (-kappa * distance).exp())
}
fn dipole_potential(&self, dipole: impl Into<Vector3>, r: impl Into<Vector3>) -> f64 {
let dipole: NalgebraVector3 = dipole.into().into();
let r: NalgebraVector3 = r.into().into();
let r2 = r.norm_squared();
if r2 >= self.cutoff_squared() {
return 0.0;
}
let r1 = r2.sqrt(); let q = r1 / self.cutoff();
let srf0 = self.short_range_f0(q);
let srf1 = self.short_range_f1(q);
dipole.dot(&r) / (r2 * r1)
* if let Some(kappa) = self.kappa() {
(srf0 * (1.0 + kappa * r1) - q * srf1) * (-kappa * r1).exp()
} else {
srf0 - q * srf1
}
}
fn quadrupole_potential(&self, quad: impl Into<Matrix3>, r: impl Into<Vector3>) -> f64 {
let quad: NalgebraMatrix3 = quad.into().into();
let r: NalgebraVector3 = r.into().into();
let r2 = r.norm_squared();
if r2 >= self.cutoff_squared() {
return 0.0;
}
let r1 = r.norm();
let q = r1 / self.cutoff();
let srf0 = self.short_range_f0(q);
let srf1 = self.short_range_f1(q);
let srf2 = self.short_range_f2(q);
let trace = quad.trace();
let f = 3.0 / r2 * (r.transpose() * quad * r)[0] - trace;
0.5 / (r1 * r2)
* if let Some(kappa) = self.kappa() {
let kr = kappa * r1;
let kr2 = kr * kr;
let a = srf0 * (1.0 + kr + kr2 / 3.0) - q * srf1 * (1.0 + 2.0 / 3.0 * kr)
+ q * q / 3.0 * srf2;
let b = (srf0 * kr2 - 2.0 * kr * q * srf1 + srf2 * q * q) / 3.0;
(f * a + trace * b) * (-kr).exp()
} else {
let a = srf0 - q * srf1 + q * q / 3.0 * srf2;
let b = (srf2 * q * q) / 3.0;
f * a + trace * b
}
}
}