use nalgebra::DMatrix;
pub fn electronic_energy(
density: &DMatrix<f64>,
h_core: &DMatrix<f64>,
fock: &DMatrix<f64>,
) -> f64 {
let n = density.nrows();
let hf = h_core + fock;
let mut energy = 0.0;
for i in 0..n {
for j in 0..n {
energy += density[(i, j)] * hf[(i, j)];
}
}
0.5 * energy
}
pub fn total_energy(
density: &DMatrix<f64>,
h_core: &DMatrix<f64>,
fock: &DMatrix<f64>,
nuclear_repulsion: f64,
) -> f64 {
electronic_energy(density, h_core, fock) + nuclear_repulsion
}
pub fn one_electron_energy(density: &DMatrix<f64>, h_core: &DMatrix<f64>) -> f64 {
let n = density.nrows();
let mut energy = 0.0;
for i in 0..n {
for j in 0..n {
energy += density[(i, j)] * h_core[(i, j)];
}
}
energy
}
pub fn two_electron_energy(
density: &DMatrix<f64>,
h_core: &DMatrix<f64>,
fock: &DMatrix<f64>,
) -> f64 {
let g = fock - h_core;
let n = density.nrows();
let mut energy = 0.0;
for i in 0..n {
for j in 0..n {
energy += density[(i, j)] * g[(i, j)];
}
}
0.5 * energy
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_energy_decomposition() {
let h = DMatrix::from_row_slice(2, 2, &[-1.0, -0.3, -0.3, -0.8]);
let f = DMatrix::from_row_slice(2, 2, &[-0.9, -0.2, -0.2, -0.7]);
let p = DMatrix::from_row_slice(2, 2, &[1.5, 0.2, 0.2, 0.5]);
let e_1e = one_electron_energy(&p, &h);
let e_2e = two_electron_energy(&p, &h, &f);
let e_elec = electronic_energy(&p, &h, &f);
assert!((e_elec - (e_1e + e_2e)).abs() < 1e-12);
}
#[test]
fn test_total_energy_includes_nuclear() {
let h = DMatrix::from_row_slice(2, 2, &[-1.0, 0.0, 0.0, -1.0]);
let f = h.clone();
let p = DMatrix::identity(2, 2);
let e_nuc = 0.5;
let e_total = total_energy(&p, &h, &f, e_nuc);
let e_elec = electronic_energy(&p, &h, &f);
assert!((e_total - (e_elec + e_nuc)).abs() < 1e-14);
}
}