libcint/cint_prop.rs
1//! Properties of the [`CInt`] instance (compute and memory cost is not
2//! essential in this module).
3
4use crate::prelude::*;
5
6/// Properties of the `CInt` instance (compute and memory cost is not essential
7/// in this module).
8impl CInt {
9 /// Whether pseudo potential is used in the system.
10 ///
11 /// # PySCF Equivalent
12 ///
13 /// Method `Mole.has_ecp`
14 ///
15 /// # Examples
16 ///
17 /// ```rust
18 /// use libcint::prelude::*;
19 /// let cint_data = init_h2o_def2_tzvp();
20 /// assert!(!cint_data.has_ecp());
21 ///
22 /// let cint_data = init_sb2me4_cc_pvtz();
23 /// assert!(cint_data.has_ecp());
24 /// ```
25 #[inline]
26 pub fn has_ecp(&self) -> bool {
27 !self.ecpbas.is_empty()
28 }
29
30 /// Whether spin-orbit coupling is enabled in ECP.
31 ///
32 /// # PySCF Equivalent
33 ///
34 /// Method `Mole.has_ecp_soc`
35 ///
36 /// # Examples
37 /// ```rust
38 /// use libcint::prelude::*;
39 /// let cint_data = init_sb2me4_cc_pvtz();
40 /// assert!(!cint_data.has_ecp_soc());
41 /// ```
42 #[inline]
43 pub fn has_ecp_soc(&self) -> bool {
44 const SO_TYPE_OF: usize = cecp_ffi::SO_TYPE_OF as usize;
45 self.ecpbas.iter().any(|ecp| ecp[SO_TYPE_OF] == 1)
46 }
47
48 /// Number of shells in the system.
49 ///
50 /// This does not count ECP shells.
51 ///
52 /// Please note that, `self.bas.len()` may not be the actual number of
53 /// shells, because in some cases, ECP shells are merged into the GTO
54 /// shells.
55 ///
56 /// # PySCF Equivalent
57 ///
58 /// Attribute `Mole.nbas`
59 ///
60 /// # Examples
61 ///
62 /// ```rust
63 /// use libcint::prelude::*;
64 ///
65 /// let cint_data = init_h2o_def2_tzvp();
66 /// assert_eq!(cint_data.nbas(), 19);
67 ///
68 /// let cint_data = init_sb2me4_cc_pvtz();
69 /// assert_eq!(cint_data.nbas(), 130);
70 ///
71 /// # // This is test that when ecpbas is merged, the number of shells is still correct.
72 /// # let merged = cint_data.merge_ecpbas();
73 /// # assert_eq!(merged.nbas(), 130);
74 /// ```
75 #[inline]
76 pub fn nbas(&self) -> usize {
77 if self.is_ecp_merged() {
78 self.bas.len() - self.ecpbas.len()
79 } else {
80 self.bas.len()
81 }
82 }
83
84 /// Number of atoms in the system.
85 #[inline]
86 pub fn natm(&self) -> usize {
87 self.atm.len()
88 }
89
90 /// Pointer to the shell data.
91 #[inline]
92 pub fn bas_ptr(&self) -> *const c_int {
93 self.bas.as_ptr() as *const c_int
94 }
95
96 /// Pointer to the atom data.
97 #[inline]
98 pub fn atm_ptr(&self) -> *const c_int {
99 self.atm.as_ptr() as *const c_int
100 }
101
102 /// Pointer to the environment data.
103 #[inline]
104 pub fn env_ptr(&self) -> *const f64 {
105 self.env.as_ptr()
106 }
107
108 /// Nuclear effective charge of the given atom id.
109 ///
110 /// # Note
111 ///
112 /// `atom_charge != charge(atom_symbol)` when ECP is enabled.
113 ///
114 /// Number of electrons screened by ECP can be obtained by
115 /// `charge(atom_symbol) - atom_charge`.
116 ///
117 /// # PySCF Equivalent
118 ///
119 /// Method `Mole.atom_charge`
120 ///
121 /// # Examples
122 /// ```rust
123 /// use libcint::prelude::*;
124 /// let cint_data = init_h2o_def2_tzvp();
125 /// assert_eq!(cint_data.atom_charge(0), 8.0); // O (8)
126 /// assert_eq!(cint_data.atom_charge(1), 1.0); // H (1)
127 ///
128 /// let cint_data = init_sb2me4_cc_pvtz();
129 /// assert_eq!(cint_data.atom_charge(0), 23.0); // Sb (51) with ECP (-36)
130 /// assert_eq!(cint_data.atom_charge(2), 6.0); // C (6)
131 /// ```
132 #[inline]
133 pub fn atom_charge(&self, atm_id: usize) -> f64 {
134 const NUC_MOD_OF: usize = cint_ffi::NUC_MOD_OF as usize;
135 const CHARGE_OF: usize = cint_ffi::CHARGE_OF as usize;
136 const FRAC_CHARGE_NUC: u32 = cint_ffi::FRAC_CHARGE_NUC;
137 const PTR_FRAC_CHARGE: usize = cint_ffi::PTR_FRAC_CHARGE as usize;
138
139 if self.atm[atm_id][NUC_MOD_OF] as u32 != FRAC_CHARGE_NUC {
140 // regular QM atoms
141 self.atm[atm_id][CHARGE_OF] as f64
142 } else {
143 // MM atoms with fractional charges
144 self.env[self.atm[atm_id][PTR_FRAC_CHARGE] as usize]
145 }
146 }
147
148 /// List of Nuclear effective charge of all atoms in system.
149 ///
150 /// # Note
151 ///
152 /// `atom_charge != charge(atom_symbol)` when ECP is enabled.
153 ///
154 /// Number of electrons screened by ECP can be obtained by
155 /// `charge(atom_symbol) - atom_charge`.
156 ///
157 /// # PySCF Equivalent
158 ///
159 /// Method `Mole.atom_charges`
160 ///
161 /// # Examples
162 /// ```rust
163 /// use libcint::prelude::*;
164 /// let cint_data = init_h2o_def2_tzvp();
165 /// assert_eq!(cint_data.atom_charges(), vec![8., 1., 1.]);
166 ///
167 /// // For Sb2Me4, the first atom is Sb (51) with ECP (-36), so the charge is 23.
168 /// let cint_data = init_sb2me4_cc_pvtz();
169 /// assert_eq!(cint_data.atom_charges(), vec![23., 23., 6., 6., 6., 6., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]);
170 /// ```
171 #[inline]
172 pub fn atom_charges(&self) -> Vec<f64> {
173 (0..self.atm.len()).map(|i| self.atom_charge(i)).collect()
174 }
175
176 /// The number of spinor associated with given angular momentum $l$ and
177 /// kappa $\kappa$.
178 ///
179 /// - If $\kappa = 0$, it returns $4 l + 2$.
180 /// - If $\kappa > 0$, it returns $2 l + 2$.
181 /// - If $\kappa < 0$, it returns $2 l$.
182 ///
183 /// # PySCF Equivalent
184 ///
185 /// Function `gto.mole.len_spinor`
186 #[inline]
187 pub fn len_spinor(l: i32, kappa: i32) -> usize {
188 if kappa == 0 {
189 (l * 4 + 2) as usize
190 } else if kappa > 0 {
191 (l * 2 + 2) as usize
192 } else {
193 (l * 2) as usize
194 }
195 }
196
197 /// The number of Cartesian function associated with given angular momentum
198 /// $l$.
199 ///
200 /// This will gives $\frac{(l + 1) (l + 2)}{2}$
201 ///
202 /// # PySCF Equivalent
203 ///
204 /// Function `gto.mole.len_cart`
205 #[inline]
206 pub fn len_cart(l: i32) -> usize {
207 ((l + 1) * (l + 2) / 2) as usize
208 }
209
210 /// The number of spherical function associated with given angular momentum
211 /// $l$.
212 ///
213 /// This will gives $2 l + 1$.
214 ///
215 /// # PySCF Equivalent
216 ///
217 /// Function `gto.mole.len_sph`
218 #[inline]
219 pub fn len_sph(l: i32) -> usize {
220 (l * 2 + 1) as usize
221 }
222
223 /// Location mapping from shell to basis.
224 ///
225 /// The type of integral is specified by struct field `cint_type`.
226 ///
227 /// Output vector is of length `nshl + 1`, where `nshl` is the number of
228 /// shells.
229 ///
230 /// # PySCF Equivalent
231 ///
232 /// This implementation follows `gto.moleintor.make_loc`.
233 ///
234 /// For `gto.Mole` object, methods `ao_loc`, `ao_loc_nr`, `ao_loc_2c` are
235 /// also relevant.
236 ///
237 /// # Examples
238 ///
239 /// ```rust
240 /// use libcint::prelude::*;
241 /// let cint_data = init_h2o_def2_tzvp();
242 ///
243 /// let loc_sph = cint_data.make_loc();
244 /// assert_eq!(loc_sph, vec![ 0, 1, 2, 3, 4, 5, 8, 11, 14, 19, 24, 31, 32, 33, 34, 37, 38, 39, 40, 43]);
245 /// ```
246 #[inline]
247 pub fn make_loc(&self) -> Vec<usize> {
248 self.make_loc_with_type(self.cint_type)
249 }
250
251 /// Location mapping from shell to basis.
252 ///
253 /// # See also
254 ///
255 /// [`CInt::make_loc`]
256 #[inline]
257 pub fn ao_loc(&self) -> Vec<usize> {
258 self.make_loc()
259 }
260
261 /// Location mapping from shell to basis (with integral type specified).
262 ///
263 /// This mapping is cumulated, and can be different for sph, cart and spinor
264 /// types.
265 ///
266 /// Output vector is of length `nshl + 1`, where `nshl` is the number of
267 /// shells.
268 ///
269 /// # PySCF Equivalent
270 ///
271 /// This implementation follows `gto.moleintor.make_loc`.
272 ///
273 /// For `gto.Mole` object, methods `ao_loc`, `ao_loc_nr`, `ao_loc_2c` are
274 /// also relevant.
275 ///
276 /// # Examples
277 ///
278 /// ```rust
279 /// use libcint::prelude::*;
280 /// let cint_data = init_h2o_def2_tzvp();
281 ///
282 /// let loc_sph = cint_data.make_loc_with_type(CIntType::Spheric);
283 /// assert_eq!(loc_sph, vec![ 0, 1, 2, 3, 4, 5, 8, 11, 14, 19, 24, 31, 32, 33, 34, 37, 38, 39, 40, 43]);
284 ///
285 /// let loc_cart = cint_data.make_loc_with_type(CIntType::Cartesian);
286 /// assert_eq!(loc_cart, vec![ 0, 1, 2, 3, 4, 5, 8, 11, 14, 20, 26, 36, 37, 38, 39, 42, 43, 44, 45, 48]);
287 ///
288 /// let loc_spinor = cint_data.make_loc_with_type(CIntType::Spinor);
289 /// assert_eq!(loc_spinor, vec![ 0, 2, 4, 6, 8, 10, 16, 22, 28, 38, 48, 62, 64, 66, 68, 74, 76, 78, 80, 86]);
290 /// ```
291 pub fn make_loc_with_type(&self, cint_type: CIntType) -> Vec<usize> {
292 const ANG_OF: usize = cint_ffi::ANG_OF as usize;
293 const KAPPA_OF: usize = cint_ffi::KAPPA_OF as usize;
294 const NCTR_OF: usize = cint_ffi::NCTR_OF as usize;
295
296 let mut ao_loc = vec![0];
297 let nbas = self.nbas();
298 for shl in 0..nbas {
299 let l = self.bas[shl][ANG_OF];
300 let k = self.bas[shl][KAPPA_OF];
301 let nctr = self.bas[shl][NCTR_OF] as usize;
302 let val = match cint_type {
303 Spheric => Self::len_sph(l) * nctr,
304 Cartesian => Self::len_cart(l) * nctr,
305 Spinor => Self::len_spinor(l, k) * nctr,
306 };
307 ao_loc.push(ao_loc[shl] + val);
308 }
309 ao_loc
310 }
311
312 /// Get the number of basis (atomic orbitals).
313 ///
314 /// The type of integral is specified by struct field `cint_type`.
315 ///
316 /// This value is the same to the last of [`CInt::make_loc_with_type`].
317 ///
318 /// # PySCF Equivalent
319 ///
320 /// For `gto.Mole` object, methods `nao_nr`, `nao_cart`, `nao_2c` are
321 /// relevant.
322 ///
323 ///
324 /// # Examples
325 ///
326 /// ```rust
327 /// use libcint::prelude::*;
328 /// let cint_data = init_h2o_def2_tzvp();
329 ///
330 /// let nao_sph = cint_data.nao();
331 /// assert_eq!(nao_sph, 43);
332 ///
333 /// let cint_data = init_sb2me4_cc_pvtz();
334 /// let nao_sph = cint_data.nao();
335 /// assert_eq!(nao_sph, 366);
336 ///
337 /// # // This is test that when ecpbas is merged, the number of
338 /// # // atomic orbitals is still correct.
339 /// # let merged = cint_data.merge_ecpbas();
340 /// # let nao_cart = merged.nao();
341 /// # assert_eq!(nao_cart, 366);
342 /// ```
343 #[inline]
344 pub fn nao(&self) -> usize {
345 self.nao_with_type(self.cint_type)
346 }
347
348 /// Get the number of basis (atomic orbitals, with integral type specified).
349 ///
350 /// This value is different for sph, cart and spinor types.
351 ///
352 /// This value is the same to the last of [`CInt::make_loc_with_type`].
353 ///
354 /// # PySCF Equivalent
355 ///
356 /// For `gto.Mole` object, methods `nao_nr`, `nao_cart`, `nao_2c` are
357 /// relevant.
358 ///
359 ///
360 /// # Examples
361 ///
362 /// ```rust
363 /// use libcint::prelude::*;
364 /// let cint_data = init_h2o_def2_tzvp();
365 ///
366 /// let nao_sph = cint_data.nao_with_type(CIntType::Spheric);
367 /// assert_eq!(nao_sph, 43);
368 ///
369 /// let nao_cart = cint_data.nao_with_type(CIntType::Cartesian);
370 /// assert_eq!(nao_cart, 48);
371 ///
372 /// let nao_spinor = cint_data.nao_with_type(CIntType::Spinor);
373 /// assert_eq!(nao_spinor, 86);
374 ///
375 /// let cint_data = init_sb2me4_cc_pvtz();
376 /// let nao_sph = cint_data.nao_with_type(CIntType::Spheric);
377 /// assert_eq!(nao_sph, 366);
378 ///
379 /// # // This is test that when ecpbas is merged, the number of
380 /// # // atomic orbitals is still correct.
381 /// # let merged = cint_data.merge_ecpbas();
382 /// # let nao_cart = merged.nao_with_type(CIntType::Spheric);
383 /// # assert_eq!(nao_cart, 366);
384 /// ```
385 #[inline]
386 pub fn nao_with_type(&self, cint_type: CIntType) -> usize {
387 const ANG_OF: usize = cint_ffi::ANG_OF as usize;
388 const KAPPA_OF: usize = cint_ffi::KAPPA_OF as usize;
389 const NCTR_OF: usize = cint_ffi::NCTR_OF as usize;
390
391 let mut nao = 0;
392 let nbas = self.nbas();
393 for shl in 0..nbas {
394 let l = self.bas[shl][ANG_OF];
395 let k = self.bas[shl][KAPPA_OF];
396 let nctr = self.bas[shl][NCTR_OF];
397 let val = match cint_type {
398 Spheric => Self::len_sph(l) as c_int * nctr,
399 Cartesian => Self::len_cart(l) as c_int * nctr,
400 Spinor => Self::len_spinor(l, k) as c_int * nctr,
401 };
402 nao += val as usize;
403 }
404 nao
405 }
406
407 /// Coordinates of the given atom id in unit Bohr (a.u.).
408 ///
409 /// # PySCF Equivalent
410 ///
411 /// Method `Mole.atom_coord` with `unit="Bohr"`.
412 #[inline]
413 pub fn atom_coord(&self, atm_id: usize) -> [f64; 3] {
414 const PTR_COORD: usize = cint_ffi::PTR_COORD as usize;
415
416 let ptr = self.atm[atm_id][PTR_COORD] as usize;
417 [self.env[ptr], self.env[ptr + 1], self.env[ptr + 2]]
418 }
419
420 /// Coordinates of all atoms in unit Bohr (a.u.).
421 ///
422 /// # PySCF Equivalent
423 ///
424 /// Method `Mole.atom_coords` with `unit="Bohr"`.
425 ///
426 /// # Examples
427 ///
428 /// ```rust
429 /// use libcint::prelude::*;
430 /// # use approx::assert_relative_eq;
431 /// let cint_data = init_h2o_def2_tzvp();
432 /// let coords = cint_data.atom_coords();
433 /// // Output:
434 /// // [[ 0.000000, 0.000000, 0.000000],
435 /// // [ 1.776343, 0.000000, 0.000000],
436 /// // [ -0.444761, 0.000000, 1.719762]]
437 /// assert_relative_eq!(cint_fingerprint(&coords), -2.4358371781626658, max_relative=1e-12);
438 /// ```
439 #[inline]
440 pub fn atom_coords(&self) -> Vec<[f64; 3]> {
441 (0..self.atm.len()).map(|i| self.atom_coord(i)).collect_vec()
442 }
443
444 /// Number of shells of the given atom
445 ///
446 /// # PySCF Equivalent
447 ///
448 /// Method `Mole.atom_nshells`
449 ///
450 /// # Examples
451 ///
452 /// ```rust
453 /// use libcint::prelude::*;
454 /// let cint_data = init_h2o_def2_tzvp();
455 /// assert_eq!(cint_data.atom_nshells(0), 11);
456 /// ```
457 #[inline]
458 pub fn atom_nshells(&self, atm_id: usize) -> usize {
459 const ATOM_OF: usize = cint_ffi::ATOM_OF as usize;
460
461 let nbas = self.nbas();
462 self.bas[..nbas].iter().filter(|bas| bas[ATOM_OF] as usize == atm_id).count()
463 }
464
465 /// List of shell ids of the given atom.
466 ///
467 /// # PySCF Equivalent
468 ///
469 /// Method `Mole.atom_shell_ids`
470 ///
471 /// # Examples
472 ///
473 /// ```rust
474 /// use libcint::prelude::*;
475 /// let cint_data = init_h2o_def2_tzvp();
476 /// assert_eq!(cint_data.atom_shell_ids(0), vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
477 /// ```
478 #[inline]
479 pub fn atom_shell_ids(&self, atm_id: usize) -> Vec<usize> {
480 const ATOM_OF: usize = cint_ffi::ATOM_OF as usize;
481
482 let nbas = self.nbas();
483 self.bas[..nbas].iter().enumerate().filter_map(|(i, bas)| if bas[ATOM_OF] as usize == atm_id { Some(i) } else { None }).collect()
484 }
485
486 #[inline]
487 fn check_bas_id(&self, bas_id: usize) {
488 let nbas = self.nbas();
489 if bas_id >= nbas {
490 panic!("Invalid bas_id: {bas_id}. Number of shells is {nbas}");
491 }
492 }
493
494 /// Coordinates of the given shell in unit Bohr (a.u.).
495 ///
496 /// # PySCF Equivalent
497 ///
498 /// Method `Mole.bas_coord`
499 #[inline]
500 pub fn bas_coord(&self, bas_id: usize) -> [f64; 3] {
501 const PTR_COORD: usize = cint_ffi::PTR_COORD as usize;
502
503 self.check_bas_id(bas_id);
504 let atm_id = self.bas_atom(bas_id);
505 let ptr = self.atm[atm_id][PTR_COORD] as usize;
506 [self.env[ptr], self.env[ptr + 1], self.env[ptr + 2]]
507 }
508
509 /// The atom (0-based id) that the given shell sits on.
510 ///
511 /// # PySCF Equivalent
512 ///
513 /// Method `Mole.bas_atom`
514 #[inline]
515 pub fn bas_atom(&self, bas_id: usize) -> usize {
516 const ATOM_OF: usize = cint_ffi::ATOM_OF as usize;
517
518 self.check_bas_id(bas_id);
519 self.bas[bas_id][ATOM_OF] as usize
520 }
521
522 /// The angular momentum associated with the given shell.
523 ///
524 /// # PySCF Equivalent
525 ///
526 /// Method `Mole.bas_angular`
527 #[inline]
528 pub fn bas_angular(&self, bas_id: usize) -> usize {
529 const ANG_OF: usize = cint_ffi::ANG_OF as usize;
530
531 self.check_bas_id(bas_id);
532 self.bas[bas_id][ANG_OF] as usize
533 }
534
535 /// The number of contracted GTOs for the given shell.
536 ///
537 /// # PySCF Equivalent
538 ///
539 /// Method `Mole.bas_nctr`
540 #[inline]
541 pub fn bas_nctr(&self, bas_id: usize) -> usize {
542 const NCTR_OF: usize = cint_ffi::NCTR_OF as usize;
543
544 self.check_bas_id(bas_id);
545 self.bas[bas_id][NCTR_OF] as usize
546 }
547
548 /// The number of primitive GTOs for the given shell.
549 ///
550 /// # PySCF Equivalent
551 ///
552 /// Method `Mole.bas_nprim`
553 #[inline]
554 pub fn bas_nprim(&self, bas_id: usize) -> usize {
555 const NPRIM_OF: usize = cint_ffi::NPRIM_OF as usize;
556
557 self.check_bas_id(bas_id);
558 self.bas[bas_id][NPRIM_OF] as usize
559 }
560
561 /// Kappa (if l < j, -l-1, else l) of the given shell.
562 ///
563 /// # PySCF Equivalent
564 ///
565 /// Method `Mole.bas_kappa`
566 #[inline]
567 pub fn bas_kappa(&self, bas_id: usize) -> c_int {
568 const KAPPA_OF: usize = cint_ffi::KAPPA_OF as usize;
569
570 self.check_bas_id(bas_id);
571 self.bas[bas_id][KAPPA_OF]
572 }
573
574 /// Exponents of the given shell.
575 ///
576 /// # PySCF Equivalent
577 ///
578 /// Method `Mole.bas_exp`
579 #[inline]
580 pub fn bas_exp(&self, bas_id: usize) -> &[f64] {
581 const PTR_EXP: usize = cint_ffi::PTR_EXP as usize;
582
583 self.check_bas_id(bas_id);
584 let ptr = self.bas[bas_id][PTR_EXP] as usize;
585 &self.env[ptr..ptr + self.bas_nprim(bas_id)]
586 }
587
588 /// Exponents of all shells.
589 ///
590 /// # PySCF Equivalent
591 ///
592 /// Method `Mole.bas_exps`
593 #[inline]
594 pub fn bas_exps(&self) -> Vec<Vec<f64>> {
595 (0..self.nbas()).map(|i| self.bas_exp(i).to_vec()).collect()
596 }
597
598 /// Shell and basis (atomic orbitals) offsets for each atom.
599 ///
600 /// This will give a list of `[shl_start, shl_end, ao_start, ao_end]` for
601 /// each atom.
602 ///
603 /// # PySCF Equivalent
604 ///
605 /// Method `Mole.aoslice_by_atom`. This implementation does not allow
606 /// arbitrary `ao_loc` (which can be obtained by [`CInt::make_loc`]).
607 /// However, user can provide `cint_type` to specify if you wish to use
608 /// sph, cart or spinor.
609 ///
610 /// # Examples
611 ///
612 /// ```rust
613 /// use libcint::prelude::*;
614 /// let cint_data = init_h2o_def2_tzvp();
615 /// let aoslice = cint_data.aoslice_by_atom();
616 /// assert_eq!(aoslice, vec![
617 /// [ 0, 11, 0, 31],
618 /// [11, 15, 31, 37],
619 /// [15, 19, 37, 43],
620 /// ]);
621 /// ```
622 #[inline]
623 pub fn aoslice_by_atom(&self) -> Vec<[usize; 4]> {
624 self.aoslice_by_atom_with_type(self.cint_type)
625 }
626
627 /// Shell and basis (atomic orbitals) offsets for each atom (with integral
628 /// type specified).
629 ///
630 /// This will give a list of `[shl_start, shl_end, ao_start, ao_end]` for
631 /// each atom.
632 ///
633 /// # PySCF Equivalent
634 ///
635 /// Method `Mole.aoslice_by_atom`. This implementation does not allow
636 /// arbitrary `ao_loc` (which can be obtained by [`CInt::make_loc`]).
637 /// However, user can provide `cint_type` to specify if you wish to use
638 /// sph, cart or spinor.
639 ///
640 /// # Examples
641 ///
642 /// ```rust
643 /// use libcint::prelude::*;
644 /// let cint_data = init_h2o_def2_tzvp();
645 /// let aoslice = cint_data.aoslice_by_atom_with_type(CIntType::Spheric);
646 /// assert_eq!(aoslice, vec![
647 /// [ 0, 11, 0, 31],
648 /// [11, 15, 31, 37],
649 /// [15, 19, 37, 43],
650 /// ]);
651 /// ```
652 pub fn aoslice_by_atom_with_type(&self, cint_type: CIntType) -> Vec<[usize; 4]> {
653 const ATOM_OF: usize = cint_ffi::ATOM_OF as usize;
654
655 let nbas = self.nbas();
656 let ao_loc = self.make_loc_with_type(cint_type);
657
658 // category atoms with basis index
659 // [atm_id, shl_start, shl_end]
660 let mut category = vec![];
661 if nbas > 0 {
662 let mut current_atm = self.bas[0][ATOM_OF];
663 let mut shl_start = 0;
664 for shl in 0..self.nbas() {
665 let atm_id = self.bas[shl][ATOM_OF];
666 if atm_id != current_atm {
667 category.push([current_atm as usize, shl_start, shl]);
668 current_atm = atm_id;
669 shl_start = shl;
670 }
671 }
672 category.push([current_atm as usize, shl_start, nbas]);
673 }
674
675 // fill result based on category
676 let mut result = vec![];
677 let mut ptr_category = 0;
678 for atm_id in 0..self.atm.len() {
679 if ptr_category < category.len() && atm_id == category[ptr_category][0] {
680 // atom exists in bas
681 let shl_start = category[ptr_category][1];
682 let shl_end = category[ptr_category][2];
683 let ao_start = ao_loc[shl_start];
684 let ao_end = ao_loc[shl_end];
685 result.push([shl_start, shl_end, ao_start, ao_end]);
686 ptr_category += 1;
687 } else {
688 // atom missing in bas
689 // following unwrap occurs for first atom
690 let &[_, shl_end, _, ao_end] = result.last().unwrap_or(&[0; 4]);
691 result.push([shl_end, shl_end, ao_end, ao_end]);
692 }
693 }
694
695 // panic if I am wrong
696 // check result length
697 assert_eq!(result.len(), self.atm.len());
698 // check total number of basis
699 assert_eq!(result.last().unwrap()[3], self.nao_with_type(cint_type));
700 result
701 }
702
703 /// Number of grids that is used in `int1e_grids`.
704 ///
705 /// Note that this function is mostly for internal usage. User should
706 /// generally not call this function.
707 pub fn ngrids(&self) -> usize {
708 const NGRIDS: usize = crate::ffi::cint_ffi::NGRIDS as usize;
709 self.env[NGRIDS] as usize
710 }
711
712 /// Balances the partition of shells into blocks of a given size.
713 ///
714 /// This function can be useful if the full bulk of integrals is not
715 /// available in memory, and you have to generate them batch by batch.
716 ///
717 /// The outputs are
718 /// - `shl_start`: the starting index of the shell in the partition
719 /// (included)
720 /// - `shl_end`: the ending index of the shell in the partition (not
721 /// included)
722 /// - `nbatch_ao`: the number of AOs in the batch.
723 ///
724 /// # PySCF equivalent
725 ///
726 /// `ao2mo.outcore.balance_partition`
727 ///
728 /// # Example
729 ///
730 /// ```
731 /// use libcint::prelude::*;
732 /// let cint_data = init_h2o_def2_tzvp();
733 /// let partition = cint_data.balance_partition(20);
734 /// assert_eq!(partition, vec![[0, 9, 19], [9, 17, 20], [17, 19, 4]]);
735 /// ```
736 pub fn balance_partition(&self, block_size: usize) -> Vec<[usize; 3]> {
737 crate::util::balance_partition(self, block_size, None, None)
738 }
739
740 /// Balances the partition of shells into blocks of a given size.
741 ///
742 /// This function is similar to [`CInt::balance_partition`], but allows
743 /// user to specify the start and end id of the shells.
744 pub fn balance_partition_advanced(&self, block_size: usize, start_id: Option<usize>, end_id: Option<usize>) -> Vec<[usize; 3]> {
745 crate::util::balance_partition(self, block_size, start_id, end_id)
746 }
747}
748
749#[cfg(test)]
750mod test {
751 use crate::prelude::*;
752
753 #[test]
754 fn playground() {
755 let cint_data = init_h2o_def2_tzvp();
756 println!("{:?}", cint_data.aoslice_by_atom());
757 }
758}