use super::ShortRangeFunction;
use crate::{Cutoff, Matrix3, NalgebraMatrix3, NalgebraVector3, Vector3};
pub trait MultipoleField: ShortRangeFunction + Cutoff {
fn ion_field(&self, charge: f64, r: impl Into<Vector3>) -> Vector3 {
let r: NalgebraVector3 = r.into().into();
let r2 = r.norm_squared();
if r2 >= self.cutoff_squared() {
return NalgebraVector3::zeros().into();
}
let r1 = r.norm();
let q = r1 / self.cutoff();
let srf0 = self.short_range_f0(q);
let srf1 = self.short_range_f1(q);
let result = charge * r / (r2 * r1)
* match self.kappa() {
Some(kappa) => ((1.0 + kappa * r1) * srf0 - q * srf1) * (-kappa * r1).exp(),
None => srf0 - q * srf1,
};
result.into()
}
fn ion_field_scalar(&self, charge: f64, r: f64) -> f64 {
if r >= self.cutoff() {
return 0.0;
}
let q = r / self.cutoff();
let srf0 = self.short_range_f0(q);
let srf1 = self.short_range_f1(q);
charge / r.powi(2)
* if let Some(kappa) = self.kappa() {
((1.0 + kappa * r) * srf0 - q * srf1) * (-kappa * r).exp()
} else {
srf0 - q * srf1
}
}
fn dipole_field(&self, dipole: impl Into<Vector3>, r: impl Into<Vector3>) -> Vector3 {
let dipole: NalgebraVector3 = dipole.into().into();
let r: NalgebraVector3 = r.into().into();
let r2 = r.norm_squared();
if r2 >= self.cutoff_squared() {
return NalgebraVector3::zeros().into();
}
let r1 = r.norm();
let r3_inv = (r1 * r2).recip();
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 mut field = (3.0 * dipole.dot(&r) * r / r2 - dipole) * r3_inv;
let result = if let Some(kappa) = self.kappa() {
let kr = kappa * r1;
let kr2 = kr * kr;
field *= srf0 * (1.0 + kr + kr2 / 3.0) - q * srf1 * (1.0 + 2.0 / 3.0 * kr)
+ q * q / 3.0 * srf2;
let field_i = dipole * r3_inv * (srf0 * kr2 - 2.0 * kr * q * srf1 + srf2 * q * q) / 3.0;
(field + field_i) * (-kr).exp()
} else {
field *= srf0 - q * srf1 + q * q / 3.0 * srf2;
let field_i = dipole * r3_inv * q * q * srf2 / 3.0;
field + field_i
};
result.into()
}
fn quadrupole_field(&self, quad: impl Into<Matrix3>, r: impl Into<Vector3>) -> Vector3 {
let quad: NalgebraMatrix3 = quad.into().into();
let r: NalgebraVector3 = r.into().into();
let r2 = r.norm_squared();
if r2 >= self.cutoff_squared() {
return NalgebraVector3::zeros().into();
}
let r1 = r.norm();
let r_hat = r / r1;
let q = r1 / self.cutoff();
let q2 = q * q;
let r4 = r2 * r2;
let quadrh = quad * r_hat;
let quad_trh = quad.transpose() * r_hat;
let s0 = self.short_range_f0(q);
let s1 = self.short_range_f1(q);
let s2 = self.short_range_f2(q);
let s3 = self.short_range_f3(q);
let f = (1.0 / r2 * r.transpose() * quad * r)[0]; let mut field_d = 3.0 * ((5.0 * f - quad.trace()) * r_hat - quadrh - quad_trh) / r4;
let result = if let Some(kappa) = self.kappa() {
let kr = kappa * r1;
let kr2 = kr * kr;
field_d *=
s0 * (1.0 + kr + kr2 / 3.0) - q * s1 * (1.0 + 2.0 / 3.0 * kr) + q2 / 3.0 * s2;
let field_i = f * r_hat / r4
* (s0 * (1.0 + kr) * kr2 - q * s1 * (3.0 * kr + 2.0) * kr
+ s2 * (1.0 + 3.0 * kr) * q2
- q2 * q * s3);
0.5 * (field_d + field_i) * (-kr).exp()
} else {
field_d *= s0 - q * s1 + q2 / 3.0 * s2;
let field_i = f * r_hat / r4 * (s2 * q2 - q2 * q * s3);
0.5 * (field_d + field_i)
};
result.into()
}
}