1use nalgebra::DMatrix;
7
8#[derive(Debug, Clone)]
10pub struct ScfOutput {
11 pub energy: f64,
13 pub orbital_energies: Vec<f64>,
15 pub iterations: usize,
17 pub converged: bool,
19 pub density: Option<DMatrix<f64>>,
21 pub mulliken_charges: Option<Vec<f64>>,
23}
24
25#[derive(Debug, Clone)]
27pub struct ScfConvergenceConfig {
28 pub max_iter: usize,
30 pub energy_threshold: f64,
32 pub density_threshold: f64,
34 pub diis_size: usize,
36 pub level_shift: f64,
38 pub use_adiis: bool,
40 pub adiis_switch_iter: usize,
42}
43
44impl Default for ScfConvergenceConfig {
45 fn default() -> Self {
46 ScfConvergenceConfig {
47 max_iter: 100,
48 energy_threshold: 1e-8,
49 density_threshold: 1e-6,
50 diis_size: 8,
51 level_shift: 0.0,
52 use_adiis: false,
53 adiis_switch_iter: 5,
54 }
55 }
56}
57
58pub trait ScfSolver {
60 fn solve(&self, config: &ScfConvergenceConfig) -> Result<ScfOutput, String>;
62
63 fn method_name(&self) -> &str;
65
66 fn n_basis(&self) -> usize;
68
69 fn n_electrons(&self) -> usize;
71}
72
73pub struct HfScfSolver {
75 pub h_core: DMatrix<f64>,
76 pub s_mat: DMatrix<f64>,
77 pub eris: Vec<f64>,
78 pub n_elec: usize,
79}
80
81impl ScfSolver for HfScfSolver {
82 fn solve(&self, config: &ScfConvergenceConfig) -> Result<ScfOutput, String> {
83 let hf_config = super::scf::ScfConfig {
84 max_iter: config.max_iter,
85 energy_threshold: config.energy_threshold,
86 density_threshold: config.density_threshold,
87 diis_size: config.diis_size,
88 level_shift: config.level_shift,
89 };
90
91 let result = super::scf::solve_scf(
92 &self.h_core,
93 &self.s_mat,
94 &self.eris,
95 None,
96 self.n_elec,
97 &hf_config,
98 );
99
100 Ok(ScfOutput {
101 energy: result.energy,
102 orbital_energies: result.orbital_energies,
103 iterations: result.iterations,
104 converged: result.converged,
105 density: Some(result.density),
106 mulliken_charges: None,
107 })
108 }
109
110 fn method_name(&self) -> &str {
111 "HF"
112 }
113 fn n_basis(&self) -> usize {
114 self.h_core.nrows()
115 }
116 fn n_electrons(&self) -> usize {
117 self.n_elec
118 }
119}
120
121pub struct Pm3ScfSolver {
123 pub elements: Vec<u8>,
124 pub positions: Vec<[f64; 3]>,
125}
126
127impl ScfSolver for Pm3ScfSolver {
128 fn solve(&self, _config: &ScfConvergenceConfig) -> Result<ScfOutput, String> {
129 let result = crate::pm3::solver::solve_pm3(&self.elements, &self.positions)?;
130 Ok(ScfOutput {
131 energy: result.total_energy,
132 orbital_energies: result.orbital_energies,
133 iterations: result.scf_iterations,
134 converged: result.converged,
135 density: None,
136 mulliken_charges: Some(result.mulliken_charges),
137 })
138 }
139
140 fn method_name(&self) -> &str {
141 "PM3"
142 }
143 fn n_basis(&self) -> usize {
144 0
145 } fn n_electrons(&self) -> usize {
147 0
148 }
149}
150
151pub struct XtbScfSolver {
153 pub elements: Vec<u8>,
154 pub positions: Vec<[f64; 3]>,
155}
156
157impl ScfSolver for XtbScfSolver {
158 fn solve(&self, _config: &ScfConvergenceConfig) -> Result<ScfOutput, String> {
159 let result = crate::xtb::solver::solve_xtb(&self.elements, &self.positions)?;
160 Ok(ScfOutput {
161 energy: result.total_energy,
162 orbital_energies: result.orbital_energies,
163 iterations: result.scc_iterations,
164 converged: result.converged,
165 density: None,
166 mulliken_charges: Some(result.mulliken_charges),
167 })
168 }
169
170 fn method_name(&self) -> &str {
171 "xTB"
172 }
173 fn n_basis(&self) -> usize {
174 0
175 }
176 fn n_electrons(&self) -> usize {
177 0
178 }
179}