pub fn compute_srb(elements: &[u8], positions_bohr: &[[f64; 3]]) -> f64 {
let n = elements.len();
let mut e_srb = 0.0;
let s_srb = 0.03;
let gamma_srb = 0.7;
for a in 0..n {
for b in (a + 1)..n {
let r = distance(positions_bohr, a, b);
if r < 1e-10 {
continue;
}
let za = elements[a] as f64;
let zb = elements[b] as f64;
let r_cov = covalent_radius_bohr(elements[a]) + covalent_radius_bohr(elements[b]);
let gaussian = (-gamma_srb * (r - r_cov).powi(2)).exp();
let prefactor = s_srb * (za * zb).sqrt();
e_srb += prefactor * gaussian;
}
}
e_srb
}
fn covalent_radius_bohr(z: u8) -> f64 {
let ang = match z {
1 => 0.31,
6 => 0.76,
7 => 0.71,
8 => 0.66,
9 => 0.57,
14 => 1.11,
15 => 1.07,
16 => 1.05,
17 => 1.02,
_ => 1.0,
};
ang * super::basis::ANG_TO_BOHR
}
fn distance(positions: &[[f64; 3]], a: usize, b: usize) -> f64 {
let dx = positions[a][0] - positions[b][0];
let dy = positions[a][1] - positions[b][1];
let dz = positions[a][2] - positions[b][2];
(dx * dx + dy * dy + dz * dz).sqrt()
}
#[cfg(test)]
mod tests {
use super::super::basis::ANG_TO_BOHR;
use super::*;
#[test]
fn test_srb_at_equilibrium() {
let r = 1.54 * ANG_TO_BOHR;
let positions = [[0.0, 0.0, 0.0], [0.0, 0.0, r]];
let e = compute_srb(&[6, 6], &positions);
assert!(e > 0.0, "SRB should be positive");
assert!(e < 0.5, "SRB should be small: {e}");
}
#[test]
fn test_srb_decays_away_from_covalent() {
let r1 = 1.54 * ANG_TO_BOHR;
let r2 = 5.0 * ANG_TO_BOHR;
let e1 = compute_srb(&[6, 6], &[[0.0, 0.0, 0.0], [0.0, 0.0, r1]]);
let e2 = compute_srb(&[6, 6], &[[0.0, 0.0, 0.0], [0.0, 0.0, r2]]);
assert!(e1 > e2, "SRB should be larger near covalent radius");
}
}