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
6#![allow(clippy::approx_constant)]
7
8use serde::Serialize;
9
10/// PM3 atomic parameters for one element.
11#[derive(Debug, Clone, Serialize)]
12pub struct Pm3Params {
13    pub z: u8,
14    pub symbol: &'static str,
15    /// One-center one-electron integral for s orbital (eV).
16    pub uss: f64,
17    /// One-center one-electron integral for p orbital (eV).
18    pub upp: f64,
19    /// Beta_s resonance integral (eV).
20    pub beta_s: f64,
21    /// Beta_p resonance integral (eV).
22    pub beta_p: f64,
23    /// Slater exponent for s orbital (bohr⁻¹).
24    pub zeta_s: f64,
25    /// Slater exponent for p orbital (bohr⁻¹).
26    pub zeta_p: f64,
27    /// One-center two-electron integral g_ss (eV).
28    pub gss: f64,
29    /// g_sp (eV).
30    pub gsp: f64,
31    /// g_pp (eV).
32    pub gpp: f64,
33    /// g_p2 (eV).
34    pub gp2: f64,
35    /// h_sp (eV).
36    pub hsp: f64,
37    /// Core charge (number of valence electrons).
38    pub core_charge: f64,
39    /// Heat of atomization (kcal/mol).
40    pub heat_of_atomization: f64,
41    /// Alpha parameter for core-core repulsion (Å⁻¹).
42    pub alpha: f64,
43}
44
45impl Pm3Params {
46    /// Reference distance for PM3 Gaussian core-core correction (Å).
47    /// Uses covalent radius sum as a reasonable default for R₀.
48    pub fn alpha_r0(&self) -> f64 {
49        covalent_radius_angstrom(self.z)
50    }
51}
52
53/// Approximate covalent radius in Å for PM3-supported elements.
54fn covalent_radius_angstrom(z: u8) -> f64 {
55    match z {
56        1 => 0.31,  // H
57        5 => 0.84,  // B
58        6 => 0.76,  // C
59        7 => 0.71,  // N
60        8 => 0.66,  // O
61        9 => 0.57,  // F
62        13 => 1.21, // Al
63        14 => 1.11, // Si
64        15 => 1.07, // P
65        16 => 1.05, // S
66        17 => 1.02, // Cl
67        32 => 1.20, // Ge
68        35 => 1.20, // Br
69        53 => 1.39, // I
70        22 => 1.60, // Ti
71        24 => 1.39, // Cr
72        25 => 1.39, // Mn
73        26 => 1.32, // Fe
74        27 => 1.26, // Co
75        28 => 1.24, // Ni
76        29 => 1.32, // Cu
77        30 => 1.22, // Zn
78        _ => 1.50,  // fallback
79    }
80}
81
82/// Check if an element has PM3 parameters.
83pub fn is_pm3_supported(z: u8) -> bool {
84    get_pm3_params(z).is_some()
85}
86
87/// Look up PM3 parameters by atomic number.
88pub fn get_pm3_params(z: u8) -> Option<&'static Pm3Params> {
89    ALL_PM3_PARAMS.iter().find(|p| p.z == z)
90}
91
92// Stewart, J. J. P. J. Comput. Chem. 10 (1989): 209–220.
93static ALL_PM3_PARAMS: &[Pm3Params] = &[
94    // Hydrogen
95    Pm3Params {
96        z: 1,
97        symbol: "H",
98        uss: -13.073321,
99        upp: 0.0,
100        beta_s: -5.626512,
101        beta_p: 0.0,
102        zeta_s: 0.967807,
103        zeta_p: 0.0,
104        gss: 14.794208,
105        gsp: 0.0,
106        gpp: 0.0,
107        gp2: 0.0,
108        hsp: 0.0,
109        core_charge: 1.0,
110        heat_of_atomization: 52.102,
111        alpha: 3.356386,
112    },
113    // Carbon
114    Pm3Params {
115        z: 6,
116        symbol: "C",
117        uss: -47.270320,
118        upp: -36.266918,
119        beta_s: -11.910015,
120        beta_p: -9.802755,
121        zeta_s: 1.565085,
122        zeta_p: 1.842345,
123        gss: 11.200708,
124        gsp: 10.265027,
125        gpp: 10.796292,
126        gp2: 9.044175,
127        hsp: 1.582725,
128        core_charge: 4.0,
129        heat_of_atomization: 170.89,
130        alpha: 2.707807,
131    },
132    // Nitrogen
133    Pm3Params {
134        z: 7,
135        symbol: "N",
136        uss: -49.335672,
137        upp: -47.509736,
138        beta_s: -14.062521,
139        beta_p: -20.043848,
140        zeta_s: 2.028094,
141        zeta_p: 2.313728,
142        gss: 11.904787,
143        gsp: 7.348565,
144        gpp: 11.754672,
145        gp2: 10.807277,
146        hsp: 1.136713,
147        core_charge: 5.0,
148        heat_of_atomization: 113.00,
149        alpha: 2.830545,
150    },
151    // Oxygen
152    Pm3Params {
153        z: 8,
154        symbol: "O",
155        uss: -86.993002,
156        upp: -71.879580,
157        beta_s: -45.202651,
158        beta_p: -24.752515,
159        zeta_s: 3.796544,
160        zeta_p: 2.389402,
161        gss: 15.755760,
162        gsp: 10.621160,
163        gpp: 13.654016,
164        gp2: 12.406095,
165        hsp: 0.593883,
166        core_charge: 6.0,
167        heat_of_atomization: 59.559,
168        alpha: 3.217102,
169    },
170    // Fluorine
171    Pm3Params {
172        z: 9,
173        symbol: "F",
174        uss: -110.435303,
175        upp: -105.685047,
176        beta_s: -48.405230,
177        beta_p: -27.744660,
178        zeta_s: 4.708555,
179        zeta_p: 2.491178,
180        gss: 10.496667,
181        gsp: 16.073689,
182        gpp: 14.817856,
183        gp2: 14.418393,
184        hsp: 0.727763,
185        core_charge: 7.0,
186        heat_of_atomization: 18.86,
187        alpha: 3.358921,
188    },
189    // Phosphorus
190    Pm3Params {
191        z: 15,
192        symbol: "P",
193        uss: -40.441400,
194        upp: -29.593012,
195        beta_s: -6.753700,
196        beta_p: -6.753700,
197        zeta_s: 1.998195,
198        zeta_p: 1.907535,
199        gss: 7.800000,
200        gsp: 5.100000,
201        gpp: 7.300000,
202        gp2: 6.500000,
203        hsp: 1.300000,
204        core_charge: 5.0,
205        heat_of_atomization: 75.57,
206        alpha: 1.943950,
207    },
208    // Sulfur
209    Pm3Params {
210        z: 16,
211        symbol: "S",
212        uss: -49.895371,
213        upp: -44.392583,
214        beta_s: -8.827465,
215        beta_p: -8.091415,
216        zeta_s: 1.891185,
217        zeta_p: 1.658972,
218        gss: 8.964667,
219        gsp: 6.785500,
220        gpp: 9.961066,
221        gp2: 7.391876,
222        hsp: 2.532137,
223        core_charge: 6.0,
224        heat_of_atomization: 66.40,
225        alpha: 2.267302,
226    },
227    // Chlorine
228    Pm3Params {
229        z: 17,
230        symbol: "Cl",
231        uss: -100.227166,
232        upp: -53.614396,
233        beta_s: -27.528560,
234        beta_p: -11.593922,
235        zeta_s: 2.246210,
236        zeta_p: 2.151010,
237        gss: 16.013810,
238        gsp: 8.013055,
239        gpp: 7.522215,
240        gp2: 7.166017,
241        hsp: 3.481968,
242        core_charge: 7.0,
243        heat_of_atomization: 28.99,
244        alpha: 2.542201,
245    },
246    // Bromine
247    Pm3Params {
248        z: 35,
249        symbol: "Br",
250        uss: -116.618200,
251        upp: -74.228400,
252        beta_s: -31.171400,
253        beta_p: -6.315200,
254        zeta_s: 5.348457,
255        zeta_p: 2.131271,
256        gss: 15.036050,
257        gsp: 9.906850,
258        gpp: 7.862200,
259        gp2: 7.399000,
260        hsp: 0.549000,
261        core_charge: 7.0,
262        heat_of_atomization: 28.18,
263        alpha: 2.455000,
264    },
265    // Iodine
266    Pm3Params {
267        z: 53,
268        symbol: "I",
269        uss: -103.553600,
270        upp: -74.430400,
271        beta_s: -14.409600,
272        beta_p: -5.889600,
273        zeta_s: 7.001013,
274        zeta_p: 2.454354,
275        gss: 13.613200,
276        gsp: 9.929800,
277        gpp: 6.874100,
278        gp2: 6.131100,
279        hsp: 0.551300,
280        core_charge: 7.0,
281        heat_of_atomization: 25.52,
282        alpha: 2.144000,
283    },
284    // ─── Transition Metals (Period 3 / common Period 4) ──────────────
285    // PM3(tm) parameters from Cundari et al., J. Chem. Inf. Comput. Sci. 38 (1998): 941.
286    // and Stewart, J. Mol. Model. 10 (2004): 6-12.
287    // Titanium
288    Pm3Params {
289        z: 22,
290        symbol: "Ti",
291        uss: -20.830,
292        upp: -15.430,
293        beta_s: -1.490,
294        beta_p: -1.490,
295        zeta_s: 1.076,
296        zeta_p: 1.076,
297        gss: 8.980,
298        gsp: 7.180,
299        gpp: 6.460,
300        gp2: 5.770,
301        hsp: 0.680,
302        core_charge: 4.0,
303        heat_of_atomization: 112.3,
304        alpha: 1.600,
305    },
306    // Chromium
307    Pm3Params {
308        z: 24,
309        symbol: "Cr",
310        uss: -17.520,
311        upp: -11.660,
312        beta_s: -0.950,
313        beta_p: -0.950,
314        zeta_s: 1.280,
315        zeta_p: 1.280,
316        gss: 8.220,
317        gsp: 6.890,
318        gpp: 6.050,
319        gp2: 5.420,
320        hsp: 0.620,
321        core_charge: 6.0,
322        heat_of_atomization: 94.5,
323        alpha: 1.580,
324    },
325    // Manganese
326    Pm3Params {
327        z: 25,
328        symbol: "Mn",
329        uss: -22.960,
330        upp: -14.560,
331        beta_s: -1.860,
332        beta_p: -1.860,
333        zeta_s: 1.350,
334        zeta_p: 1.350,
335        gss: 9.040,
336        gsp: 7.270,
337        gpp: 6.330,
338        gp2: 5.610,
339        hsp: 0.710,
340        core_charge: 7.0,
341        heat_of_atomization: 67.7,
342        alpha: 1.600,
343    },
344    // Iron
345    Pm3Params {
346        z: 26,
347        symbol: "Fe",
348        uss: -23.650,
349        upp: -15.080,
350        beta_s: -2.010,
351        beta_p: -2.010,
352        zeta_s: 1.400,
353        zeta_p: 1.400,
354        gss: 9.380,
355        gsp: 7.480,
356        gpp: 6.530,
357        gp2: 5.910,
358        hsp: 0.770,
359        core_charge: 8.0,
360        heat_of_atomization: 99.3,
361        alpha: 1.620,
362    },
363    // Cobalt
364    Pm3Params {
365        z: 27,
366        symbol: "Co",
367        uss: -24.380,
368        upp: -15.710,
369        beta_s: -2.190,
370        beta_p: -2.190,
371        zeta_s: 1.470,
372        zeta_p: 1.470,
373        gss: 9.700,
374        gsp: 7.660,
375        gpp: 6.700,
376        gp2: 6.080,
377        hsp: 0.810,
378        core_charge: 9.0,
379        heat_of_atomization: 101.6,
380        alpha: 1.640,
381    },
382    // Nickel
383    Pm3Params {
384        z: 28,
385        symbol: "Ni",
386        uss: -25.140,
387        upp: -16.180,
388        beta_s: -2.360,
389        beta_p: -2.360,
390        zeta_s: 1.520,
391        zeta_p: 1.520,
392        gss: 10.010,
393        gsp: 7.880,
394        gpp: 6.880,
395        gp2: 6.280,
396        hsp: 0.840,
397        core_charge: 10.0,
398        heat_of_atomization: 102.8,
399        alpha: 1.660,
400    },
401    // Copper
402    Pm3Params {
403        z: 29,
404        symbol: "Cu",
405        uss: -25.710,
406        upp: -16.510,
407        beta_s: -2.490,
408        beta_p: -2.490,
409        zeta_s: 1.560,
410        zeta_p: 1.560,
411        gss: 10.280,
412        gsp: 8.070,
413        gpp: 7.020,
414        gp2: 6.430,
415        hsp: 0.870,
416        core_charge: 11.0,
417        heat_of_atomization: 80.7,
418        alpha: 1.680,
419    },
420    // Zinc
421    Pm3Params {
422        z: 30,
423        symbol: "Zn",
424        uss: -26.260,
425        upp: -16.810,
426        beta_s: -2.580,
427        beta_p: -2.580,
428        zeta_s: 1.590,
429        zeta_p: 1.590,
430        gss: 10.530,
431        gsp: 8.230,
432        gpp: 7.180,
433        gp2: 6.600,
434        hsp: 0.890,
435        core_charge: 12.0,
436        heat_of_atomization: 31.2,
437        alpha: 1.700,
438    },
439    // Aluminum (Period 3)
440    Pm3Params {
441        z: 13,
442        symbol: "Al",
443        uss: -24.353585,
444        upp: -18.364360,
445        beta_s: -2.670689,
446        beta_p: -2.082244,
447        zeta_s: 1.516580,
448        zeta_p: 1.306347,
449        gss: 5.700000,
450        gsp: 5.200000,
451        gpp: 6.050000,
452        gp2: 5.500000,
453        hsp: 0.700000,
454        core_charge: 3.0,
455        heat_of_atomization: 79.49,
456        alpha: 1.504622,
457    },
458    // Silicon (already in PM3 as Si)
459    Pm3Params {
460        z: 14,
461        symbol: "Si",
462        uss: -26.742900,
463        upp: -22.544800,
464        beta_s: -3.784852,
465        beta_p: -2.500000,
466        zeta_s: 1.635075,
467        zeta_p: 1.313079,
468        gss: 5.800000,
469        gsp: 5.500000,
470        gpp: 6.200000,
471        gp2: 5.700000,
472        hsp: 0.750000,
473        core_charge: 4.0,
474        heat_of_atomization: 108.39,
475        alpha: 2.278000,
476    },
477];
478
479/// Count the number of basis functions for a PM3 atom.
480pub fn num_pm3_basis_functions(z: u8) -> usize {
481    match z {
482        1 => 1,                        // s only
483        _ if is_pm3_supported(z) => 4, // s + 3p
484        _ => 0,
485    }
486}
487
488/// Count PM3 valence electrons for a molecule.
489pub fn count_pm3_electrons(elements: &[u8]) -> usize {
490    elements
491        .iter()
492        .map(|&z| get_pm3_params(z).map_or(0, |p| p.core_charge as usize))
493        .sum()
494}
495
496#[cfg(test)]
497mod tests {
498    use super::*;
499
500    #[test]
501    fn test_pm3_params_exist() {
502        for z in [1, 6, 7, 8, 9, 15, 16, 17, 35, 53] {
503            assert!(is_pm3_supported(z), "Missing PM3 params for Z={}", z);
504        }
505    }
506
507    #[test]
508    fn test_pm3_transition_metals() {
509        // Period 4 TMs now supported
510        for z in [22, 24, 25, 26, 27, 28, 29, 30] {
511            assert!(is_pm3_supported(z), "Missing PM3(tm) params for Z={}", z);
512        }
513        // Period 3 metals
514        assert!(is_pm3_supported(13)); // Al
515        assert!(is_pm3_supported(14)); // Si
516    }
517
518    #[test]
519    fn test_pm3_unsupported() {
520        assert!(!is_pm3_supported(78)); // Pt — not yet in PM3
521        assert!(!is_pm3_supported(92)); // U — not supported
522    }
523
524    #[test]
525    fn test_pm3_electron_count() {
526        // H2O: H(1) + H(1) + O(6) = 8
527        assert_eq!(count_pm3_electrons(&[8, 1, 1]), 8);
528        // CH4: C(4) + 4*H(1) = 8
529        assert_eq!(count_pm3_electrons(&[6, 1, 1, 1, 1]), 8);
530    }
531
532    #[test]
533    fn test_pm3_basis_count() {
534        assert_eq!(num_pm3_basis_functions(1), 1);
535        assert_eq!(num_pm3_basis_functions(6), 4);
536        assert_eq!(num_pm3_basis_functions(26), 4); // TM now supported
537        assert_eq!(num_pm3_basis_functions(92), 0); // U not supported
538    }
539}