1#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
7pub struct D4Params {
8 pub c6_ref: f64,
10 pub c8_ref: f64,
12 pub alpha0: f64,
14 pub r_cov: f64,
16}
17
18pub fn get_d4_params(z: u8) -> D4Params {
20 match z {
21 1 => D4Params {
22 c6_ref: 6.50,
23 c8_ref: 94.6,
24 alpha0: 4.50,
25 r_cov: 0.60,
26 },
27 5 => D4Params {
28 c6_ref: 99.5,
29 c8_ref: 3430.0,
30 alpha0: 21.0,
31 r_cov: 1.61,
32 },
33 6 => D4Params {
34 c6_ref: 46.6,
35 c8_ref: 1315.0,
36 alpha0: 12.0,
37 r_cov: 1.46,
38 },
39 7 => D4Params {
40 c6_ref: 24.2,
41 c8_ref: 560.0,
42 alpha0: 7.40,
43 r_cov: 1.42,
44 },
45 8 => D4Params {
46 c6_ref: 15.6,
47 c8_ref: 300.0,
48 alpha0: 5.40,
49 r_cov: 1.38,
50 },
51 9 => D4Params {
52 c6_ref: 9.52,
53 c8_ref: 160.0,
54 alpha0: 3.80,
55 r_cov: 1.34,
56 },
57 14 => D4Params {
58 c6_ref: 305.0,
59 c8_ref: 15700.0,
60 alpha0: 37.0,
61 r_cov: 2.21,
62 },
63 15 => D4Params {
64 c6_ref: 185.0,
65 c8_ref: 8200.0,
66 alpha0: 25.0,
67 r_cov: 2.08,
68 },
69 16 => D4Params {
70 c6_ref: 134.0,
71 c8_ref: 5470.0,
72 alpha0: 19.6,
73 r_cov: 1.96,
74 },
75 17 => D4Params {
76 c6_ref: 94.6,
77 c8_ref: 3430.0,
78 alpha0: 15.0,
79 r_cov: 1.88,
80 },
81 35 => D4Params {
82 c6_ref: 162.0,
83 c8_ref: 6800.0,
84 alpha0: 21.0,
85 r_cov: 2.16,
86 },
87 53 => D4Params {
88 c6_ref: 385.0,
89 c8_ref: 20500.0,
90 alpha0: 35.0,
91 r_cov: 2.51,
92 },
93 22 => D4Params {
94 c6_ref: 379.0,
95 c8_ref: 17100.0,
96 alpha0: 45.0,
97 r_cov: 2.61,
98 },
99 26 => D4Params {
100 c6_ref: 202.0,
101 c8_ref: 7900.0,
102 alpha0: 30.0,
103 r_cov: 2.45,
104 },
105 29 => D4Params {
106 c6_ref: 171.0,
107 c8_ref: 6100.0,
108 alpha0: 24.0,
109 r_cov: 2.49,
110 },
111 30 => D4Params {
112 c6_ref: 207.0,
113 c8_ref: 8000.0,
114 alpha0: 28.0,
115 r_cov: 2.45,
116 },
117 2 => D4Params {
119 c6_ref: 1.46,
120 c8_ref: 14.1,
121 alpha0: 1.38,
122 r_cov: 0.46,
123 }, 3 => D4Params {
125 c6_ref: 1387.0,
126 c8_ref: 127800.0,
127 alpha0: 164.0,
128 r_cov: 2.49,
129 }, 4 => D4Params {
131 c6_ref: 214.0,
132 c8_ref: 12100.0,
133 alpha0: 38.0,
134 r_cov: 1.89,
135 }, 10 => D4Params {
137 c6_ref: 6.38,
138 c8_ref: 76.0,
139 alpha0: 2.67,
140 r_cov: 1.10,
141 }, 11 => D4Params {
143 c6_ref: 1556.0,
144 c8_ref: 165500.0,
145 alpha0: 163.0,
146 r_cov: 2.99,
147 }, 12 => D4Params {
149 c6_ref: 626.0,
150 c8_ref: 46400.0,
151 alpha0: 71.0,
152 r_cov: 2.74,
153 }, 13 => D4Params {
155 c6_ref: 528.0,
156 c8_ref: 35300.0,
157 alpha0: 60.0,
158 r_cov: 2.38,
159 }, 18 => D4Params {
161 c6_ref: 64.3,
162 c8_ref: 2450.0,
163 alpha0: 11.1,
164 r_cov: 1.74,
165 }, 19 => D4Params {
167 c6_ref: 3897.0,
168 c8_ref: 536300.0,
169 alpha0: 290.0,
170 r_cov: 3.59,
171 }, 20 => D4Params {
173 c6_ref: 2190.0,
174 c8_ref: 246200.0,
175 alpha0: 160.0,
176 r_cov: 3.31,
177 }, 21 => D4Params {
180 c6_ref: 571.0,
181 c8_ref: 30500.0,
182 alpha0: 55.0,
183 r_cov: 2.76,
184 }, 23 => D4Params {
186 c6_ref: 335.0,
187 c8_ref: 14800.0,
188 alpha0: 40.0,
189 r_cov: 2.53,
190 }, 24 => D4Params {
192 c6_ref: 276.0,
193 c8_ref: 11400.0,
194 alpha0: 35.0,
195 r_cov: 2.49,
196 }, 25 => D4Params {
198 c6_ref: 245.0,
199 c8_ref: 9700.0,
200 alpha0: 33.0,
201 r_cov: 2.49,
202 }, 27 => D4Params {
204 c6_ref: 175.0,
205 c8_ref: 6500.0,
206 alpha0: 27.0,
207 r_cov: 2.38,
208 }, 28 => D4Params {
210 c6_ref: 155.0,
211 c8_ref: 5500.0,
212 alpha0: 24.0,
213 r_cov: 2.34,
214 }, 39 => D4Params {
217 c6_ref: 700.0,
218 c8_ref: 42000.0,
219 alpha0: 65.0,
220 r_cov: 2.95,
221 }, 40 => D4Params {
223 c6_ref: 540.0,
224 c8_ref: 29000.0,
225 alpha0: 55.0,
226 r_cov: 2.80,
227 }, 42 => D4Params {
229 c6_ref: 340.0,
230 c8_ref: 15000.0,
231 alpha0: 40.0,
232 r_cov: 2.61,
233 }, 44 => D4Params {
235 c6_ref: 252.0,
236 c8_ref: 10200.0,
237 alpha0: 33.0,
238 r_cov: 2.53,
239 }, 45 => D4Params {
241 c6_ref: 220.0,
242 c8_ref: 8500.0,
243 alpha0: 29.0,
244 r_cov: 2.49,
245 }, 46 => D4Params {
247 c6_ref: 205.0,
248 c8_ref: 7600.0,
249 alpha0: 26.0,
250 r_cov: 2.49,
251 }, 47 => D4Params {
253 c6_ref: 253.0,
254 c8_ref: 10200.0,
255 alpha0: 33.0,
256 r_cov: 2.72,
257 }, 48 => D4Params {
259 c6_ref: 323.0,
260 c8_ref: 14500.0,
261 alpha0: 46.0,
262 r_cov: 2.76,
263 }, 72 => D4Params {
266 c6_ref: 485.0,
267 c8_ref: 24000.0,
268 alpha0: 50.0,
269 r_cov: 2.80,
270 }, 74 => D4Params {
272 c6_ref: 375.0,
273 c8_ref: 16500.0,
274 alpha0: 42.0,
275 r_cov: 2.68,
276 }, 76 => D4Params {
278 c6_ref: 280.0,
279 c8_ref: 11500.0,
280 alpha0: 34.0,
281 r_cov: 2.53,
282 }, 77 => D4Params {
284 c6_ref: 245.0,
285 c8_ref: 9500.0,
286 alpha0: 30.0,
287 r_cov: 2.53,
288 }, 78 => D4Params {
290 c6_ref: 225.0,
291 c8_ref: 8500.0,
292 alpha0: 28.0,
293 r_cov: 2.53,
294 }, 79 => D4Params {
296 c6_ref: 255.0,
297 c8_ref: 10500.0,
298 alpha0: 36.0,
299 r_cov: 2.57,
300 }, 80 => D4Params {
302 c6_ref: 305.0,
303 c8_ref: 13400.0,
304 alpha0: 34.0,
305 r_cov: 2.53,
306 }, 31 => D4Params {
309 c6_ref: 498.0,
310 c8_ref: 30600.0,
311 alpha0: 50.0,
312 r_cov: 2.34,
313 }, 32 => D4Params {
315 c6_ref: 354.0,
316 c8_ref: 18600.0,
317 alpha0: 40.0,
318 r_cov: 2.30,
319 }, 33 => D4Params {
321 c6_ref: 246.0,
322 c8_ref: 11400.0,
323 alpha0: 30.0,
324 r_cov: 2.23,
325 }, 34 => D4Params {
327 c6_ref: 210.0,
328 c8_ref: 9100.0,
329 alpha0: 26.0,
330 r_cov: 2.19,
331 }, 49 => D4Params {
333 c6_ref: 700.0,
334 c8_ref: 47000.0,
335 alpha0: 65.0,
336 r_cov: 2.53,
337 }, 50 => D4Params {
339 c6_ref: 530.0,
340 c8_ref: 32000.0,
341 alpha0: 53.0,
342 r_cov: 2.53,
343 }, 51 => D4Params {
345 c6_ref: 405.0,
346 c8_ref: 21000.0,
347 alpha0: 43.0,
348 r_cov: 2.49,
349 }, 52 => D4Params {
351 c6_ref: 345.0,
352 c8_ref: 16500.0,
353 alpha0: 38.0,
354 r_cov: 2.49,
355 }, _ => D4Params {
357 c6_ref: 50.0,
358 c8_ref: 1500.0,
359 alpha0: 12.0,
360 r_cov: 1.80,
361 },
362 }
363}
364
365pub fn d4_coordination_number(elements: &[u8], positions: &[[f64; 3]]) -> Vec<f64> {
367 let n = elements.len();
368 let bohr_to_ang = 0.529177;
369 let mut cn = vec![0.0; n];
370
371 for i in 0..n {
372 let pi = get_d4_params(elements[i]);
373 for j in (i + 1)..n {
374 let pj = get_d4_params(elements[j]);
375 let r_cov_ij = (pi.r_cov + pj.r_cov) * bohr_to_ang;
376
377 let dx = positions[i][0] - positions[j][0];
378 let dy = positions[i][1] - positions[j][1];
379 let dz = positions[i][2] - positions[j][2];
380 let r_ij = (dx * dx + dy * dy + dz * dz).sqrt();
381
382 if r_ij < 1e-10 {
383 continue;
384 }
385
386 let f = 1.0 / (1.0 + (-16.0 * (r_cov_ij / r_ij - 1.0)).exp());
387 cn[i] += f;
388 cn[j] += f;
389 }
390 }
391
392 cn
393}
394
395pub fn get_c6_reference(z_a: u8, z_b: u8) -> f64 {
397 let pa = get_d4_params(z_a);
398 let pb = get_d4_params(z_b);
399 let sum_alpha = pa.alpha0 + pb.alpha0;
400 if sum_alpha < 1e-15 {
401 return 0.0;
402 }
403 1.5 * pa.alpha0 * pb.alpha0 / sum_alpha
404}
405
406pub fn dynamic_c6(z_a: u8, z_b: u8, cn_a: f64, cn_b: f64) -> f64 {
408 let c6_ref = get_c6_reference(z_a, z_b);
409 let cn_ref_a = expected_cn(z_a);
410 let cn_ref_b = expected_cn(z_b);
411
412 let w_a = (-4.0 * (cn_a / cn_ref_a - 1.0).powi(2)).exp();
413 let w_b = (-4.0 * (cn_b / cn_ref_b - 1.0).powi(2)).exp();
414
415 c6_ref * w_a * w_b
416}
417
418fn expected_cn(z: u8) -> f64 {
419 match z {
420 1 => 1.0,
421 2 => 0.0, 3 | 11 | 19 => 1.0, 4 | 12 | 20 => 2.0, 5 | 13 => 3.0, 6 | 14 | 32 => 4.0, 7 | 15 | 33 => 3.0, 8 | 16 | 34 | 52 => 2.0, 9 | 17 | 35 | 53 => 1.0, 10 | 18 => 0.0, 21 => 6.0, 22 => 6.0, 23 => 6.0, 24 => 6.0, 25 => 6.0, 26 => 6.0, 27 => 6.0, 28 => 4.0, 29 => 4.0, 30 => 4.0, 39 => 6.0,
443 40 => 6.0,
444 42 => 6.0,
445 44 => 6.0,
446 45 => 6.0,
447 46 => 4.0,
448 47 => 4.0,
449 48 => 4.0,
450 72 => 6.0,
452 74 => 6.0,
453 76 => 6.0,
454 77 => 6.0,
455 78 => 4.0,
456 79 => 4.0,
457 80 => 4.0,
458 31 | 49 => 3.0, 50 | 51 => 4.0, _ => 4.0,
462 }
463}
464
465pub fn c8_from_c6(c6: f64, z_a: u8, z_b: u8) -> f64 {
467 let pa = get_d4_params(z_a);
468 let pb = get_d4_params(z_b);
469 let qa = if pa.c6_ref > 1e-10 {
470 (pa.c8_ref / pa.c6_ref).sqrt()
471 } else {
472 5.0
473 };
474 let qb = if pb.c6_ref > 1e-10 {
475 (pb.c8_ref / pb.c6_ref).sqrt()
476 } else {
477 5.0
478 };
479 3.0 * c6 * (qa * qb).sqrt()
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 fn test_d4_cn_methane() {
488 let elements = [6, 1, 1, 1, 1];
489 let pos = [
490 [0.0, 0.0, 0.0],
491 [0.63, 0.63, 0.63],
492 [-0.63, -0.63, 0.63],
493 [-0.63, 0.63, -0.63],
494 [0.63, -0.63, -0.63],
495 ];
496 let cn = d4_coordination_number(&elements, &pos);
497 assert!(cn[0] > 1.0 && cn[0] < 5.0, "C CN = {}", cn[0]);
498 assert!(cn[1] > 0.3 && cn[1] < 1.5, "H CN = {}", cn[1]);
499 }
500
501 #[test]
502 fn test_c6_reference_positive() {
503 for &z in &[1u8, 6, 7, 8, 16] {
504 let c6 = get_c6_reference(z, z);
505 assert!(c6 > 0.0, "C6({},{}) = {}", z, z, c6);
506 }
507 }
508
509 #[test]
510 fn test_c8_from_c6_positive() {
511 let c6 = get_c6_reference(6, 6);
512 let c8 = c8_from_c6(c6, 6, 6);
513 assert!(c8 > c6, "C8 should be larger than C6");
514 }
515}