use super::integrals::get_eri;
use nalgebra::DMatrix;
pub fn build_fock(
h_core: &DMatrix<f64>,
density: &DMatrix<f64>,
eris: &[f64],
n_basis: usize,
) -> DMatrix<f64> {
let mut fock = h_core.clone();
for mu in 0..n_basis {
for nu in 0..=mu {
let mut g = 0.0;
for lam in 0..n_basis {
for sig in 0..n_basis {
let p = density[(lam, sig)];
if p.abs() < 1e-15 {
continue;
}
let j = get_eri(eris, mu, nu, lam, sig, n_basis);
let k = get_eri(eris, mu, lam, nu, sig, n_basis);
g += p * (j - 0.5 * k);
}
}
fock[(mu, nu)] += g;
if mu != nu {
fock[(nu, mu)] = fock[(mu, nu)];
}
}
}
fock
}
pub fn electronic_energy(
density: &DMatrix<f64>,
h_core: &DMatrix<f64>,
fock: &DMatrix<f64>,
) -> f64 {
let n = density.nrows();
let mut energy = 0.0;
for mu in 0..n {
for nu in 0..n {
energy += density[(mu, nu)] * (h_core[(mu, nu)] + fock[(mu, nu)]);
}
}
0.5 * energy
}
pub fn nuclear_repulsion(elements: &[u8], positions_bohr: &[[f64; 3]]) -> f64 {
let n = elements.len();
let mut e_nuc = 0.0;
for a in 0..n {
for b in (a + 1)..n {
let dx = positions_bohr[a][0] - positions_bohr[b][0];
let dy = positions_bohr[a][1] - positions_bohr[b][1];
let dz = positions_bohr[a][2] - positions_bohr[b][2];
let r = (dx * dx + dy * dy + dz * dz).sqrt();
if r > 1e-10 {
e_nuc += elements[a] as f64 * elements[b] as f64 / r;
}
}
}
e_nuc
}
#[cfg(test)]
mod tests {
use super::*;
use nalgebra::DMatrix;
#[test]
fn test_nuclear_repulsion_h2() {
let elements = [1u8, 1];
let r = 0.74 * 1.8897259886; let positions = [[0.0, 0.0, 0.0], [0.0, 0.0, r]];
let e_nuc = nuclear_repulsion(&elements, &positions);
assert!(
(e_nuc - 1.0 / r).abs() < 1e-10,
"E_nuc = {e_nuc}, expected {}",
1.0 / r
);
}
#[test]
fn test_electronic_energy_trace() {
let n = 2;
let p = DMatrix::from_row_slice(n, n, &[1.0, 0.0, 0.0, 1.0]);
let h = DMatrix::from_row_slice(n, n, &[-1.0, 0.5, 0.5, -1.0]);
let f = DMatrix::from_row_slice(n, n, &[-0.5, 0.3, 0.3, -0.5]);
let e = electronic_energy(&p, &h, &f);
let expected = 0.5 * (1.0 * (-1.0 + -0.5) + 1.0 * (-1.0 + -0.5));
assert!((e - expected).abs() < 1e-10);
}
}