1use nalgebra::DMatrix;
7use std::f64::consts::PI;
8
9use super::basis::AtomicOrbital;
10
11pub fn build_overlap_matrix(basis: &[AtomicOrbital]) -> DMatrix<f64> {
13 let n = basis.len();
14 let mut s = DMatrix::zeros(n, n);
15
16 for i in 0..n {
17 s[(i, i)] = 1.0; for j in (i + 1)..n {
19 let sij = overlap_integral(&basis[i], &basis[j]);
20 s[(i, j)] = sij;
21 s[(j, i)] = sij;
22 }
23 }
24
25 s
26}
27
28fn overlap_integral(a: &AtomicOrbital, b: &AtomicOrbital) -> f64 {
31 let dx = a.center[0] - b.center[0];
32 let dy = a.center[1] - b.center[1];
33 let dz = a.center[2] - b.center[2];
34 let r2_ab = dx * dx + dy * dy + dz * dz;
35
36 let mut s = 0.0;
37
38 for ga in &a.gaussians {
39 for gb in &b.gaussians {
40 let alpha = ga.alpha;
41 let beta = gb.alpha;
42 let gamma = alpha + beta;
43
44 let px = (alpha * a.center[0] + beta * b.center[0]) / gamma;
46 let py = (alpha * a.center[1] + beta * b.center[1]) / gamma;
47 let pz = (alpha * a.center[2] + beta * b.center[2]) / gamma;
48
49 let k_ab = (-alpha * beta / gamma * r2_ab).exp();
51
52 let contrib = match (a.l, a.m, b.l, b.m) {
53 (0, 0, 0, 0) => {
55 let norm_a = (2.0 * alpha / PI).powf(0.75);
56 let norm_b = (2.0 * beta / PI).powf(0.75);
57 let s_prim = (PI / gamma).powf(1.5) * k_ab;
58 norm_a * norm_b * s_prim
59 }
60 (0, 0, 1, m) | (1, m, 0, 0) => {
62 let (s_orb, p_orb, s_alpha, p_alpha) = if a.l == 0 {
63 (a, b, alpha, beta)
64 } else {
65 (b, a, beta, alpha)
66 };
67 let s_a = s_alpha;
68 let p_a = p_alpha;
69 let g = s_a + p_a;
70
71 let norm_s = (2.0 * s_a / PI).powf(0.75);
72 let norm_p = (128.0 * p_a.powi(5) / (PI * PI * PI)).powf(0.25);
73
74 let pc = [
76 (s_a * s_orb.center[0] + p_a * p_orb.center[0]) / g,
77 (s_a * s_orb.center[1] + p_a * p_orb.center[1]) / g,
78 (s_a * s_orb.center[2] + p_a * p_orb.center[2]) / g,
79 ];
80
81 let comp_idx = match m {
83 -1 => 0usize,
84 0 => 1,
85 1 => 2,
86 _ => 0,
87 };
88
89 let pb = pc[comp_idx] - p_orb.center[comp_idx];
91
92 let base = (PI / g).powf(1.5) * k_ab;
93 norm_s * norm_p * pb * base
94 }
95 (1, m_a, 1, m_b) => {
97 let norm_a = (128.0 * alpha.powi(5) / (PI * PI * PI)).powf(0.25);
98 let norm_b = (128.0 * beta.powi(5) / (PI * PI * PI)).powf(0.25);
99
100 let comp_a = match m_a {
101 -1 => 0usize,
102 0 => 1,
103 1 => 2,
104 _ => 0,
105 };
106 let comp_b = match m_b {
107 -1 => 0usize,
108 0 => 1,
109 1 => 2,
110 _ => 0,
111 };
112
113 let pa_i = px - a.center[comp_a];
115 let pb_j;
116 if comp_a == comp_b {
119 let shift_a = match comp_a {
120 0 => px - a.center[0],
121 1 => py - a.center[1],
122 2 => pz - a.center[2],
123 _ => 0.0,
124 };
125 let shift_b = match comp_b {
126 0 => px - b.center[0],
127 1 => py - b.center[1],
128 2 => pz - b.center[2],
129 _ => 0.0,
130 };
131 let base = (PI / gamma).powf(1.5) * k_ab;
132 let val = shift_a * shift_b + 0.5 / gamma;
133 norm_a * norm_b * val * base
134 } else {
135 pb_j = match comp_b {
136 0 => px - b.center[0],
137 1 => py - b.center[1],
138 2 => pz - b.center[2],
139 _ => 0.0,
140 };
141 let base = (PI / gamma).powf(1.5) * k_ab;
142 norm_a * norm_b * pa_i * pb_j * base
143 }
144 }
145 _ => 0.0,
146 };
147
148 s += ga.coeff * gb.coeff * contrib;
149 }
150 }
151
152 s
153}
154
155#[cfg(test)]
156mod tests {
157 use super::super::basis::build_basis;
158 use super::*;
159
160 #[test]
161 fn test_overlap_diagonal_is_one() {
162 let elements = [8u8, 1, 1];
163 let positions = [[0.0, 0.0, 0.0], [0.757, 0.586, 0.0], [-0.757, 0.586, 0.0]];
164 let basis = build_basis(&elements, &positions);
165 let s = build_overlap_matrix(&basis);
166 for i in 0..s.nrows() {
167 assert!(
168 (s[(i, i)] - 1.0).abs() < 1e-10,
169 "S[{},{}] = {}",
170 i,
171 i,
172 s[(i, i)]
173 );
174 }
175 }
176
177 #[test]
178 fn test_overlap_symmetry() {
179 let elements = [8u8, 1, 1];
180 let positions = [[0.0, 0.0, 0.0], [0.757, 0.586, 0.0], [-0.757, 0.586, 0.0]];
181 let basis = build_basis(&elements, &positions);
182 let s = build_overlap_matrix(&basis);
183 for i in 0..s.nrows() {
184 for j in 0..s.ncols() {
185 assert!(
186 (s[(i, j)] - s[(j, i)]).abs() < 1e-14,
187 "S not symmetric at ({},{})",
188 i,
189 j,
190 );
191 }
192 }
193 }
194
195 #[test]
196 fn test_overlap_off_diagonal_range() {
197 let elements = [8u8, 1, 1];
198 let positions = [[0.0, 0.0, 0.0], [0.757, 0.586, 0.0], [-0.757, 0.586, 0.0]];
199 let basis = build_basis(&elements, &positions);
200 let s = build_overlap_matrix(&basis);
201 for i in 0..s.nrows() {
203 for j in 0..s.ncols() {
204 if i != j {
205 assert!(
206 s[(i, j)].abs() <= 1.0 + 1e-10,
207 "S[{},{}] = {} exceeds 1.0",
208 i,
209 j,
210 s[(i, j)]
211 );
212 }
213 }
214 }
215 }
216
217 #[test]
218 fn test_overlap_h2_ss() {
219 let elements = [1u8, 1];
221 let positions = [[0.0, 0.0, 0.0], [0.74, 0.0, 0.0]];
222 let basis = build_basis(&elements, &positions);
223 let s = build_overlap_matrix(&basis);
224 assert_eq!(s.nrows(), 2);
225 let s12 = s[(0, 1)];
226 assert!(s12 > 0.3 && s12 < 0.9, "S_12 for H2 = {}", s12);
227 }
228}