use super::two_electron::dewar_klopman_monopole;
use crate::parameters::SemiEmpiricalElementParams;
pub fn compute_pair_core_repulsion(
r_angstrom: f64,
elem_a: &SemiEmpiricalElementParams,
elem_b: &SemiEmpiricalElementParams,
) -> f64 {
if r_angstrom < 1e-10 {
return 0.0;
}
let gab = dewar_klopman_monopole(r_angstrom, elem_a.gss, elem_b.gss);
let scale_a = if (elem_a.z == 7 || elem_a.z == 8) && elem_b.z == 1 {
r_angstrom
} else {
1.0
};
let scale_b = if (elem_b.z == 7 || elem_b.z == 8) && elem_a.z == 1 {
r_angstrom
} else {
1.0
};
let exp_a = scale_a * (-elem_a.alpha * r_angstrom).exp();
let exp_b = scale_b * (-elem_b.alpha * r_angstrom).exp();
let base_scale = 1.0 + exp_a + exp_b;
let base_repulsion = elem_a.core_charge * elem_b.core_charge * gab * base_scale;
let mut gaussian_sum = 0.0;
let is_am1_b_pair = (elem_a.z == 5 || elem_b.z == 5)
&& matches!(
if elem_a.z == 5 { elem_b.z } else { elem_a.z },
1 | 6 | 9 | 17 | 35 | 53
);
if is_am1_b_pair {
let other = if elem_a.z == 5 { elem_b } else { elem_a };
let b_pairs: &[(f64, f64, f64)] = match other.z {
1 => &[(0.412253, 10.0, 0.832586), (-0.149917, 6.0, 1.186220)],
6 => &[(0.261751, 8.0, 1.063995), (0.050275, 5.0, 1.936492)],
_ => &[(0.359244, 9.0, 0.819351), (0.074729, 9.0, 1.574414)],
};
for &(a, b, c) in b_pairs {
let dr = r_angstrom - c;
let exponent = b * dr * dr;
if exponent <= 25.0 {
gaussian_sum += a * (-exponent).exp();
}
}
for k in 0..other.num_gaussians {
let g = &other.gaussians[k];
let dr = r_angstrom - g.c;
let exponent = g.b * dr * dr;
if exponent <= 25.0 {
gaussian_sum += g.a * (-exponent).exp();
}
}
} else {
for k in 0..elem_a.num_gaussians {
let g = &elem_a.gaussians[k];
let dr = r_angstrom - g.c;
let exponent = g.b * dr * dr;
if exponent < 25.0 {
gaussian_sum += g.a * (-exponent).exp();
}
}
for k in 0..elem_b.num_gaussians {
let g = &elem_b.gaussians[k];
let dr = r_angstrom - g.c;
let exponent = g.b * dr * dr;
if exponent < 25.0 {
gaussian_sum += g.a * (-exponent).exp();
}
}
}
let gaussian_repulsion = (elem_a.core_charge * elem_b.core_charge / r_angstrom) * gaussian_sum;
base_repulsion + gaussian_repulsion
}
pub fn compute_pair_core_repulsion_pm6(
r_angstrom: f64,
elem_a: &SemiEmpiricalElementParams,
elem_b: &SemiEmpiricalElementParams,
) -> f64 {
if r_angstrom < 1e-10 {
return 0.0;
}
let gab = dewar_klopman_monopole(r_angstrom, elem_a.gss, elem_b.gss);
let (alpb, xfac) = crate::parameters::pm6::Pm6Model::get_pair_params(elem_a.z, elem_b.z);
let z_min = elem_a.z.min(elem_b.z);
let z_max = elem_a.z.max(elem_b.z);
let scale = if z_min == 1 && (z_max == 6 || z_max == 7 || z_max == 8) {
1.0 + 2.0 * xfac * (-alpb * r_angstrom * r_angstrom).exp()
} else if xfac > 1e-5 {
1.0 + 2.0 * xfac * (-alpb * (r_angstrom + 0.0003 * r_angstrom.powi(6))).exp()
} else {
1.0 + 10.0 * (-2.18 * r_angstrom).exp()
};
let base_repulsion = elem_a.core_charge * elem_b.core_charge * gab * scale;
let mut gaussian_sum = 0.0;
for k in 0..elem_a.num_gaussians {
let g = &elem_a.gaussians[k];
let dr = r_angstrom - g.c;
let exponent = g.b * dr * dr;
if exponent < 25.0 {
gaussian_sum += g.a * (-exponent).exp();
}
}
for k in 0..elem_b.num_gaussians {
let g = &elem_b.gaussians[k];
let dr = r_angstrom - g.c;
let exponent = g.b * dr * dr;
if exponent < 25.0 {
gaussian_sum += g.a * (-exponent).exp();
}
}
let gaussian_repulsion = (elem_a.core_charge * elem_b.core_charge / r_angstrom) * gaussian_sum;
base_repulsion + gaussian_repulsion
}
pub fn compute_total_core_repulsion(
batch: &crate::types::MolecularBatch,
model: &dyn crate::parameters::ParameterModel,
) -> f64 {
let mut total_energy = 0.0;
for i in 0..batch.natoms {
let za = batch.atomic_numbers[i];
let elem_a = match model.get_element(za) {
Some(p) => p,
None => continue,
};
for j in (i + 1)..batch.natoms {
let zb = batch.atomic_numbers[j];
let elem_b = match model.get_element(zb) {
Some(p) => p,
None => continue,
};
let r = batch.distance(i, j);
total_energy += model.pair_core_repulsion(r, &elem_a, &elem_b);
}
}
total_energy
}