#![cfg(all(
feature = "alpha-dft",
feature = "alpha-obara-saika",
feature = "alpha-reaxff"
))]
use sci_form::dft::ks_fock::{solve_ks_dft, DftConfig};
use sci_form::eht::solver::solve_eht;
#[test]
fn h2_all_methods_converge() {
let elements = [1u8, 1];
let positions = [[0.0, 0.0, 0.0], [0.74, 0.0, 0.0]];
let dft = solve_ks_dft(&elements, &positions, &DftConfig::default()).unwrap();
assert!(dft.converged, "DFT/SVWN must converge for H₂");
let pm3 = sci_form::compute_pm3(&elements, &positions).unwrap();
assert!(pm3.converged, "PM3 must converge for H₂");
let eht = solve_eht(&elements, &positions, None).unwrap();
assert!(eht.gap.is_finite(), "EHT should give finite HOMO-LUMO gap");
}
#[test]
fn h2_homo_lumo_gap_positive_all_methods() {
let elements = [1u8, 1];
let positions = [[0.0, 0.0, 0.0], [0.74, 0.0, 0.0]];
let dft = solve_ks_dft(&elements, &positions, &DftConfig::default()).unwrap();
assert!(dft.gap > 0.0, "DFT gap ({:.3} eV) should be > 0", dft.gap);
let pm3 = sci_form::compute_pm3(&elements, &positions).unwrap();
assert!(pm3.gap > 0.0, "PM3 gap ({:.3} eV) should be > 0", pm3.gap);
let eht = solve_eht(&elements, &positions, None).unwrap();
assert!(eht.gap > 0.0, "EHT gap ({:.3} eV) should be > 0", eht.gap);
}
#[test]
fn ethanol_embed_then_dft() {
let conf = sci_form::embed("CCO", 42);
assert!(conf.error.is_none(), "Embed should succeed for CCO");
let positions: Vec<[f64; 3]> = conf.coords.chunks(3).map(|c| [c[0], c[1], c[2]]).collect();
let dft = solve_ks_dft(&conf.elements, &positions, &DftConfig::default()).unwrap();
assert!(dft.converged, "DFT must converge for ethanol");
assert!(
dft.total_energy < 0.0,
"Total energy should be negative for ethanol"
);
assert_eq!(dft.n_electrons, 26, "Ethanol has 26 electrons");
}
#[test]
fn ethanol_embed_then_pm3() {
let conf = sci_form::embed("CCO", 42);
assert!(conf.error.is_none());
let positions: Vec<[f64; 3]> = conf.coords.chunks(3).map(|c| [c[0], c[1], c[2]]).collect();
let pm3 = sci_form::compute_pm3(&conf.elements, &positions).unwrap();
assert!(pm3.converged, "PM3 must converge for ethanol");
assert!(
pm3.heat_of_formation.is_finite(),
"PM3 HOF should be finite"
);
}
#[test]
fn h2_charge_neutrality_all_methods() {
let elements = [1u8, 1];
let positions = [[0.0, 0.0, 0.0], [0.74, 0.0, 0.0]];
let dft = solve_ks_dft(&elements, &positions, &DftConfig::default()).unwrap();
let dft_sum: f64 = dft.mulliken_charges.iter().sum();
assert!(
dft_sum.abs() < 0.1,
"DFT Mulliken sum ({:.4}) should be near 0",
dft_sum
);
let pm3 = sci_form::compute_pm3(&elements, &positions).unwrap();
let pm3_sum: f64 = pm3.mulliken_charges.iter().sum();
assert!(
pm3_sum.abs() < 0.1,
"PM3 Mulliken sum ({:.4}) should be near 0",
pm3_sum
);
let eht_pop = sci_form::compute_population(&elements, &positions).unwrap();
let eht_sum: f64 = eht_pop.mulliken_charges.iter().sum();
assert!(
eht_sum.abs() < 0.5,
"EHT Mulliken sum ({:.4}) should be near 0",
eht_sum
);
}
#[test]
fn h2_equilibrium_more_stable_than_stretched() {
let elements = [1u8, 1];
let dft_eq = solve_ks_dft(
&elements,
&[[0.0, 0.0, 0.0], [0.74, 0.0, 0.0]],
&DftConfig::default(),
)
.unwrap();
let dft_stretched = solve_ks_dft(
&elements,
&[[0.0, 0.0, 0.0], [3.0, 0.0, 0.0]],
&DftConfig::default(),
)
.unwrap();
assert!(
dft_eq.total_energy < dft_stretched.total_energy,
"DFT: equilibrium ({:.4}) should be lower than stretched ({:.4})",
dft_eq.total_energy,
dft_stretched.total_energy
);
use sci_form::forcefield::reaxff::gradients::compute_reaxff_gradient;
use sci_form::forcefield::reaxff::params::ReaxffParams;
let params = ReaxffParams::default_chon();
let (e_eq, _) =
compute_reaxff_gradient(&[0.0, 0.0, 0.0, 0.74, 0.0, 0.0], &elements, ¶ms).unwrap();
let (e_str, _) =
compute_reaxff_gradient(&[0.0, 0.0, 0.0, 3.0, 0.0, 0.0], &elements, ¶ms).unwrap();
assert!(e_eq.is_finite() && e_str.is_finite());
}