qsym2/integrals/overlap.rs
1//! Atomic-orbital $`n`$-centre overlap evaluations.
2
3/// Implements the `overlap` method for [`crate::integrals::shell_tuple::ShellTuple`] and
4/// [`crate::integrals::shell_tuple::ShellTupleCollection`] of a specified pattern.
5macro_rules! impl_shell_tuple_overlap {
6 ( $RANK:ident, <$($shell_name:ident),+> ) => {
7 #[duplicate_item(
8 [
9 dtype [ f64 ]
10 zg_type [ &self.zg ]
11 dd_type [ &self.dd ]
12 rl2cart_type [
13 self.rl2carts[i]
14 .as_ref()
15 .unwrap_or_else(|| panic!("Transformation matrix to convert shell {i} to spherical order not found."))
16 ]
17 exp_kqs_func [
18 if self.ks.iter().any(|k| k.is_some()) {
19 panic!("Real-valued overlaps cannot handle plane-wave vectors.")
20 } else {
21 (
22 None::<Vec<Array<f64, Dim<[usize; $RANK]>>>>,
23 None::<Vec<Array<f64, Dim<[usize; $RANK]>>>>,
24 )
25 }
26 ]
27 exp_ks_kqs_to_int_func [
28 (0..3).for_each(|i| {
29 match (exp_ks_opt.as_ref(), exp_kqs_opt.as_ref()) {
30 (Some(_), _) | (_, Some(_)) => {
31 panic!("Real-valued overlaps cannot handle plane-wave vectors.")
32 }
33 (None, None) => {
34 ints_r[i][l_tuple][n_tuple] = Some(
35 &pre_zg * &exp_zgs[i]
36 );
37 }
38 }
39 })
40 ]
41 n_recur_k_term_kk [ _ ]
42 n_recur_k_term_func [
43 panic!("Real-valued overlaps cannot handle plane-wave vectors.")
44 ]
45 l_recur_k_term_kr [ _ ]
46 l_recur_k_term_func [
47 panic!("Real-valued overlaps cannot handle plane-wave vectors.")
48 ]
49 ]
50 [
51 dtype [ C128 ]
52 zg_type [ self.zg.mapv(C128::from) ]
53 dd_type [ &self.dd.mapv(C128::from) ]
54 rl2cart_type [
55 &self.rl2carts[i]
56 .as_ref()
57 .unwrap_or_else(|| panic!("Transformation matrix to convert shell {i} to spherical order not found."))
58 .mapv(C128::from)
59 ]
60 exp_kqs_func [
61 if self.ks.iter().any(|k| k.is_some()) {
62 // exp_ks = exp(-|k|^2 / 4 zg)
63 // exp_ks[i] is the contribution from the ith Cartesian component.
64 // zg is primitive-combination-specific.
65 let exp_ks = (0..3).map(|i| {
66 self.zg.mapv(|zg| {
67 (-self.k[i].abs().powi(2) / (4.0 * zg)).exp()
68 })
69 }).collect::<Vec<_>>();
70
71 // exp_kqs = exp(ii * sum(j) k_j · q_j)
72 // exp_kqs[i] is the contribution from the ith Cartesian component.
73 // q_j is primitive-combination-specific.
74 let exp_kqs = (0..3).map(|i| {
75 (0..$RANK).filter_map(|j| {
76 match (self.ks[j], self.qs[j].as_ref()) {
77 (Some(kj), Some(qj)) => {
78 Some(
79 qj.mapv(|qjj| kj[i] * qjj[i])
80 )
81 }
82 _ => None
83 }
84 })
85 .fold(
86 Array::<C128, Dim<[usize; $RANK]>>::zeros(self.zg.raw_dim()),
87 |acc, arr| acc + arr
88 )
89 .mapv(|x| (x * C128::i()).exp())
90 })
91 .collect::<Vec<_>>();
92 (Some(exp_ks), Some(exp_kqs))
93 } else {
94 (None, None)
95 }
96 ]
97 exp_ks_kqs_to_int_func [
98 (0..3).for_each(|i| {
99 match (exp_ks_opt.as_ref(), exp_kqs_opt.as_ref()) {
100 // Element-wise multiplication. Each element is for a specific
101 // primitive combination.
102 (Some(exp_ks), Some(exp_kqs)) => {
103 ints_r[i][l_tuple][n_tuple] = Some(
104 (&pre_zg * &exp_zgs[i] * &exp_ks[i]).mapv(C128::from)
105 * &exp_kqs[i]
106 );
107 }
108 _ => {
109 ints_r[i][l_tuple][n_tuple] = Some(
110 (&pre_zg * &exp_zgs[i]).mapv(C128::from)
111 );
112 }
113 }
114 })
115 ]
116 n_recur_k_term_kk [ kk ]
117 n_recur_k_term_func [
118 // 1 / (2 * zg) * sum(i) ii * k_iα * [[:|:]]
119 // zg is primitive-combination-specific.
120 (0..3).for_each(|i| {
121 let add_term = self.zg.mapv(|zg| {
122 C128::i() * kk[i] / (2.0 * zg)
123 }) * ints_r[i][l_tuple][n_tuple].as_ref().unwrap_or_else(|| {
124 panic!("({l_tuple:?}, {n_tuple:?}) => ({l_tuple:?}, {next_n_tuple:?}) failed.")
125 });
126 if let Some(arr) = ints_r[i][l_tuple][next_n_tuple].as_mut() {
127 Zip::from(arr).and(&add_term).for_each(|a, &t| *a += t);
128 } else {
129 ints_r[i][l_tuple][next_n_tuple] = Some(add_term);
130 }
131 });
132 ]
133 l_recur_k_term_kr [ kr ]
134 l_recur_k_term_func [
135 // -ii * k_gα * [[:|:]]
136 (0..3).for_each(|i| {
137 let add_term = C128::i()
138 * kr[i]
139 * ints_r[i][l_tuple][n_tuple].as_ref().unwrap_or_else(|| {
140 panic!("({l_tuple:?}, {n_tuple:?}) => ({next_l_tuple:?}, {n_tuple:?}) failed.")
141 });
142 if let Some(arr) = ints_r[i][next_l_tuple][n_tuple].as_mut() {
143 Zip::from(arr).and(&add_term).for_each(|a, &t| *a -= t);
144 } else {
145 ints_r[i][next_l_tuple][n_tuple] = Some(-add_term);
146 }
147 });
148 ]
149 ]
150 )]
151 impl<'a> ShellTuple<'a, $RANK, dtype> {
152 /// Calculates the overlap arrays for this shell tuple.
153 ///
154 /// # Arguments
155 ///
156 /// * `ls` - The derivative pattern.
157 ///
158 /// # Returns
159 ///
160 /// A vector of overlap arrays, each of which is for one derivative component.
161 pub fn overlap(
162 &self, ls: [usize; $RANK]
163 ) -> Vec<Array<dtype, Dim<[usize; $RANK]>>> {
164 // ~~~~~~~~~~~~~~~~~~~
165 // Preparation begins.
166 // ~~~~~~~~~~~~~~~~~~~
167
168 // We require extra Cartesian degrees to calculate derivatives, because each
169 // derivative order increases a corresponding Cartesian rank by one.
170 let ns: [usize; $RANK] = if ls.iter().any(|l| *l > 0) {
171 let mut ns = self.ns.clone();
172 ns.iter_mut().for_each(|n| *n += 1);
173 ns
174 } else {
175 self.ns.clone()
176 };
177
178 // Generate all terms in recurrence series
179 // First index: Cartesian component
180 // Next stc.rank indices: l-recursion indices
181 // Next stc.rank indices: n-recursion indices
182 // Last stc.rank indices: primitive indices
183 // E.g.: rank 3,
184 // ints_r[1][(0, 0, 1)][(0, 1, 2)][(3, 8, 7)]: y-component integral value with
185 // 0th y-derivative of 0th Cartesian y-power of 3rd primitive on first shell,
186 // 0th y-derivative of 1st Cartesian y-power of 8th primitive on second shell, and
187 // 1st y-derivative of 2nd Cartesian y-power of 7th primitive on third shell
188 let lrecursion_shape = {
189 let mut ls_mut = ls.clone();
190 ls_mut.iter_mut().for_each(|l| *l += 1);
191 ls_mut
192 };
193 let nrecursion_shape = {
194 let mut ns_mut = ns.clone();
195 ns_mut.iter_mut().for_each(|n| *n += 1);
196 ns_mut
197 };
198 let arr = Array::<_, Dim<[usize; $RANK]>>::from_elem(
199 lrecursion_shape, Array::<_, Dim<[usize; $RANK]>>::from_elem(
200 nrecursion_shape, None::<Array::<dtype, Dim<[usize; $RANK]>>>
201 )
202 );
203 let mut ints_r = [arr.clone(), arr.clone(), arr];
204
205 let default_tuple = [$(replace_expr!(($shell_name) 0)),+];
206 let l_tuples = ls
207 .iter()
208 .map(|l| 0..=*l)
209 .multi_cartesian_product()
210 .map(|ltuple| {
211 let mut ltuple_arr = default_tuple.clone();
212 ltuple_arr.iter_mut().enumerate().for_each(|(i, l)| *l = ltuple[i]);
213 ltuple_arr
214 })
215 .collect::<Vec<_>>();
216 let n_tuples = ns
217 .iter()
218 .map(|n| 0..=*n)
219 .multi_cartesian_product()
220 .map(|ntuple| {
221 let mut ntuple_arr = default_tuple.clone();
222 ntuple_arr.iter_mut().enumerate().for_each(|(i, n)| *n = ntuple[i]);
223 ntuple_arr
224 })
225 .collect::<Vec<_>>();
226 let n_tuples_noextra = ns
227 .iter()
228 .map(|n| 0..*n)
229 .multi_cartesian_product()
230 .map(|ntuple| {
231 let mut ntuple_arr = default_tuple.clone();
232 ntuple_arr.iter_mut().enumerate().for_each(|(i, n)| *n = ntuple[i]);
233 ntuple_arr
234 })
235 .collect::<Vec<_>>();
236
237 let all_tuples = l_tuples.iter().cloned().cartesian_product(
238 n_tuples.iter().cloned()
239 ).into_iter().collect::<IndexSet<_>>();
240 let mut remaining_tuples = all_tuples.clone();
241
242 let remaining_tuples_noextra = l_tuples.iter().cloned().cartesian_product(
243 n_tuples_noextra.iter().cloned()
244 ).into_iter().collect::<IndexSet<_>>();
245
246 let extra_tuples = all_tuples
247 .difference(&remaining_tuples_noextra)
248 .cloned()
249 .collect::<IndexSet<_>>();
250 // ~~~~~~~~~~~~~~~~~
251 // Preparation ends.
252 // ~~~~~~~~~~~~~~~~~
253
254 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
255 // Loop over all tuples begins.
256 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
257 for (tuple_index, (l_tuple, n_tuple)) in all_tuples.into_iter().enumerate() {
258 // ~~~~~~~~~~~~~~~~~~~~
259 // Initial term begins.
260 // ~~~~~~~~~~~~~~~~~~~~
261 if tuple_index == 0 {
262 debug_assert!(remaining_tuples.shift_remove(&(l_tuple, n_tuple)));
263
264 // pre_zg = sqrt(pi / zg)
265 // zg is primitive-combination-specific.
266 let pre_zg = self.zg.mapv(|zg| (std::f64::consts::PI / zg).sqrt());
267
268 // exp_zgs = sum(g < h) [ -(z_g * z_h) / zg * |r_g - r_h|^2 ]
269 // exp_zgs[i] is the contribution from the ith Cartesian component.
270 // z_g, z_h, and zg are primitive-combination-specific.
271 let exp_zgs = (0..3).map(|i| {
272 let mut exp_zg_i = self.zg.clone();
273 exp_zg_i.indexed_iter_mut().for_each(|(indices, zg)| {
274 let ($($shell_name),+) = indices;
275 let indices = [$($shell_name),+];
276 *zg = (
277 -1.0
278 / *zg
279 * (0..$RANK).flat_map(|g| ((g + 1)..$RANK).map(move |h| {
280 self.zs[g][indices[g]]
281 * self.zs[h][indices[h]]
282 * (self.rs[g][i] - self.rs[h][i]).powi(2)
283 })).sum::<f64>()
284 ).exp();
285 });
286 exp_zg_i
287 }).collect::<Vec<_>>();
288
289 // let (exp_ks_opt, exp_kqs_opt) = if self.ks.iter().any(|k| k.is_some()) {
290 // // exp_ks = exp(-|k|^2 / 4 zg)
291 // // exp_ks[i] is the contribution from the ith Cartesian component.
292 // // zg is primitive-combination-specific.
293 // let exp_ks = (0..3).map(|i| {
294 // self.zg.mapv(|zg| {
295 // (-self.k[i].abs().powi(2) / (4.0 * zg)).exp()
296 // })
297 // }).collect::<Vec<_>>();
298
299 // // exp_kqs = exp(ii * sum(j) k_j · q_j)
300 // // exp_kqs[i] is the contribution from the ith Cartesian component.
301 // // q_j is primitive-combination-specific.
302 // let exp_kqs = (0..3).map(|i| {
303 // (0..$RANK).filter_map(|j| {
304 // match (self.ks[j], self.qs[j].as_ref()) {
305 // (Some(kj), Some(qj)) => {
306 // Some(
307 // qj.mapv(|qjj| kj[i] * qjj[i])
308 // )
309 // }
310 // _ => None
311 // }
312 // })
313 // .fold(
314 // Array::<C128, Dim<[usize; $RANK]>>::zeros(self.zg.raw_dim()),
315 // |acc, arr| acc + arr
316 // )
317 // .mapv(|x| (x * C128::i()).exp())
318 // })
319 // .collect::<Vec<_>>();
320 // (Some(exp_ks), Some(exp_kqs))
321 // } else {
322 // (None, None)
323 // };
324 let (exp_ks_opt, exp_kqs_opt) = exp_kqs_func;
325
326 // (0..3).for_each(|i| {
327 // match (exp_ks_opt.as_ref(), exp_kqs_opt.as_ref()) {
328 // // Element-wise multiplication. Each element is for a specific
329 // // primitive combination.
330 // (Some(exp_ks), Some(exp_kqs)) => {
331 // ints_r[i][l_tuple][n_tuple] = Some(
332 // (&pre_zg * &exp_zgs[i] * &exp_ks[i]).mapv(C128::from)
333 // * &exp_kqs[i]
334 // );
335 // }
336 // _ => {
337 // ints_r[i][l_tuple][n_tuple] = Some(
338 // (&pre_zg * &exp_zgs[i]).mapv(C128::from)
339 // );
340 // }
341 // }
342 // });
343 exp_ks_kqs_to_int_func
344 }
345 // ~~~~~~~~~~~~~~~~~~
346 // Initial term ends.
347 // ~~~~~~~~~~~~~~~~~~
348
349 // ~~~~~~~~~~~~~~~~~~~~~~~~
350 // n-recurrent terms begin.
351 // ~~~~~~~~~~~~~~~~~~~~~~~~
352 for r_index in 0..$RANK {
353 // r_index: recursion index (j in handwritten note)
354 let next_n_tuple = {
355 let mut new_n_tuple = n_tuple.clone();
356 new_n_tuple.iter_mut().enumerate().for_each(|(t, n)| {
357 if t == r_index { *n += 1 }
358 });
359 new_n_tuple
360 };
361 if !remaining_tuples.shift_remove(&(l_tuple, next_n_tuple)) {
362 continue
363 }
364
365 (0..3).for_each(|i| {
366 // (rg - r_j) * [[:|:]]
367 // rg is primitive-combination-specific.
368 ints_r[i][l_tuple][next_n_tuple] = Some(
369 self.rg.map(|r| dtype::from(r[i] - self.rs[r_index][i]))
370 * ints_r[i][l_tuple][n_tuple].as_ref().unwrap_or_else(|| {
371 panic!("({l_tuple:?}, {n_tuple:?}) => ({l_tuple:?}, {next_n_tuple:?}) failed.")
372 })
373 );
374 });
375
376 (0..$RANK).for_each(|k| {
377 // if let Some(kk) = self.ks[k].as_ref() {
378 // // 1 / (2 * zg) * sum(i) ii * k_iα * [[:|:]]
379 // // zg is primitive-combination-specific.
380 // (0..3).for_each(|i| {
381 // let add_term = self.zg.mapv(|zg| {
382 // C128::i() * kk[i] / (2.0 * zg)
383 // }) * ints_r[i][l_tuple][n_tuple].as_ref().unwrap_or_else(|| {
384 // panic!("({l_tuple:?}, {n_tuple:?}) => ({l_tuple:?}, {next_n_tuple:?}) failed.")
385 // });
386 // if let Some(arr) = ints_r[i][l_tuple][next_n_tuple].as_mut() {
387 // Zip::from(arr).and(&add_term).for_each(|a, &t| *a += t);
388 // } else {
389 // ints_r[i][l_tuple][next_n_tuple] = Some(add_term);
390 // }
391 // });
392 // };
393 if let Some(n_recur_k_term_kk) = self.ks[k].as_ref() {
394 n_recur_k_term_func
395 }
396
397 if n_tuple[k] > 0 {
398 let mut prev_n_tuple_k = n_tuple.clone();
399 prev_n_tuple_k.iter_mut().enumerate().for_each(|(t, n)| {
400 if t == k { *n -= 1 }
401 });
402 debug_assert!(!remaining_tuples.contains(&(l_tuple, prev_n_tuple_k)));
403 // 1 / (2 * zg) * sum(i) Nα(n_i) * [[n_i - 1_α:|:]]
404 (0..3).for_each(|i| {
405 let add_term = self.zg.mapv(|zg| {
406 dtype::from(1.0)
407 / (2.0 * zg)
408 * n_tuple[k]
409 .to_f64()
410 .unwrap_or_else(|| panic!("Unable to convert `n_tuple[k]` = {} to `f64`.", n_tuple[k]))
411 }) * ints_r[i][l_tuple][prev_n_tuple_k].as_ref().unwrap_or_else(|| {
412 panic!("({l_tuple:?}, {prev_n_tuple_k:?}) => ({l_tuple:?}, {next_n_tuple:?}) failed.")
413 });
414 if let Some(arr) = ints_r[i][l_tuple][next_n_tuple].as_mut() {
415 Zip::from(arr).and(&add_term).for_each(|a, &t| *a += t);
416 } else {
417 ints_r[i][l_tuple][next_n_tuple] = Some(add_term);
418 }
419 });
420 };
421 });
422
423 if l_tuple[r_index] > 0 {
424 let mut prev_l_tuple = l_tuple.clone();
425 prev_l_tuple.iter_mut().enumerate().for_each(|(t, l)| {
426 if t == r_index { *l -= 1 }
427 });
428 debug_assert!(!remaining_tuples.contains(&(prev_l_tuple, n_tuple)));
429 // -Nα(l_j) * [[:l_j - 1_α|:]]
430 // Note that Nα(l_j) = (l_j)_α.
431 (0..3).for_each(|i| {
432 let add_term = dtype::from(l_tuple[r_index]
433 .to_f64()
434 .unwrap_or_else(|| panic!("Unable to convert `l_tuple[r_index]` = {} to `f64`.", l_tuple[r_index]))
435 )
436 * ints_r[i][prev_l_tuple][n_tuple]
437 .as_ref()
438 .unwrap_or_else(|| {
439 panic!("({prev_l_tuple:?}, {n_tuple:?}) => ({l_tuple:?}, {next_n_tuple:?}) failed.")
440 });
441 if let Some(arr) = ints_r[i][l_tuple][next_n_tuple].as_mut() {
442 Zip::from(arr).and(&add_term).for_each(|a, &t| *a -= t);
443 } else {
444 ints_r[i][l_tuple][next_n_tuple] = Some(-add_term);
445 }
446 });
447 }
448
449 (0..$RANK).for_each(|k| {
450 if l_tuple[k] > 0 {
451 let mut prev_l_tuple_k = l_tuple.clone();
452 prev_l_tuple_k.iter_mut().enumerate().for_each(|(t, l)| {
453 if t == k { *l -= 1 }
454 });
455 debug_assert!(!remaining_tuples.contains(&(prev_l_tuple_k, n_tuple)));
456 // (1 / zg) * sum(g) z_g * Nα(l_g) * [[:l_g - 1_α|:]]
457 (0..3).for_each(|i| {
458 let add_term = dtype::from(
459 l_tuple[k]
460 .to_f64()
461 .unwrap_or_else(|| panic!("Unable to convert `l_tuple[k]` = {} to `f64`.", l_tuple[k])))
462 / zg_type
463 * &self.zs[k] // broadcasting zs[k] to the shape of zg.
464 * ints_r[i][prev_l_tuple_k][n_tuple].as_ref().unwrap_or_else(|| {
465 panic!("({prev_l_tuple_k:?}, {n_tuple:?}) => ({l_tuple:?}, {next_n_tuple:?}) failed.")
466 });
467 if let Some(arr) = ints_r[i][l_tuple][next_n_tuple].as_mut() {
468 Zip::from(arr).and(&add_term).for_each(|a, &t| *a += t);
469 } else {
470 ints_r[i][l_tuple][next_n_tuple] = Some(add_term);
471 }
472 });
473 }
474 })
475 }
476 // ~~~~~~~~~~~~~~~~~~~~~~
477 // n-recurrent terms end.
478 // ~~~~~~~~~~~~~~~~~~~~~~
479
480 // ~~~~~~~~~~~~~~~~~~~~~~~~
481 // l-recurrent terms begin.
482 // ~~~~~~~~~~~~~~~~~~~~~~~~
483 if extra_tuples.contains(&(l_tuple, n_tuple)) {
484 continue
485 }
486 for r_index in 0..$RANK {
487 // r_index: recursion index (g in handwritten note)
488 let next_l_tuple = {
489 let mut new_l_tuple = l_tuple.clone();
490 new_l_tuple.iter_mut().enumerate().for_each(|(t, l)| {
491 if t == r_index { *l += 1 }
492 });
493 new_l_tuple
494 };
495 if !remaining_tuples.shift_remove(&(next_l_tuple, n_tuple)) {
496 continue
497 }
498
499 if let Some(l_recur_k_term_kr) = self.ks[r_index].as_ref() {
500 // // -ii * k_gα * [[:|:]]
501 // (0..3).for_each(|i| {
502 // let add_term = C128::i()
503 // * kr[i]
504 // * ints_r[i][l_tuple][n_tuple].as_ref().unwrap_or_else(|| {
505 // panic!("({l_tuple:?}, {n_tuple:?}) => ({next_l_tuple:?}, {n_tuple:?}) failed.")
506 // });
507 // if let Some(arr) = ints_r[i][next_l_tuple][n_tuple].as_mut() {
508 // Zip::from(arr).and(&add_term).for_each(|a, &t| *a -= t);
509 // } else {
510 // ints_r[i][next_l_tuple][n_tuple] = Some(-add_term);
511 // }
512 // });
513 l_recur_k_term_func
514 }
515
516 let next_n_tuple = {
517 let mut new_n_tuple = n_tuple.clone();
518 new_n_tuple.iter_mut().enumerate().for_each(|(t, n)| {
519 if t == r_index { *n += 1 }
520 });
521 new_n_tuple
522 };
523 debug_assert!(next_n_tuple.iter().enumerate().all(|(t, n)| *n <= ns[t]));
524 debug_assert!(!remaining_tuples.contains(&(l_tuple, next_n_tuple)));
525
526 // 2 * z_g * [[n_g + 1_α:|:]]
527 (0..3).for_each(|i| {
528 let add_term = dtype::from(2.0)
529 * ints_r[i][l_tuple][next_n_tuple].as_ref().unwrap_or_else(|| {
530 panic!("({l_tuple:?}, {next_n_tuple:?}) => ({next_l_tuple:?}, {n_tuple:?}) failed.")
531 })
532 * &self.zs[r_index]; // broadcasting zs[r_index] to the shape of
533 // ints_r[i][l_tuple][next_n_tuple].
534 if let Some(arr) = ints_r[i][next_l_tuple][n_tuple].as_mut() {
535 Zip::from(arr).and(&add_term).for_each(|a, &t| *a += t);
536 } else {
537 ints_r[i][next_l_tuple][n_tuple] = Some(add_term);
538 }
539 });
540
541 if n_tuple[r_index] > 0 {
542 let mut prev_n_tuple = n_tuple.clone();
543 prev_n_tuple.iter_mut().enumerate().for_each(|(t, n)| {
544 if t == r_index { *n -= 1 }
545 });
546 debug_assert!(!remaining_tuples.contains(&(l_tuple, prev_n_tuple)));
547
548 // -Nα(n_g) * [[n_g - 1_α:|:]]
549 (0..3).for_each(|i| {
550 let add_term = dtype::from(
551 n_tuple[r_index]
552 .to_f64()
553 .unwrap_or_else(|| panic!("Unable to convert `n_tuple[r_index]` = {} to `f64`.", n_tuple[r_index]))
554 )
555 * ints_r[i][l_tuple][prev_n_tuple].as_ref().unwrap_or_else(|| {
556 panic!("({l_tuple:?}, {prev_n_tuple:?}) => ({next_l_tuple:?}, {n_tuple:?}) failed.")
557 });
558 if let Some(arr) = ints_r[i][next_l_tuple][n_tuple].as_mut() {
559 Zip::from(arr).and(&add_term).for_each(|a, &t| *a -= t);
560 } else {
561 ints_r[i][next_l_tuple][n_tuple] = Some(-add_term);
562 }
563 });
564 }
565 }
566 // ~~~~~~~~~~~~~~~~~~~~~~
567 // l-recurrent terms end.
568 // ~~~~~~~~~~~~~~~~~~~~~~
569 }
570 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
571 // Loop over all tuples ends.
572 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
573
574 // ~~~~~~~~~~~~~~~~~~~~~
575 // Normalisation begins.
576 // ~~~~~~~~~~~~~~~~~~~~~
577 // Each element of `norm_arr` gives the scaling factor to be multiplied by an
578 // integral involving a specific combination of primitives (i.e. in the integral,
579 // each centre comprises a single Gaussian function) over one of the three
580 // Cartesian directions, such that each primitive is normalised and that the
581 // two-centre integrals〈g0|g0〉is precisely one.
582 // The elements of `norm_arr` are only dependent on the exponents, not the
583 // contraction coefficients, because `g0` is taken to have a coefficient of 1.
584 // Contraction coefficients are introduced later.
585 for n_tuple in n_tuples.iter() {
586 let rank_i32 = $RANK
587 .to_i32()
588 .expect("Unable to convert the tuple rank to `i32`.");
589 let norm_arr =
590 (2.0 / std::f64::consts::PI).sqrt().sqrt().powi(rank_i32)
591 * n_tuple.iter().map(|n| {
592 let doufac = if *n == 0 {
593 1
594 } else {
595 ((2 * n) - 1)
596 .checked_double_factorial()
597 .unwrap_or_else(|| panic!("Unable to obtain `{}!!`.", 2 * n - 1))
598 }
599 .to_f64()
600 .unwrap_or_else(|| panic!("Unable to convert `{}!!` to `f64`.", 2 * n - 1));
601 1.0 / doufac.sqrt()
602 }).product::<f64>()
603 * self.zd.map(|zd| zd.sqrt().sqrt());
604 let norm_arr = self
605 .zs
606 .iter()
607 .zip(n_tuple.iter())
608 .enumerate()
609 .fold(norm_arr, |acc, (j, (z, n))| {
610 let mut shape = [$(replace_expr!(($shell_name) 1)),+];
611 shape[j] = z.len();
612 let z_transformed = z.mapv(|z_val| {
613 if n.rem_euclid(2) == 0 {
614 (4.0 * z_val).powi(
615 (n.div_euclid(2))
616 .to_i32()
617 .expect("Unable to convert `n` to `i32`.")
618 )
619 } else {
620 (4.0 * z_val).powf(
621 n.to_f64().expect("Unable to convert `n` to `f64`.")
622 / 2.0
623 )
624 }
625 })
626 .into_shape_with_order(shape)
627 .expect("Unable to convert transformed `z` to {$RANK} dimensions.");
628 acc * z_transformed
629 });
630
631 for l_tuple in l_tuples.iter() {
632 (0..3).for_each(|i| {
633 if let Some(arr) = ints_r[i][*l_tuple][*n_tuple].as_mut() {
634 Zip::from(arr)
635 .and(&norm_arr)
636 .for_each(|a, &n| *a *= dtype::from(n));
637 }
638 });
639 }
640 }
641 // ~~~~~~~~~~~~~~~~~~~
642 // Normalisation ends.
643 // ~~~~~~~~~~~~~~~~~~~
644
645 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
646 // Population of Cartesian integrals for each derivative component begins.
647 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
648 let lex_cart_orders = (0..=*ls.iter().max().expect("Unable to determine the maximum derivative order."))
649 .map(|l| CartOrder::lex(u32::try_from(l).expect("Unable to convert a derivative order to `u32`.")))
650 .collect::<Vec<_>>();
651 let cart_shell_shape = {
652 let mut cart_shell_shape_iter = self
653 .ns
654 .iter()
655 .map(|n| ((n + 1) * (n + 2)).div_euclid(2));
656 $(
657 let $shell_name = cart_shell_shape_iter
658 .next()
659 .expect("cart_shell_shape out of range.");
660 )+
661 [$($shell_name),+]
662 };
663 let all_shells_contraction_str = (0..$RANK)
664 .map(|i| (i.to_u8().expect("Unable to convert a shell index to `u8`.") + 97) as char)
665 .collect::<String>();
666 let shell_blocks = ls
667 .iter()
668 .map(|l| 0..((l + 1) * (l + 2)).div_euclid(2))
669 .multi_cartesian_product()
670 .map(|l_indices| {
671 // ls = [m, n, p, ...]
672 // l_indices = [a, b, c, ...]
673 // - a-th component (lexicographic order) of the m-th derivative of the first shell,
674 // - b-th component (lexicographic order) of the n-th derivative of the second shell,
675 // - etc.
676 // The derivative components are arranged in lexicographic Cartersian order.
677 // If ls = [0, 1, 2], then the a particular l_indices could take the value
678 // [0, 2, 3] which represents
679 // - 0th derivative of the first shell
680 // - d/dz of the second shell (x, y, z)
681 // - d2/dyy of the third shell (xx, xy, xz, yy, yz, zz)
682 debug_assert_eq!(l_indices.len(), $RANK);
683 let mut l_indices_iter = l_indices.into_iter();
684 $(
685 let $shell_name = l_indices_iter
686 .next()
687 .expect("l index out of range.");
688 )+
689 let l_indices = [$($shell_name),+];
690
691 // l_powers translates l_indices into tuples of component derivative orders
692 // for each shell.
693 // For example, with ls = [0, 1, 2] and l_indices = [0, 2, 3],
694 // l_powers is given by [(0, 0, 0), (0, 0, 1), (0, 2, 0)].
695 let l_powers = {
696 let mut l_powers_mut = [$(
697 replace_expr!(($shell_name) (0, 0, 0))
698 ),+];
699 l_powers_mut.iter_mut().enumerate().for_each(|(shell_index, l_power)| {
700 *l_power = lex_cart_orders[ls[shell_index]].cart_tuples[l_indices[shell_index]].clone();
701 });
702 l_powers_mut
703 };
704
705 // l_tuples_xyz gives l_tuple for each Cartesian component.
706 // With l_powers [(0, 0, 0), (0, 0, 1), (0, 2, 0)],
707 // l_tuples_xyz is given by
708 // [(0, 0, 0), (0, 0, 2), (0, 1, 0)]
709 // ----x---- ----y---- ----z----
710 // which means: take the product of the (0, 0, 0) x-derivative,
711 // (0, 0, 2) y-derivative, and (0, 1, 0) z-derivative to give int_xyz.
712 // Essentially, l_tuples_xyz is transposed l_powers.
713 // l_tuples_xyz will be cloned inside the for loop below because it
714 // is consumed after every iteration.
715 let outer_l_tuples_xyz = {
716 let mut l_tuples_xyz_mut = [
717 [$(replace_expr!(($shell_name) 0usize)),+]; 3
718 ];
719 l_tuples_xyz_mut[0].iter_mut().enumerate().for_each(|(shell_index, l)| {
720 *l = usize::try_from(l_powers[shell_index].0)
721 .expect("Unable to convert `l` to `usize`.");
722 });
723 l_tuples_xyz_mut[1].iter_mut().enumerate().for_each(|(shell_index, l)| {
724 *l = usize::try_from(l_powers[shell_index].1)
725 .expect("Unable to convert `l` to `usize`.");
726 });
727 l_tuples_xyz_mut[2].iter_mut().enumerate().for_each(|(shell_index, l)| {
728 *l = usize::try_from(l_powers[shell_index].2)
729 .expect("Unable to convert `l` to `usize`.");
730 });
731 l_tuples_xyz_mut
732 };
733
734 let mut cart_shell_block = Array::<dtype, Dim<[usize; $RANK]>>::zeros(
735 cart_shell_shape
736 );
737 for cart_indices in cart_shell_shape.iter().map(|d| 0..*d).multi_cartesian_product() {
738 // cart_indices = [i, j, k, l, ...]
739 // - i-th Cartesian component (shell's specified order) of the first shell,
740 // - j-th Cartesian component (shell's specified order) of the second shell,
741 // - etc.
742 // If a shell has pure ordering, a lexicographic Cartesian order will
743 // be used. Integrals involving this shell will be converted back to
744 // pure form later.
745 // If shell_tuple.ns = [0, 2, 3, 1], then the a particular cart_indices could
746 // take the value [0, 2, 10, 1] which represents
747 // - s function on the first shell
748 // - dxz function on the second shell
749 // - fzzz function on the third shell
750 // - py function on the fourth shell
751 let mut cart_indices_iter = cart_indices.into_iter();
752 $(
753 let $shell_name = cart_indices_iter
754 .next()
755 .expect("cart_index out of range.");
756 )+
757 let cart_indices = [$($shell_name),+];
758
759 // cart_powers translates cart_indices into tuples of Cartesian powers
760 // for each shell.
761 // For example, with shell_tuple.ns = (0, 2, 3, 1) and
762 // cart_indices = (0, 2, 10, 1), cart_powers is given by
763 // [(0, 0, 0), (1, 0, 1), (0, 0, 3), (0, 1, 0)] (assuming
764 // lexicographic ordering).
765 let cart_powers = {
766 let mut cart_powers_mut = [$(
767 replace_expr!(($shell_name) (0, 0, 0))
768 ),+];
769 cart_powers_mut.iter_mut().enumerate().for_each(|(shell_index, cart_power)| {
770 let cart_order = match &self
771 .shells[shell_index].0
772 .basis_shell()
773 .shell_order {
774 ShellOrder::Pure(po) => CartOrder::lex(po.lpure),
775 ShellOrder::Cart(co) => co.clone(),
776 ShellOrder::Spinor(_) => panic!("Cartesian orders cannot be constructed for a spinor shell.")
777 };
778 *cart_power = cart_order
779 .cart_tuples[cart_indices[shell_index]]
780 .clone();
781 });
782 cart_powers_mut
783 };
784
785 // n_tuples_xyz gives n_tuple for each Cartesian component.
786 // With cart_powers = [(0, 0, 0), (1, 0, 1), (0, 0, 3), (0, 1, 0)],
787 // n_tuples_xyz is given by
788 // [(0, 1, 0, 0), (0, 0, 0, 1), (0, 1, 3, 0)]
789 // -----x------ -----y------ -----z------
790 // which means: take the product of the (0, 1, 0, 0) x-integral,
791 // (0, 0, 0, 1) y-integral, and (0, 1, 3, 0) z-integral to give int_xyz.
792 // Essentially, n_tuples_xyz is transposed cart_powers.
793 let l_tuples_xyz = outer_l_tuples_xyz.clone();
794 let n_tuples_xyz = {
795 let mut n_tuples_xyz_mut = [[$(replace_expr!(($shell_name) 0usize)),+]; 3];
796 n_tuples_xyz_mut[0].iter_mut().enumerate().for_each(|(shell_index, n)| {
797 *n = usize::try_from(cart_powers[shell_index].0)
798 .expect("Unable to convert `n` to `usize`.");
799 });
800 n_tuples_xyz_mut[1].iter_mut().enumerate().for_each(|(shell_index, n)| {
801 *n = usize::try_from(cart_powers[shell_index].1)
802 .expect("Unable to convert `n` to `usize`.");
803 });
804 n_tuples_xyz_mut[2].iter_mut().enumerate().for_each(|(shell_index, n)| {
805 *n = usize::try_from(cart_powers[shell_index].2)
806 .expect("Unable to convert `n` to `usize`.");
807 });
808 n_tuples_xyz_mut
809 };
810 let int_xyz = izip!(l_tuples_xyz.iter(), n_tuples_xyz.iter())
811 .enumerate()
812 .map(|(i, (l_tuple, n_tuple))| {
813 ints_r[i][*l_tuple][*n_tuple].as_ref()
814 })
815 .collect::<Option<Vec<_>>>()
816 .map(|arrs| arrs.into_iter().fold(
817 Array::<dtype, Dim<[usize; $RANK]>>::ones(
818 self.primitive_shell_shape
819 ),
820 |acc, arr| acc * arr
821 ))
822 .unwrap_or_else(
823 || Array::<dtype, Dim<[usize; $RANK]>>::zeros(
824 self.primitive_shell_shape
825 )
826 );
827
828 // Contraction coefficients are involved here.
829 cart_shell_block[cart_indices] = einsum(
830 &format!("{all_shells_contraction_str},{all_shells_contraction_str}->"),
831 &[&int_xyz, dd_type]
832 )
833 .expect("Unable to contract `int_xyz` with `dd`.")
834 .into_iter()
835 .next()
836 .expect("Unable to retrieve the result of the contraction between `int_xyz` and `dd`.");
837 }
838
839 // Transform some shells to spherical if necessary
840 if (0..$RANK).any(|i| matches!(self.shells[i].0.basis_shell().shell_order, ShellOrder::Pure(_))) {
841 // We need an extra letter for the contraction axis.
842 assert!($RANK < 26);
843 let rank_u8 = $RANK.to_u8().expect("Unable to convert the shell tuple rank to `u8`.");
844 let transformed_shell_block = (0..$RANK)
845 .fold(cart_shell_block, |acc, i| {
846 if let ShellOrder::Pure(_) = self.shells[i].0.basis_shell().shell_order {
847 let i_u8 = i.to_u8().expect("Unable to convert a shell index to `u8`.");
848 let rl2cart = rl2cart_type;
849 let cart_to_pure_contraction_str = format!(
850 "{}{}",
851 (i_u8 + 97) as char,
852 (i_u8 + 97 + rank_u8) as char,
853 );
854 let result_str = (0..$RANK).map(|j| {
855 if j == i {
856 (i_u8 + 97 + rank_u8) as char
857 } else {
858 let j_u8 = j.to_u8().expect("Unable to convert a shell index to `u8`.");
859 (j_u8 + 97) as char
860 }
861 }).collect::<String>();
862 einsum(
863 &format!(
864 "{all_shells_contraction_str},\
865 {cart_to_pure_contraction_str}->\
866 {result_str}"
867 ),
868 &[&acc, rl2cart]
869 )
870 .unwrap_or_else(|_| panic!("Unable to convert shell {i} to spherical order."))
871 .into_dimensionality::<Dim<[usize; $RANK]>>()
872 .unwrap_or_else(|_| panic!("Unable to convert the transformed shell block into the correct shape."))
873 } else {
874 acc
875 }
876 });
877 transformed_shell_block
878 } else {
879 cart_shell_block
880 }
881 }).collect::<Vec<_>>();
882 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
883 // Population of Cartesian integrals for each derivative component ends.
884 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
885
886 shell_blocks
887 }
888 }
889
890 #[duplicate_item(
891 [
892 dtype [ f64 ]
893 ]
894 [
895 dtype [ C128 ]
896 ]
897 )]
898 impl<'a> ShellTupleCollection<'a, $RANK, dtype> {
899 /// Calculates the overlap arrays for this shell tuple collection.
900 ///
901 /// # Arguments
902 ///
903 /// * `ls` - The derivative pattern.
904 ///
905 /// # Returns
906 ///
907 /// A vector of overlap arrays, each of which is for one derivative component.
908 pub fn overlap(
909 &self, ls: [usize; $RANK]
910 ) -> Vec<Array<dtype, Dim<[usize; $RANK]>>> {
911 let lex_cart_orders = (0..=*ls.iter().max().expect("Unable to determine the maximum derivative order."))
912 .map(|l| CartOrder::lex(u32::try_from(l).expect("Unable to convert a derivative order to `u32`.")))
913 .collect::<Vec<_>>();
914
915 // shell_blockss = [shell_blocks0, shell_blocks1, ...]
916 // shell_blocks0: all derivative components for shell tuple 0
917 // shell_blocks0 = [shell_block00, shell_block11, ...]
918 // shell_block01: first derivative component for shell tuple 0
919 let shell_blockss = self
920 .unique_shell_tuples_iter(ls)
921 .par_bridge()
922 .map(|(shell_tuple, unique_perm, equiv_perms)| {
923 log::debug!("Working on unique permutation: {unique_perm:?}");
924 (shell_tuple.overlap(ls), unique_perm, equiv_perms)
925 })
926 .collect::<Vec<_>>();
927
928 // Pack integrals
929 let intss = ls
930 .iter()
931 .map(|l| 0..(((l + 1) * (l + 2)).div_euclid(2)))
932 .multi_cartesian_product()
933 .enumerate()
934 .map(|(l_component_index, l_indices)| {
935 let mut l_indices_iter = l_indices.into_iter();
936 $(
937 let $shell_name = l_indices_iter
938 .next()
939 .expect("l index out of range.");
940 )+
941 let l_indices = [$($shell_name),+];
942
943 // l_powers translates l_indices into tuples of component derivative orders
944 // for each shell.
945 // For example, with ls = [0, 1, 2] and l_indices = [0, 2, 3],
946 // l_powers is given by [(0, 0, 0), (0, 0, 1), (0, 2, 0)].
947 let l_powers = {
948 let mut l_powers_mut = [$(
949 replace_expr!(($shell_name) (0, 0, 0))
950 ),+];
951 l_powers_mut.iter_mut().enumerate().for_each(|(shell_index, l_power)| {
952 *l_power = lex_cart_orders[ls[shell_index]].cart_tuples[l_indices[shell_index]].clone();
953 });
954 l_powers_mut
955 };
956 log::debug!("Component {l_component_index} is for derivative {l_powers:?}.");
957 let mut ints = Array::<dtype, Dim<[usize; $RANK]>>::zeros(
958 self.angular_all_shell_shape
959 );
960
961 shell_blockss.iter().for_each(|(shell_blocks, unique_perm, equiv_perms)| {
962 equiv_perms.iter().for_each(|&equiv_perm| {
963 let mut unique_perm_vec = unique_perm
964 .iter()
965 .map(|i| Some(*i))
966 .collect::<Vec<_>>();
967 let mut transpose_indices_iter = equiv_perm.iter().map(|i| {
968 let index = unique_perm_vec
969 .iter()
970 .position(|&j| j == Some(*i))
971 .unwrap_or_else(|| {
972 panic!("Unable to find a permutation that maps {unique_perm:?} to {equiv_perm:?}.");
973 });
974 unique_perm_vec[index] = None;
975 index
976 });
977 $(
978 let $shell_name = transpose_indices_iter
979 .next()
980 .expect("Shell index out of range.");
981 )+
982 let transpose_indices = [$($shell_name),+];
983
984 let mut shell_boundaries_iter = equiv_perm
985 .iter()
986 .enumerate()
987 .map(|(shell_index, &i)| {
988 self.basis_sets[shell_index].shell_boundaries()[i]
989 });
990 $(
991 let $shell_name = shell_boundaries_iter
992 .next()
993 .expect("Shell index out of range.");
994 )+
995 let shell_slices = s![$(
996 $shell_name.0..$shell_name.1
997 ),+];
998 ints.slice_mut(shell_slices).assign(
999 &shell_blocks[l_component_index]
1000 .clone()
1001 .permuted_axes(transpose_indices)
1002 );
1003 })
1004 });
1005 ints
1006 }).collect::<Vec<_>>();
1007
1008 intss
1009 }
1010 }
1011 }
1012}
1013
1014pub(crate) use impl_shell_tuple_overlap;