1#![allow(clippy::approx_constant)]
7
8use serde::Serialize;
9
10#[derive(Debug, Clone, Serialize)]
12pub struct Pm3Params {
13 pub z: u8,
14 pub symbol: &'static str,
15 pub uss: f64,
17 pub upp: f64,
19 pub beta_s: f64,
21 pub beta_p: f64,
23 pub zeta_s: f64,
25 pub zeta_p: f64,
27 pub gss: f64,
29 pub gsp: f64,
31 pub gpp: f64,
33 pub gp2: f64,
35 pub hsp: f64,
37 pub core_charge: f64,
39 pub heat_of_atomization: f64,
41 pub alpha: f64,
43}
44
45impl Pm3Params {
46 pub fn alpha_r0(&self) -> f64 {
49 covalent_radius_angstrom(self.z)
50 }
51}
52
53fn covalent_radius_angstrom(z: u8) -> f64 {
55 match z {
56 1 => 0.31, 5 => 0.84, 6 => 0.76, 7 => 0.71, 8 => 0.66, 9 => 0.57, 13 => 1.21, 14 => 1.11, 15 => 1.07, 16 => 1.05, 17 => 1.02, 32 => 1.20, 35 => 1.20, 53 => 1.39, 22 => 1.60, 24 => 1.39, 25 => 1.39, 26 => 1.32, 27 => 1.26, 28 => 1.24, 29 => 1.32, 30 => 1.22, _ => 1.50, }
80}
81
82pub fn is_pm3_supported(z: u8) -> bool {
84 get_pm3_params(z).is_some()
85}
86
87pub fn get_pm3_params(z: u8) -> Option<&'static Pm3Params> {
89 ALL_PM3_PARAMS.iter().find(|p| p.z == z)
90}
91
92static ALL_PM3_PARAMS: &[Pm3Params] = &[
94 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
479pub fn num_pm3_basis_functions(z: u8) -> usize {
481 match z {
482 1 => 1, _ if is_pm3_supported(z) => 4, _ => 0,
485 }
486}
487
488pub 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 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 assert!(is_pm3_supported(13)); assert!(is_pm3_supported(14)); }
517
518 #[test]
519 fn test_pm3_unsupported() {
520 assert!(!is_pm3_supported(78)); assert!(!is_pm3_supported(92)); }
523
524 #[test]
525 fn test_pm3_electron_count() {
526 assert_eq!(count_pm3_electrons(&[8, 1, 1]), 8);
528 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); assert_eq!(num_pm3_basis_functions(92), 0); }
539}