Skip to main content

sci_form/pm3/
params.rs

1//! PM3 parameter tables.
2//!
3//! Standard PM3 parameters from Stewart, J. J. P. J. Comput. Chem. 10 (1989): 209.
4//! Only the most chemically useful elements are parameterized.
5
6use serde::Serialize;
7
8/// PM3 atomic parameters for one element.
9#[derive(Debug, Clone, Serialize)]
10pub struct Pm3Params {
11    pub z: u8,
12    pub symbol: &'static str,
13    /// One-center one-electron integral for s orbital (eV).
14    pub uss: f64,
15    /// One-center one-electron integral for p orbital (eV).
16    pub upp: f64,
17    /// Beta_s resonance integral (eV).
18    pub beta_s: f64,
19    /// Beta_p resonance integral (eV).
20    pub beta_p: f64,
21    /// Slater exponent for s orbital (bohr⁻¹).
22    pub zeta_s: f64,
23    /// Slater exponent for p orbital (bohr⁻¹).
24    pub zeta_p: f64,
25    /// One-center two-electron integral g_ss (eV).
26    pub gss: f64,
27    /// g_sp (eV).
28    pub gsp: f64,
29    /// g_pp (eV).
30    pub gpp: f64,
31    /// g_p2 (eV).
32    pub gp2: f64,
33    /// h_sp (eV).
34    pub hsp: f64,
35    /// Core charge (number of valence electrons).
36    pub core_charge: f64,
37    /// Heat of atomization (kcal/mol).
38    pub heat_of_atomization: f64,
39    /// Alpha parameter for core-core repulsion (Å⁻¹).
40    pub alpha: f64,
41}
42
43/// Check if an element has PM3 parameters.
44pub fn is_pm3_supported(z: u8) -> bool {
45    get_pm3_params(z).is_some()
46}
47
48/// Look up PM3 parameters by atomic number.
49pub fn get_pm3_params(z: u8) -> Option<&'static Pm3Params> {
50    ALL_PM3_PARAMS.iter().find(|p| p.z == z)
51}
52
53// Stewart, J. J. P. J. Comput. Chem. 10 (1989): 209–220.
54static ALL_PM3_PARAMS: &[Pm3Params] = &[
55    // Hydrogen
56    Pm3Params {
57        z: 1,
58        symbol: "H",
59        uss: -13.073321,
60        upp: 0.0,
61        beta_s: -5.626512,
62        beta_p: 0.0,
63        zeta_s: 0.967807,
64        zeta_p: 0.0,
65        gss: 14.794208,
66        gsp: 0.0,
67        gpp: 0.0,
68        gp2: 0.0,
69        hsp: 0.0,
70        core_charge: 1.0,
71        heat_of_atomization: 52.102,
72        alpha: 3.356386,
73    },
74    // Carbon
75    Pm3Params {
76        z: 6,
77        symbol: "C",
78        uss: -47.270320,
79        upp: -36.266918,
80        beta_s: -11.910015,
81        beta_p: -9.802755,
82        zeta_s: 1.565085,
83        zeta_p: 1.842345,
84        gss: 11.200708,
85        gsp: 10.265027,
86        gpp: 10.796292,
87        gp2: 9.044175,
88        hsp: 1.582725,
89        core_charge: 4.0,
90        heat_of_atomization: 170.89,
91        alpha: 2.707807,
92    },
93    // Nitrogen
94    Pm3Params {
95        z: 7,
96        symbol: "N",
97        uss: -49.335672,
98        upp: -47.509736,
99        beta_s: -14.062521,
100        beta_p: -20.043848,
101        zeta_s: 2.028094,
102        zeta_p: 2.313728,
103        gss: 11.904787,
104        gsp: 7.348565,
105        gpp: 11.754672,
106        gp2: 10.807277,
107        hsp: 1.136713,
108        core_charge: 5.0,
109        heat_of_atomization: 113.00,
110        alpha: 2.830545,
111    },
112    // Oxygen
113    Pm3Params {
114        z: 8,
115        symbol: "O",
116        uss: -86.993002,
117        upp: -71.879580,
118        beta_s: -45.202651,
119        beta_p: -24.752515,
120        zeta_s: 3.796544,
121        zeta_p: 2.389402,
122        gss: 15.755760,
123        gsp: 10.621160,
124        gpp: 13.654016,
125        gp2: 12.406095,
126        hsp: 0.593883,
127        core_charge: 6.0,
128        heat_of_atomization: 59.559,
129        alpha: 3.217102,
130    },
131    // Fluorine
132    Pm3Params {
133        z: 9,
134        symbol: "F",
135        uss: -110.435303,
136        upp: -105.685047,
137        beta_s: -48.405230,
138        beta_p: -27.744660,
139        zeta_s: 4.708555,
140        zeta_p: 2.491178,
141        gss: 10.496667,
142        gsp: 16.073689,
143        gpp: 14.817856,
144        gp2: 14.418393,
145        hsp: 0.727763,
146        core_charge: 7.0,
147        heat_of_atomization: 18.86,
148        alpha: 3.358921,
149    },
150    // Phosphorus
151    Pm3Params {
152        z: 15,
153        symbol: "P",
154        uss: -40.441400,
155        upp: -29.593012,
156        beta_s: -6.753700,
157        beta_p: -6.753700,
158        zeta_s: 1.998195,
159        zeta_p: 1.907535,
160        gss: 7.800000,
161        gsp: 5.100000,
162        gpp: 7.300000,
163        gp2: 6.500000,
164        hsp: 1.300000,
165        core_charge: 5.0,
166        heat_of_atomization: 75.57,
167        alpha: 1.943950,
168    },
169    // Sulfur
170    Pm3Params {
171        z: 16,
172        symbol: "S",
173        uss: -49.895371,
174        upp: -44.392583,
175        beta_s: -8.827465,
176        beta_p: -8.091415,
177        zeta_s: 1.891185,
178        zeta_p: 1.658972,
179        gss: 8.964667,
180        gsp: 6.785500,
181        gpp: 9.961066,
182        gp2: 7.391876,
183        hsp: 2.532137,
184        core_charge: 6.0,
185        heat_of_atomization: 66.40,
186        alpha: 2.267302,
187    },
188    // Chlorine
189    Pm3Params {
190        z: 17,
191        symbol: "Cl",
192        uss: -100.227166,
193        upp: -53.614396,
194        beta_s: -27.528560,
195        beta_p: -11.593922,
196        zeta_s: 2.246210,
197        zeta_p: 2.151010,
198        gss: 16.013810,
199        gsp: 8.013055,
200        gpp: 7.522215,
201        gp2: 7.166017,
202        hsp: 3.481968,
203        core_charge: 7.0,
204        heat_of_atomization: 28.99,
205        alpha: 2.542201,
206    },
207    // Bromine
208    Pm3Params {
209        z: 35,
210        symbol: "Br",
211        uss: -116.618200,
212        upp: -74.228400,
213        beta_s: -31.171400,
214        beta_p: -6.315200,
215        zeta_s: 5.348457,
216        zeta_p: 2.131271,
217        gss: 15.036050,
218        gsp: 9.906850,
219        gpp: 7.862200,
220        gp2: 7.399000,
221        hsp: 0.549000,
222        core_charge: 7.0,
223        heat_of_atomization: 28.18,
224        alpha: 2.455000,
225    },
226    // Iodine
227    Pm3Params {
228        z: 53,
229        symbol: "I",
230        uss: -103.553600,
231        upp: -74.430400,
232        beta_s: -14.409600,
233        beta_p: -5.889600,
234        zeta_s: 7.001013,
235        zeta_p: 2.454354,
236        gss: 13.613200,
237        gsp: 9.929800,
238        gpp: 6.874100,
239        gp2: 6.131100,
240        hsp: 0.551300,
241        core_charge: 7.0,
242        heat_of_atomization: 25.52,
243        alpha: 2.144000,
244    },
245];
246
247/// Count the number of basis functions for a PM3 atom.
248pub fn num_pm3_basis_functions(z: u8) -> usize {
249    match z {
250        1 => 1,                        // s only
251        _ if is_pm3_supported(z) => 4, // s + 3p
252        _ => 0,
253    }
254}
255
256/// Count PM3 valence electrons for a molecule.
257pub fn count_pm3_electrons(elements: &[u8]) -> usize {
258    elements
259        .iter()
260        .map(|&z| get_pm3_params(z).map_or(0, |p| p.core_charge as usize))
261        .sum()
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267
268    #[test]
269    fn test_pm3_params_exist() {
270        for z in [1, 6, 7, 8, 9, 15, 16, 17, 35, 53] {
271            assert!(is_pm3_supported(z), "Missing PM3 params for Z={}", z);
272        }
273    }
274
275    #[test]
276    fn test_pm3_unsupported() {
277        assert!(!is_pm3_supported(26)); // Fe
278        assert!(!is_pm3_supported(78)); // Pt
279    }
280
281    #[test]
282    fn test_pm3_electron_count() {
283        // H2O: H(1) + H(1) + O(6) = 8
284        assert_eq!(count_pm3_electrons(&[8, 1, 1]), 8);
285        // CH4: C(4) + 4*H(1) = 8
286        assert_eq!(count_pm3_electrons(&[6, 1, 1, 1, 1]), 8);
287    }
288
289    #[test]
290    fn test_pm3_basis_count() {
291        assert_eq!(num_pm3_basis_functions(1), 1);
292        assert_eq!(num_pm3_basis_functions(6), 4);
293        assert_eq!(num_pm3_basis_functions(26), 0);
294    }
295}