use nalgebra::DMatrix;
#[derive(Debug, Clone)]
pub struct ScfOutput {
pub energy: f64,
pub orbital_energies: Vec<f64>,
pub iterations: usize,
pub converged: bool,
pub density: Option<DMatrix<f64>>,
pub mulliken_charges: Option<Vec<f64>>,
}
#[derive(Debug, Clone)]
pub struct ScfConvergenceConfig {
pub max_iter: usize,
pub energy_threshold: f64,
pub density_threshold: f64,
pub diis_size: usize,
pub level_shift: f64,
pub use_adiis: bool,
pub adiis_switch_iter: usize,
}
impl Default for ScfConvergenceConfig {
fn default() -> Self {
ScfConvergenceConfig {
max_iter: 100,
energy_threshold: 1e-8,
density_threshold: 1e-6,
diis_size: 8,
level_shift: 0.0,
use_adiis: false,
adiis_switch_iter: 5,
}
}
}
pub trait ScfSolver {
fn solve(&self, config: &ScfConvergenceConfig) -> Result<ScfOutput, String>;
fn method_name(&self) -> &str;
fn n_basis(&self) -> usize;
fn n_electrons(&self) -> usize;
}
pub struct HfScfSolver {
pub h_core: DMatrix<f64>,
pub s_mat: DMatrix<f64>,
pub eris: Vec<f64>,
pub n_elec: usize,
}
impl ScfSolver for HfScfSolver {
fn solve(&self, config: &ScfConvergenceConfig) -> Result<ScfOutput, String> {
let hf_config = super::scf::ScfConfig {
max_iter: config.max_iter,
energy_threshold: config.energy_threshold,
density_threshold: config.density_threshold,
diis_size: config.diis_size,
level_shift: config.level_shift,
};
let result = super::scf::solve_scf(
&self.h_core,
&self.s_mat,
&self.eris,
None,
self.n_elec,
&hf_config,
);
Ok(ScfOutput {
energy: result.energy,
orbital_energies: result.orbital_energies,
iterations: result.iterations,
converged: result.converged,
density: Some(result.density),
mulliken_charges: None,
})
}
fn method_name(&self) -> &str {
"HF"
}
fn n_basis(&self) -> usize {
self.h_core.nrows()
}
fn n_electrons(&self) -> usize {
self.n_elec
}
}
pub struct Pm3ScfSolver {
pub elements: Vec<u8>,
pub positions: Vec<[f64; 3]>,
}
impl ScfSolver for Pm3ScfSolver {
fn solve(&self, _config: &ScfConvergenceConfig) -> Result<ScfOutput, String> {
let result = crate::pm3::solver::solve_pm3(&self.elements, &self.positions)?;
Ok(ScfOutput {
energy: result.total_energy,
orbital_energies: result.orbital_energies,
iterations: result.scf_iterations,
converged: result.converged,
density: None,
mulliken_charges: Some(result.mulliken_charges),
})
}
fn method_name(&self) -> &str {
"PM3"
}
fn n_basis(&self) -> usize {
0
} fn n_electrons(&self) -> usize {
0
}
}
pub struct XtbScfSolver {
pub elements: Vec<u8>,
pub positions: Vec<[f64; 3]>,
}
impl ScfSolver for XtbScfSolver {
fn solve(&self, _config: &ScfConvergenceConfig) -> Result<ScfOutput, String> {
let result = crate::xtb::gfn2::solve_gfn2(&self.elements, &self.positions)?;
Ok(ScfOutput {
energy: result.total_energy,
orbital_energies: result.orbital_energies,
iterations: result.scc_iterations,
converged: result.converged,
density: None,
mulliken_charges: Some(result.mulliken_charges),
})
}
fn method_name(&self) -> &str {
"xTB"
}
fn n_basis(&self) -> usize {
0
}
fn n_electrons(&self) -> usize {
0
}
}