use chemx_core::Molecule;
pub fn numerical_hessian<F>(molecule: &Molecule, step_size: f64, gradient_fn: F) -> Vec<f64>
where
F: Fn(&Molecule) -> Vec<f64>,
{
let natom = molecule.len();
let ndof = 3 * natom;
let mut h = vec![0.0f64; ndof * ndof];
for atom in 0..natom {
for axis in 0..3 {
let dof = atom * 3 + axis;
let mol_fwd = displaced(molecule, atom, axis, step_size);
let mol_bwd = displaced(molecule, atom, axis, -step_size);
let gf = gradient_fn(&mol_fwd);
let gb = gradient_fn(&mol_bwd);
for j in 0..ndof {
h[dof * ndof + j] = (gf[j] - gb[j]) / (2.0 * step_size);
}
}
}
for i in 0..ndof {
for j in i + 1..ndof {
let avg = (h[i * ndof + j] + h[j * ndof + i]) / 2.0;
h[i * ndof + j] = avg;
h[j * ndof + i] = avg;
}
}
h
}
pub fn displaced(molecule: &Molecule, idx: usize, axis: usize, delta: f64) -> Molecule {
let mut atoms = molecule.atoms.clone();
atoms[idx].position[axis] += delta;
Molecule::new(atoms, molecule.charge, molecule.multiplicity)
}