1pub mod traits;
2pub mod parameters;
3pub mod solver;
4
5pub use traits::{GasteigerAtom, GasteigerBond};
6pub use solver::GasteigerSolver;
7
8#[cfg(test)]
9mod tests {
10 use super::*;
11
12 struct MockAtom {
13 name: &'static str,
14 element: usize,
15 formal_charge: f32,
16 }
17
18 impl GasteigerAtom for MockAtom {
19 fn atomic_number(&self) -> usize { self.element }
20 fn formal_charge(&self) -> f32 { self.formal_charge }
21 }
22
23 struct MockBond {
24 pair: (usize, usize),
25 order: f32,
26 }
27
28 impl GasteigerBond for MockBond {
29 fn atom_indices(&self) -> (usize, usize) { self.pair }
30 fn bond_order(&self) -> f32 { self.order }
31 }
32
33 fn print_charges(atoms: &[MockAtom], charges: &[f64]) {
34 println!("{:<5} {:<10} {:<15}", "Idx", "Element", "Partial Charge");
35 for (i, (atom, &charge)) in atoms.iter().zip(charges.iter()).enumerate() {
36 println!("{:<5} {:<10} {:<15.6}", i, atom.name, charge);
37 }
38 let total: f64 = charges.iter().sum();
39 println!("Total Charge: {:.6}", total);
40 }
41
42 #[test]
43 fn test_triple_bonds() {
44 let solver = GasteigerSolver::default();
45
46 let atoms_ace = vec![
48 MockAtom { name: "C1", element: 6, formal_charge: 0.0 }, MockAtom { name: "C2", element: 6, formal_charge: 0.0 }, MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
51 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
52 ];
53 let bonds_ace = vec![
54 MockBond { pair: (0, 1), order: 3.0 }, MockBond { pair: (0, 2), order: 1.0 },
56 MockBond { pair: (1, 3), order: 1.0 },
57 ];
58 let charges_ace = solver.compute_charges(&atoms_ace, &bonds_ace);
59 println!("\n--- Acetylene (HC#CH) ---");
60 print_charges(&atoms_ace, &charges_ace);
61 assert!(charges_ace.iter().sum::<f64>().abs() < 1e-6);
62
63 let atoms_nit = vec![
65 MockAtom { name: "C_me", element: 6, formal_charge: 0.0 }, MockAtom { name: "C_cn", element: 6, formal_charge: 0.0 }, MockAtom { name: "N", element: 7, formal_charge: 0.0 }, MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
69 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
70 MockAtom { name: "H3", element: 1, formal_charge: 0.0 },
71 ];
72 let bonds_nit = vec![
73 MockBond { pair: (0, 1), order: 1.0 },
74 MockBond { pair: (1, 2), order: 3.0 }, MockBond { pair: (0, 3), order: 1.0 },
76 MockBond { pair: (0, 4), order: 1.0 },
77 MockBond { pair: (0, 5), order: 1.0 },
78 ];
79 let charges_nit = solver.compute_charges(&atoms_nit, &bonds_nit);
80 println!("\n--- Acetonitrile (CH3-CN) ---");
81 print_charges(&atoms_nit, &charges_nit);
82 assert!(charges_nit.iter().sum::<f64>().abs() < 1e-6);
83 }
84
85 #[test]
86 fn test_ethene_and_benzene() {
87 let solver = GasteigerSolver::default();
88
89 let atoms_ethene = vec![
90 MockAtom { name: "C1", element: 6, formal_charge: 0.0 },
91 MockAtom { name: "C2", element: 6, formal_charge: 0.0 },
92 MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
93 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
94 MockAtom { name: "H3", element: 1, formal_charge: 0.0 },
95 MockAtom { name: "H4", element: 1, formal_charge: 0.0 },
96 ];
97 let bonds_ethene = vec![
98 MockBond { pair: (0, 1), order: 2.0 },
99 MockBond { pair: (0, 2), order: 1.0 },
100 MockBond { pair: (0, 3), order: 1.0 },
101 MockBond { pair: (1, 4), order: 1.0 },
102 MockBond { pair: (1, 5), order: 1.0 },
103 ];
104 let charges_ethene = solver.compute_charges(&atoms_ethene, &bonds_ethene);
105 println!("\n--- Ethene (CH2=CH2) ---");
106 print_charges(&atoms_ethene, &charges_ethene);
107 assert!(charges_ethene.iter().sum::<f64>().abs() < 1e-6);
108
109 let mut atoms_benzene = Vec::new();
110 for _ in 0..6 { atoms_benzene.push(MockAtom { name: "C", element: 6, formal_charge: 0.0 }); }
111 for _ in 0..6 { atoms_benzene.push(MockAtom { name: "H", element: 1, formal_charge: 0.0 }); }
112
113 let mut bonds_benzene = Vec::new();
114 for i in 0..6 {
115 bonds_benzene.push(MockBond { pair: (i, (i + 1) % 6), order: 1.5 });
116 bonds_benzene.push(MockBond { pair: (i, i + 6), order: 1.0 });
117 }
118 let charges_benzene = solver.compute_charges(&atoms_benzene, &bonds_benzene);
119 println!("\n--- Benzene (C6H6) ---");
120 print_charges(&atoms_benzene, &charges_benzene);
121 assert!(charges_benzene.iter().sum::<f64>().abs() < 1e-6);
122 }
123
124 #[test]
125 fn test_sulfur_compounds() {
126 let solver = GasteigerSolver::default();
127 let atoms = vec![
128 MockAtom { name: "S", element: 16, formal_charge: 0.0 },
129 MockAtom { name: "C1", element: 6, formal_charge: 0.0 },
130 MockAtom { name: "C2", element: 6, formal_charge: 0.0 },
131 ];
132 let bonds = vec![
133 MockBond { pair: (0, 1), order: 1.0 },
134 MockBond { pair: (0, 2), order: 1.0 },
135 ];
136 let charges = solver.compute_charges(&atoms, &bonds);
137 println!("\n--- Dimethyl Sulfide (CH3-S-CH3) ---");
138 print_charges(&atoms, &charges);
139
140 let atoms_thio = vec![
141 MockAtom { name: "S", element: 16, formal_charge: 0.0 },
142 MockAtom { name: "C", element: 6, formal_charge: 0.0 },
143 ];
144 let bonds_thio = vec![
145 MockBond { pair: (0, 1), order: 2.0 },
146 ];
147 let charges_thio = solver.compute_charges(&atoms_thio, &bonds_thio);
148 println!("\n--- Thioformaldehyde (CH2=S) ---");
149 print_charges(&atoms_thio, &charges_thio);
150 assert!(charges_thio[0] < 0.0);
151 }
152
153 #[test]
154 fn test_unknown_element_handling() {
155 let atoms = vec![
156 MockAtom { name: "C", element: 6, formal_charge: 0.0 },
157 MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
158 MockAtom { name: "Pd", element: 46, formal_charge: 0.0 },
159 ];
160 let bonds = vec![
161 MockBond { pair: (0, 1), order: 1.0 },
162 MockBond { pair: (0, 2), order: 1.0 },
163 ];
164 let solver = GasteigerSolver::default();
165 let charges = solver.compute_charges(&atoms, &bonds);
166 println!("\n--- Unknown Element Test (C-H + C-Pd) ---");
167 print_charges(&atoms, &charges);
168 assert!(charges[2].abs() < 1e-10);
169 }
170
171 #[test]
172 fn test_methane_charges() {
173 let atoms = vec![
174 MockAtom { name: "C", element: 6, formal_charge: 0.0 },
175 MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
176 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
177 MockAtom { name: "H3", element: 1, formal_charge: 0.0 },
178 MockAtom { name: "H4", element: 1, formal_charge: 0.0 },
179 ];
180 let bonds = vec![
181 MockBond { pair: (0, 1), order: 1.0 },
182 MockBond { pair: (0, 2), order: 1.0 },
183 MockBond { pair: (0, 3), order: 1.0 },
184 MockBond { pair: (0, 4), order: 1.0 },
185 ];
186 let solver = GasteigerSolver::default();
187 let charges = solver.compute_charges(&atoms, &bonds);
188 println!("\n--- Methane (CH4) ---");
189 print_charges(&atoms, &charges);
190 assert!(charges[0] < 0.0);
191 }
192
193 #[test]
194 fn test_water_charges() {
195 let atoms = vec![
196 MockAtom { name: "O", element: 8, formal_charge: 0.0 },
197 MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
198 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
199 ];
200 let bonds = vec![
201 MockBond { pair: (0, 1), order: 1.0 },
202 MockBond { pair: (0, 2), order: 1.0 },
203 ];
204 let solver = GasteigerSolver::default();
205 let charges = solver.compute_charges(&atoms, &bonds);
206 println!("\n--- Water (H2O) ---");
207 print_charges(&atoms, &charges);
208 assert!(charges[0] < 0.0);
209 }
210
211 #[test]
212 fn test_multi_molecule_system() {
213 let atoms = vec![
214 MockAtom { name: "C", element: 6, formal_charge: 0.0 },
215 MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
216 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
217 MockAtom { name: "H3", element: 1, formal_charge: 0.0 },
218 MockAtom { name: "H4", element: 1, formal_charge: 0.0 },
219 MockAtom { name: "O", element: 8, formal_charge: 0.0 },
220 MockAtom { name: "Hw1", element: 1, formal_charge: 0.0 },
221 MockAtom { name: "Hw2", element: 1, formal_charge: 0.0 },
222 ];
223 let bonds = vec![
224 MockBond { pair: (0, 1), order: 1.0 },
225 MockBond { pair: (0, 2), order: 1.0 },
226 MockBond { pair: (0, 3), order: 1.0 },
227 MockBond { pair: (0, 4), order: 1.0 },
228 MockBond { pair: (5, 6), order: 1.0 },
229 MockBond { pair: (5, 7), order: 1.0 },
230 ];
231 let solver = GasteigerSolver::default();
232 let charges = solver.compute_charges(&atoms, &bonds);
233 println!("\n--- Multi-molecule (CH4 + H2O) ---");
234 print_charges(&atoms, &charges);
235 let methane_sum: f64 = charges[0..5].iter().sum();
236 let water_sum: f64 = charges[5..8].iter().sum();
237 assert!(methane_sum.abs() < 1e-6);
238 assert!(water_sum.abs() < 1e-6);
239 }
240
241 #[test]
242 fn test_ion_charge_conservation() {
243 let atoms = vec![
244 MockAtom { name: "N+", element: 7, formal_charge: 1.0 },
245 MockAtom { name: "H1", element: 1, formal_charge: 0.0 },
246 MockAtom { name: "H2", element: 1, formal_charge: 0.0 },
247 MockAtom { name: "H3", element: 1, formal_charge: 0.0 },
248 MockAtom { name: "H4", element: 1, formal_charge: 0.0 },
249 ];
250 let bonds = vec![
251 MockBond { pair: (0, 1), order: 1.0 },
252 MockBond { pair: (0, 2), order: 1.0 },
253 MockBond { pair: (0, 3), order: 1.0 },
254 MockBond { pair: (0, 4), order: 1.0 },
255 ];
256 let solver = GasteigerSolver::default();
257 let charges = solver.compute_charges(&atoms, &bonds);
258 println!("\n--- Ammonium Ion (NH4+) ---");
259 print_charges(&atoms, &charges);
260 let total_charge: f64 = charges.iter().sum();
261 assert!((total_charge - 1.0).abs() < 1e-6);
262 }
263}