1use std::f64::consts::PI;
25
26#[inline]
28pub fn gaussian_product_center(alpha: f64, a: f64, beta: f64, b: f64) -> f64 {
29 (alpha * a + beta * b) / (alpha + beta)
30}
31
32#[inline]
34pub fn distance_squared(a: &[f64; 3], b: &[f64; 3]) -> f64 {
35 let dx = a[0] - b[0];
36 let dy = a[1] - b[1];
37 let dz = a[2] - b[2];
38 dx * dx + dy * dy + dz * dz
39}
40
41pub fn overlap_ss(alpha: f64, center_a: &[f64; 3], beta: f64, center_b: &[f64; 3]) -> f64 {
45 let p = alpha + beta;
46 let mu = alpha * beta / p;
47 let ab2 = distance_squared(center_a, center_b);
48 (PI / p).powf(1.5) * (-mu * ab2).exp()
49}
50
51pub fn overlap_1d(la: u32, lb: u32, pa_x: f64, pb_x: f64, p: f64, prefactor: f64) -> f64 {
55 let max_a = (la + 1) as usize;
56 let max_b = (lb + 1) as usize;
57 let mut table = vec![vec![0.0f64; max_b]; max_a];
58
59 table[0][0] = prefactor;
60
61 if max_a > 1 {
62 table[1][0] = pa_x * table[0][0];
63 }
64 for a in 2..max_a {
65 table[a][0] = pa_x * table[a - 1][0] + (a - 1) as f64 / (2.0 * p) * table[a - 2][0];
66 }
67
68 for b in 1..max_b {
69 for a in 0..max_a {
70 table[a][b] = pb_x * table[a][b - 1];
71 if b > 1 {
72 table[a][b] += (b - 1) as f64 / (2.0 * p) * table[a][b - 2];
73 }
74 if a > 0 {
75 table[a][b] += a as f64 / (2.0 * p) * table[a - 1][b - 1];
76 }
77 }
78 }
79
80 table[la as usize][lb as usize]
81}
82
83pub fn overlap_primitive(
85 alpha: f64,
86 center_a: &[f64; 3],
87 la: [u32; 3],
88 beta: f64,
89 center_b: &[f64; 3],
90 lb: [u32; 3],
91) -> f64 {
92 let p = alpha + beta;
93 let mu = alpha * beta / p;
94 let ab2 = distance_squared(center_a, center_b);
95
96 let prefactor_3d = (PI / p).powf(1.5) * (-mu * ab2).exp();
97
98 let px = gaussian_product_center(alpha, center_a[0], beta, center_b[0]);
99 let py = gaussian_product_center(alpha, center_a[1], beta, center_b[1]);
100 let pz = gaussian_product_center(alpha, center_a[2], beta, center_b[2]);
101
102 let pa = [px - center_a[0], py - center_a[1], pz - center_a[2]];
103 let pb = [px - center_b[0], py - center_b[1], pz - center_b[2]];
104
105 let prefactor_1d = prefactor_3d.cbrt();
106
107 let sx = overlap_1d(la[0], lb[0], pa[0], pb[0], p, prefactor_1d);
108 let sy = overlap_1d(la[1], lb[1], pa[1], pb[1], p, prefactor_1d);
109 let sz = overlap_1d(la[2], lb[2], pa[2], pb[2], p, prefactor_1d);
110
111 sx * sy * sz
112}
113
114pub fn overlap_1d_direct(la: u32, lb: u32, alpha: f64, ax: f64, beta: f64, bx: f64) -> f64 {
116 let p = alpha + beta;
117 let mu = alpha * beta / p;
118 let ab = ax - bx;
119 let px = gaussian_product_center(alpha, ax, beta, bx);
120 let pa = px - ax;
121 let pb = px - bx;
122
123 let prefactor = (PI / p).sqrt() * (-mu * ab * ab).exp();
124 overlap_1d(la, lb, pa, pb, p, prefactor)
125}
126
127pub fn boys_function(n: u32, x: f64) -> f64 {
131 if x < 1.0e-10 {
132 return 1.0 / (2.0 * n as f64 + 1.0);
133 }
134
135 if x > 30.0 + n as f64 {
136 let df = if n == 0 {
138 1.0
139 } else {
140 double_factorial_f64(2 * n - 1)
141 };
142 return df * PI.sqrt() / (2.0f64.powi(n as i32 + 1) * x.powi(n as i32) * x.sqrt());
143 }
144
145 let mut sum = 0.0;
146 let mut term = 1.0 / (2.0 * n as f64 + 1.0);
147 sum += term;
148
149 for k in 1..100 {
150 term *= 2.0 * x / (2.0 * (n + k) as f64 + 1.0);
151 sum += term;
152 if term.abs() < 1.0e-15 * sum.abs() {
153 break;
154 }
155 }
156
157 (-x).exp() * sum
158}
159
160fn double_factorial_f64(n: u32) -> f64 {
162 if n <= 1 {
163 return 1.0;
164 }
165 let mut result = 1.0;
166 let mut k = n;
167 while k > 1 {
168 result *= k as f64;
169 k -= 2;
170 }
171 result
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_overlap_ss_same_center() {
180 let s = overlap_ss(1.0, &[0.0, 0.0, 0.0], 1.0, &[0.0, 0.0, 0.0]);
181 let expected = (PI / 2.0).powf(1.5);
182 assert!((s - expected).abs() < 1e-10);
183 }
184
185 #[test]
186 fn test_overlap_ss_decays_with_distance() {
187 let a = [0.0, 0.0, 0.0];
188 let s1 = overlap_ss(1.0, &a, 1.0, &[0.0, 0.0, 0.0]);
189 let s2 = overlap_ss(1.0, &a, 1.0, &[1.0, 0.0, 0.0]);
190 let s3 = overlap_ss(1.0, &a, 1.0, &[2.0, 0.0, 0.0]);
191 assert!(s1 > s2);
192 assert!(s2 > s3);
193 }
194
195 #[test]
196 fn test_overlap_primitive_ss() {
197 let s = overlap_primitive(
198 1.0,
199 &[0.0, 0.0, 0.0],
200 [0, 0, 0],
201 1.0,
202 &[0.0, 0.0, 0.0],
203 [0, 0, 0],
204 );
205 let expected = (PI / 2.0).powf(1.5);
206 assert!((s - expected).abs() < 1e-10);
207 }
208
209 #[test]
210 fn test_boys_function_zero() {
211 let f = boys_function(0, 0.0);
212 assert!((f - 1.0).abs() < 1e-10);
213 }
214
215 #[test]
216 fn test_boys_function_large_x() {
217 let x = 50.0;
218 let f = boys_function(0, x);
219 let expected = 0.5 * (PI / x).sqrt();
220 assert!((f - expected).abs() / expected < 0.01);
221 }
222
223 #[test]
224 fn test_gaussian_product_center() {
225 let c = gaussian_product_center(1.0, 0.0, 1.0, 2.0);
226 assert!((c - 1.0).abs() < 1e-10);
227 }
228}