1use std::f64::consts::PI;
16
17#[derive(Debug, Clone, Copy)]
19pub struct GaussianPrimitive {
20 pub alpha: f64,
22 pub coefficient: f64,
24}
25
26#[derive(Debug, Clone)]
28pub struct ContractedShell {
29 pub atom_index: usize,
31 pub center: [f64; 3],
33 pub l: u32,
35 pub primitives: Vec<GaussianPrimitive>,
37}
38
39impl ContractedShell {
40 pub fn n_cartesian(&self) -> usize {
43 match self.l {
44 0 => 1,
45 1 => 3,
46 2 => 6,
47 _ => ((self.l + 1) * (self.l + 2) / 2) as usize,
48 }
49 }
50
51 pub fn evaluate_s(&self, r_sq: f64) -> f64 {
53 self.primitives
54 .iter()
55 .map(|p| p.coefficient * (-p.alpha * r_sq).exp())
56 .sum()
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct BasisFunction {
63 pub atom_index: usize,
65 pub center: [f64; 3],
67 pub angular: [u32; 3],
69 pub l_total: u32,
71 pub primitives: Vec<GaussianPrimitive>,
73}
74
75impl BasisFunction {
76 pub fn normalization(alpha: f64, lx: u32, ly: u32, lz: u32) -> f64 {
80 let l = lx + ly + lz;
81 let prefactor = (2.0 * alpha / PI).powf(0.75) * (4.0 * alpha).powf(l as f64 / 2.0);
82 let denom = odd_double_factorial(2 * lx as i32 - 1)
83 * odd_double_factorial(2 * ly as i32 - 1)
84 * odd_double_factorial(2 * lz as i32 - 1);
85 prefactor / denom.sqrt()
86 }
87
88 pub fn evaluate(&self, x: f64, y: f64, z: f64) -> f64 {
90 let dx = x - self.center[0];
91 let dy = y - self.center[1];
92 let dz = z - self.center[2];
93 let r_sq = dx * dx + dy * dy + dz * dz;
94
95 let angular_part = dx.powi(self.angular[0] as i32)
96 * dy.powi(self.angular[1] as i32)
97 * dz.powi(self.angular[2] as i32);
98
99 let radial: f64 = self
100 .primitives
101 .iter()
102 .map(|p| {
103 let norm =
104 Self::normalization(p.alpha, self.angular[0], self.angular[1], self.angular[2]);
105 norm * p.coefficient * (-p.alpha * r_sq).exp()
106 })
107 .sum();
108
109 angular_part * radial
110 }
111}
112
113#[cfg(test)]
116fn double_factorial(n: u32) -> u64 {
117 if n <= 1 {
118 return 1;
119 }
120 let mut result = 1u64;
121 let mut k = n;
122 while k > 1 {
123 result *= k as u64;
124 k -= 2;
125 }
126 result
127}
128
129fn odd_double_factorial(n: i32) -> f64 {
132 if n <= 0 {
133 return 1.0;
134 }
135 let mut acc = 1.0;
136 let mut k = n;
137 while k > 0 {
138 acc *= k as f64;
139 k -= 2;
140 }
141 acc
142}
143
144#[derive(Debug, Clone)]
146pub struct BasisSet {
147 pub functions: Vec<BasisFunction>,
149 pub shells: Vec<ContractedShell>,
151 pub n_basis: usize,
153 pub function_to_atom: Vec<usize>,
155}
156
157impl BasisSet {
158 pub fn sto3g(elements: &[u8], positions_bohr: &[[f64; 3]]) -> Self {
160 let mut functions = Vec::new();
161 let mut shells = Vec::new();
162 let mut function_to_atom = Vec::new();
163
164 for (atom_idx, (&z, ¢er)) in elements.iter().zip(positions_bohr.iter()).enumerate() {
165 let atom_shells = get_sto3g_shells(z, atom_idx, center);
166 for shell in &atom_shells {
167 match shell.l {
168 0 => {
169 functions.push(BasisFunction {
170 atom_index: atom_idx,
171 center,
172 angular: [0, 0, 0],
173 l_total: 0,
174 primitives: shell.primitives.clone(),
175 });
176 function_to_atom.push(atom_idx);
177 }
178 1 => {
179 for (lx, ly, lz) in [(1, 0, 0), (0, 1, 0), (0, 0, 1)] {
180 functions.push(BasisFunction {
181 atom_index: atom_idx,
182 center,
183 angular: [lx, ly, lz],
184 l_total: 1,
185 primitives: shell.primitives.clone(),
186 });
187 function_to_atom.push(atom_idx);
188 }
189 }
190 2 => {
191 for (lx, ly, lz) in [
192 (2, 0, 0),
193 (1, 1, 0),
194 (1, 0, 1),
195 (0, 2, 0),
196 (0, 1, 1),
197 (0, 0, 2),
198 ] {
199 functions.push(BasisFunction {
200 atom_index: atom_idx,
201 center,
202 angular: [lx, ly, lz],
203 l_total: 2,
204 primitives: shell.primitives.clone(),
205 });
206 function_to_atom.push(atom_idx);
207 }
208 }
209 _ => {}
210 }
211 }
212 shells.extend(atom_shells);
213 }
214
215 let n_basis = functions.len();
216 BasisSet {
217 functions,
218 shells,
219 n_basis,
220 function_to_atom,
221 }
222 }
223}
224
225fn get_sto3g_shells(z: u8, atom_index: usize, center: [f64; 3]) -> Vec<ContractedShell> {
229 match z {
230 1 => vec![ContractedShell {
232 atom_index,
233 center,
234 l: 0,
235 primitives: vec![
236 GaussianPrimitive {
237 alpha: 3.42525091,
238 coefficient: 0.15432897,
239 },
240 GaussianPrimitive {
241 alpha: 0.62353014,
242 coefficient: 0.53532814,
243 },
244 GaussianPrimitive {
245 alpha: 0.16885540,
246 coefficient: 0.44463454,
247 },
248 ],
249 }],
250
251 2 => vec![ContractedShell {
253 atom_index,
254 center,
255 l: 0,
256 primitives: vec![
257 GaussianPrimitive {
258 alpha: 6.36242139,
259 coefficient: 0.15432897,
260 },
261 GaussianPrimitive {
262 alpha: 1.15892300,
263 coefficient: 0.53532814,
264 },
265 GaussianPrimitive {
266 alpha: 0.31364979,
267 coefficient: 0.44463454,
268 },
269 ],
270 }],
271
272 6 => vec![
274 ContractedShell {
275 atom_index,
276 center,
277 l: 0,
278 primitives: vec![
279 GaussianPrimitive {
280 alpha: 71.6168370,
281 coefficient: 0.15432897,
282 },
283 GaussianPrimitive {
284 alpha: 13.0450960,
285 coefficient: 0.53532814,
286 },
287 GaussianPrimitive {
288 alpha: 3.53051220,
289 coefficient: 0.44463454,
290 },
291 ],
292 },
293 ContractedShell {
294 atom_index,
295 center,
296 l: 0,
297 primitives: vec![
298 GaussianPrimitive {
299 alpha: 2.94124940,
300 coefficient: -0.09996723,
301 },
302 GaussianPrimitive {
303 alpha: 0.68348310,
304 coefficient: 0.39951283,
305 },
306 GaussianPrimitive {
307 alpha: 0.22228990,
308 coefficient: 0.70011547,
309 },
310 ],
311 },
312 ContractedShell {
313 atom_index,
314 center,
315 l: 1,
316 primitives: vec![
317 GaussianPrimitive {
318 alpha: 2.94124940,
319 coefficient: 0.15591627,
320 },
321 GaussianPrimitive {
322 alpha: 0.68348310,
323 coefficient: 0.60768372,
324 },
325 GaussianPrimitive {
326 alpha: 0.22228990,
327 coefficient: 0.39195739,
328 },
329 ],
330 },
331 ],
332
333 7 => vec![
335 ContractedShell {
336 atom_index,
337 center,
338 l: 0,
339 primitives: vec![
340 GaussianPrimitive {
341 alpha: 99.1061690,
342 coefficient: 0.15432897,
343 },
344 GaussianPrimitive {
345 alpha: 18.0523120,
346 coefficient: 0.53532814,
347 },
348 GaussianPrimitive {
349 alpha: 4.88566020,
350 coefficient: 0.44463454,
351 },
352 ],
353 },
354 ContractedShell {
355 atom_index,
356 center,
357 l: 0,
358 primitives: vec![
359 GaussianPrimitive {
360 alpha: 3.78045590,
361 coefficient: -0.09996723,
362 },
363 GaussianPrimitive {
364 alpha: 0.87849660,
365 coefficient: 0.39951283,
366 },
367 GaussianPrimitive {
368 alpha: 0.28571440,
369 coefficient: 0.70011547,
370 },
371 ],
372 },
373 ContractedShell {
374 atom_index,
375 center,
376 l: 1,
377 primitives: vec![
378 GaussianPrimitive {
379 alpha: 3.78045590,
380 coefficient: 0.15591627,
381 },
382 GaussianPrimitive {
383 alpha: 0.87849660,
384 coefficient: 0.60768372,
385 },
386 GaussianPrimitive {
387 alpha: 0.28571440,
388 coefficient: 0.39195739,
389 },
390 ],
391 },
392 ],
393
394 8 => vec![
396 ContractedShell {
397 atom_index,
398 center,
399 l: 0,
400 primitives: vec![
401 GaussianPrimitive {
402 alpha: 130.709320,
403 coefficient: 0.15432897,
404 },
405 GaussianPrimitive {
406 alpha: 23.8088610,
407 coefficient: 0.53532814,
408 },
409 GaussianPrimitive {
410 alpha: 6.44360830,
411 coefficient: 0.44463454,
412 },
413 ],
414 },
415 ContractedShell {
416 atom_index,
417 center,
418 l: 0,
419 primitives: vec![
420 GaussianPrimitive {
421 alpha: 5.03315130,
422 coefficient: -0.09996723,
423 },
424 GaussianPrimitive {
425 alpha: 1.16959610,
426 coefficient: 0.39951283,
427 },
428 GaussianPrimitive {
429 alpha: 0.38038900,
430 coefficient: 0.70011547,
431 },
432 ],
433 },
434 ContractedShell {
435 atom_index,
436 center,
437 l: 1,
438 primitives: vec![
439 GaussianPrimitive {
440 alpha: 5.03315130,
441 coefficient: 0.15591627,
442 },
443 GaussianPrimitive {
444 alpha: 1.16959610,
445 coefficient: 0.60768372,
446 },
447 GaussianPrimitive {
448 alpha: 0.38038900,
449 coefficient: 0.39195739,
450 },
451 ],
452 },
453 ],
454
455 9 => vec![
457 ContractedShell {
458 atom_index,
459 center,
460 l: 0,
461 primitives: vec![
462 GaussianPrimitive {
463 alpha: 166.679130,
464 coefficient: 0.15432897,
465 },
466 GaussianPrimitive {
467 alpha: 30.3608120,
468 coefficient: 0.53532814,
469 },
470 GaussianPrimitive {
471 alpha: 8.21682070,
472 coefficient: 0.44463454,
473 },
474 ],
475 },
476 ContractedShell {
477 atom_index,
478 center,
479 l: 0,
480 primitives: vec![
481 GaussianPrimitive {
482 alpha: 6.46480320,
483 coefficient: -0.09996723,
484 },
485 GaussianPrimitive {
486 alpha: 1.50228120,
487 coefficient: 0.39951283,
488 },
489 GaussianPrimitive {
490 alpha: 0.48858850,
491 coefficient: 0.70011547,
492 },
493 ],
494 },
495 ContractedShell {
496 atom_index,
497 center,
498 l: 1,
499 primitives: vec![
500 GaussianPrimitive {
501 alpha: 6.46480320,
502 coefficient: 0.15591627,
503 },
504 GaussianPrimitive {
505 alpha: 1.50228120,
506 coefficient: 0.60768372,
507 },
508 GaussianPrimitive {
509 alpha: 0.48858850,
510 coefficient: 0.39195739,
511 },
512 ],
513 },
514 ],
515
516 15 => vec![
518 ContractedShell {
519 atom_index,
520 center,
521 l: 0,
522 primitives: vec![
523 GaussianPrimitive {
524 alpha: 508.291310,
525 coefficient: 0.15432897,
526 },
527 GaussianPrimitive {
528 alpha: 92.5891370,
529 coefficient: 0.53532814,
530 },
531 GaussianPrimitive {
532 alpha: 25.0571730,
533 coefficient: 0.44463454,
534 },
535 ],
536 },
537 ContractedShell {
538 atom_index,
539 center,
540 l: 0,
541 primitives: vec![
542 GaussianPrimitive {
543 alpha: 18.5172490,
544 coefficient: -0.09996723,
545 },
546 GaussianPrimitive {
547 alpha: 4.30422160,
548 coefficient: 0.39951283,
549 },
550 GaussianPrimitive {
551 alpha: 1.39999670,
552 coefficient: 0.70011547,
553 },
554 ],
555 },
556 ContractedShell {
557 atom_index,
558 center,
559 l: 1,
560 primitives: vec![
561 GaussianPrimitive {
562 alpha: 18.5172490,
563 coefficient: 0.15591627,
564 },
565 GaussianPrimitive {
566 alpha: 4.30422160,
567 coefficient: 0.60768372,
568 },
569 GaussianPrimitive {
570 alpha: 1.39999670,
571 coefficient: 0.39195739,
572 },
573 ],
574 },
575 ContractedShell {
576 atom_index,
577 center,
578 l: 0,
579 primitives: vec![
580 GaussianPrimitive {
581 alpha: 1.56367880,
582 coefficient: -0.09996723,
583 },
584 GaussianPrimitive {
585 alpha: 0.36368650,
586 coefficient: 0.39951283,
587 },
588 GaussianPrimitive {
589 alpha: 0.11828520,
590 coefficient: 0.70011547,
591 },
592 ],
593 },
594 ContractedShell {
595 atom_index,
596 center,
597 l: 1,
598 primitives: vec![
599 GaussianPrimitive {
600 alpha: 1.56367880,
601 coefficient: 0.15591627,
602 },
603 GaussianPrimitive {
604 alpha: 0.36368650,
605 coefficient: 0.60768372,
606 },
607 GaussianPrimitive {
608 alpha: 0.11828520,
609 coefficient: 0.39195739,
610 },
611 ],
612 },
613 ],
614
615 16 => vec![
617 ContractedShell {
618 atom_index,
619 center,
620 l: 0,
621 primitives: vec![
622 GaussianPrimitive {
623 alpha: 598.642680,
624 coefficient: 0.15432897,
625 },
626 GaussianPrimitive {
627 alpha: 109.046680,
628 coefficient: 0.53532814,
629 },
630 GaussianPrimitive {
631 alpha: 29.5121090,
632 coefficient: 0.44463454,
633 },
634 ],
635 },
636 ContractedShell {
637 atom_index,
638 center,
639 l: 0,
640 primitives: vec![
641 GaussianPrimitive {
642 alpha: 22.1564680,
643 coefficient: -0.09996723,
644 },
645 GaussianPrimitive {
646 alpha: 5.14855900,
647 coefficient: 0.39951283,
648 },
649 GaussianPrimitive {
650 alpha: 1.67441430,
651 coefficient: 0.70011547,
652 },
653 ],
654 },
655 ContractedShell {
656 atom_index,
657 center,
658 l: 1,
659 primitives: vec![
660 GaussianPrimitive {
661 alpha: 22.1564680,
662 coefficient: 0.15591627,
663 },
664 GaussianPrimitive {
665 alpha: 5.14855900,
666 coefficient: 0.60768372,
667 },
668 GaussianPrimitive {
669 alpha: 1.67441430,
670 coefficient: 0.39195739,
671 },
672 ],
673 },
674 ContractedShell {
675 atom_index,
676 center,
677 l: 0,
678 primitives: vec![
679 GaussianPrimitive {
680 alpha: 1.80579080,
681 coefficient: -0.09996723,
682 },
683 GaussianPrimitive {
684 alpha: 0.41988400,
685 coefficient: 0.39951283,
686 },
687 GaussianPrimitive {
688 alpha: 0.13655330,
689 coefficient: 0.70011547,
690 },
691 ],
692 },
693 ContractedShell {
694 atom_index,
695 center,
696 l: 1,
697 primitives: vec![
698 GaussianPrimitive {
699 alpha: 1.80579080,
700 coefficient: 0.15591627,
701 },
702 GaussianPrimitive {
703 alpha: 0.41988400,
704 coefficient: 0.60768372,
705 },
706 GaussianPrimitive {
707 alpha: 0.13655330,
708 coefficient: 0.39195739,
709 },
710 ],
711 },
712 ],
713
714 17 => vec![
716 ContractedShell {
717 atom_index,
718 center,
719 l: 0,
720 primitives: vec![
721 GaussianPrimitive {
722 alpha: 696.408520,
723 coefficient: 0.15432897,
724 },
725 GaussianPrimitive {
726 alpha: 126.888800,
727 coefficient: 0.53532814,
728 },
729 GaussianPrimitive {
730 alpha: 34.3399080,
731 coefficient: 0.44463454,
732 },
733 ],
734 },
735 ContractedShell {
736 atom_index,
737 center,
738 l: 0,
739 primitives: vec![
740 GaussianPrimitive {
741 alpha: 25.9670530,
742 coefficient: -0.09996723,
743 },
744 GaussianPrimitive {
745 alpha: 6.03406090,
746 coefficient: 0.39951283,
747 },
748 GaussianPrimitive {
749 alpha: 1.96235810,
750 coefficient: 0.70011547,
751 },
752 ],
753 },
754 ContractedShell {
755 atom_index,
756 center,
757 l: 1,
758 primitives: vec![
759 GaussianPrimitive {
760 alpha: 25.9670530,
761 coefficient: 0.15591627,
762 },
763 GaussianPrimitive {
764 alpha: 6.03406090,
765 coefficient: 0.60768372,
766 },
767 GaussianPrimitive {
768 alpha: 1.96235810,
769 coefficient: 0.39195739,
770 },
771 ],
772 },
773 ContractedShell {
774 atom_index,
775 center,
776 l: 0,
777 primitives: vec![
778 GaussianPrimitive {
779 alpha: 2.14407210,
780 coefficient: -0.09996723,
781 },
782 GaussianPrimitive {
783 alpha: 0.49841410,
784 coefficient: 0.39951283,
785 },
786 GaussianPrimitive {
787 alpha: 0.16208590,
788 coefficient: 0.70011547,
789 },
790 ],
791 },
792 ContractedShell {
793 atom_index,
794 center,
795 l: 1,
796 primitives: vec![
797 GaussianPrimitive {
798 alpha: 2.14407210,
799 coefficient: 0.15591627,
800 },
801 GaussianPrimitive {
802 alpha: 0.49841410,
803 coefficient: 0.60768372,
804 },
805 GaussianPrimitive {
806 alpha: 0.16208590,
807 coefficient: 0.39195739,
808 },
809 ],
810 },
811 ],
812
813 _ => {
815 let zeta = 0.5 * (z as f64).sqrt();
816 vec![ContractedShell {
817 atom_index,
818 center,
819 l: 0,
820 primitives: vec![
821 GaussianPrimitive {
822 alpha: 3.42525091 * zeta * zeta,
823 coefficient: 0.15432897,
824 },
825 GaussianPrimitive {
826 alpha: 0.62353014 * zeta * zeta,
827 coefficient: 0.53532814,
828 },
829 GaussianPrimitive {
830 alpha: 0.16885540 * zeta * zeta,
831 coefficient: 0.44463454,
832 },
833 ],
834 }]
835 }
836 }
837}
838
839pub fn valence_electrons(z: u8) -> usize {
841 match z {
842 1 => 1,
843 2 => 2,
844 3 => 1,
845 4 => 2,
846 5 => 3,
847 6 => 4,
848 7 => 5,
849 8 => 6,
850 9 => 7,
851 10 => 8,
852 14 => 4,
853 15 => 5,
854 16 => 6,
855 17 => 7,
856 35 => 7,
857 53 => 7,
858 _ => z as usize,
859 }
860}
861
862#[cfg(test)]
863mod tests {
864 use super::*;
865
866 #[test]
867 fn test_hydrogen_basis_size() {
868 let basis = BasisSet::sto3g(&[1], &[[0.0, 0.0, 0.0]]);
869 assert_eq!(basis.n_basis, 1);
870 assert_eq!(basis.functions[0].l_total, 0);
871 }
872
873 #[test]
874 fn test_carbon_basis_size() {
875 let basis = BasisSet::sto3g(&[6], &[[0.0, 0.0, 0.0]]);
876 assert_eq!(basis.n_basis, 5); }
878
879 #[test]
880 fn test_water_basis_size() {
881 let basis = BasisSet::sto3g(
882 &[8, 1, 1],
883 &[[0.0, 0.0, 0.0], [1.43, 1.11, 0.0], [-1.43, 1.11, 0.0]],
884 );
885 assert_eq!(basis.n_basis, 7); }
887
888 #[test]
889 fn test_double_factorial() {
890 assert_eq!(double_factorial(0), 1);
891 assert_eq!(double_factorial(1), 1);
892 assert_eq!(double_factorial(5), 15);
893 assert_eq!(double_factorial(6), 48);
894 }
895
896 #[test]
897 fn test_s_function_evaluation() {
898 let basis = BasisSet::sto3g(&[1], &[[0.0, 0.0, 0.0]]);
899 let val = basis.functions[0].evaluate(0.0, 0.0, 0.0);
900 assert!(val > 0.0);
901 }
902}