1use std::cmp::Ordering;
4use std::collections::{HashMap, HashSet};
5use std::convert::TryInto;
6use std::fmt;
7use std::hash::{Hash, Hasher};
8use std::slice::Iter;
9
10use anyhow::{self, ensure, format_err};
11use counter::Counter;
12use derive_builder::Builder;
13use itertools::Itertools;
14#[cfg(test)]
15use itertools::izip;
16use serde::{Deserialize, Serialize};
17
18use crate::angmom::ANGMOM_LABELS;
19use crate::auxiliary::atom::Atom;
20use crate::auxiliary::misc::ProductRepeat;
21use crate::permutation::{PermutableCollection, Permutation, permute_inplace};
22
23#[cfg(test)]
24#[path = "ao_tests.rs"]
25mod ao_tests;
26
27#[derive(Clone, Builder, PartialEq, Eq, Hash)]
37pub struct PureOrder {
38 #[builder(setter(custom))]
40 mls: Vec<i32>,
41
42 pub lpure: u32,
44
45 #[builder(default = "self.default_even()?")]
48 pub even: bool,
49}
50
51impl PureOrderBuilder {
52 fn mls(&mut self, mls: &[i32]) -> &mut Self {
53 let lpure = self.lpure.expect("`lpure` has not been set.");
54 assert!(
55 mls.iter()
56 .map(|m| m.unsigned_abs())
57 .max()
58 .expect("The maximum |m| value could not be determined.")
59 == lpure
60 );
61 assert_eq!(mls.len(), (2 * lpure + 1) as usize);
62 self.mls = Some(mls.to_vec());
63 self
64 }
65
66 fn default_even(&self) -> Result<bool, String> {
67 let lpure = self
68 .lpure
69 .ok_or_else(|| "`lpure` has not been set.".to_string())?;
70 Ok(lpure % 2 == 0)
71 }
72}
73
74impl PureOrder {
75 pub fn builder() -> PureOrderBuilder {
77 PureOrderBuilder::default()
78 }
79
80 pub fn new(mls: &[i32]) -> Result<Self, anyhow::Error> {
83 let lpure = mls
84 .iter()
85 .map(|m| m.unsigned_abs())
86 .max()
87 .expect("The maximum |m| value could not be determined.");
88 let pure_order = PureOrder::builder()
89 .lpure(lpure)
90 .mls(mls)
91 .build()
92 .map_err(|err| format_err!(err))?;
93 ensure!(pure_order.verify(), "Invalid `PureOrder`.");
94 Ok(pure_order)
95 }
96
97 #[must_use]
108 pub fn increasingm(lpure: u32) -> Self {
109 let lpure_i32 = i32::try_from(lpure).expect("`lpure` cannot be converted to `i32`.");
110 let mls = (-lpure_i32..=lpure_i32).collect_vec();
111 Self::builder()
112 .lpure(lpure)
113 .mls(&mls)
114 .build()
115 .expect("Unable to construct a `PureOrder` structure with increasing-m order.")
116 }
117
118 #[must_use]
129 pub fn decreasingm(lpure: u32) -> Self {
130 let lpure_i32 = i32::try_from(lpure).expect("`lpure` cannot be converted to `i32`.");
131 let mls = (-lpure_i32..=lpure_i32).rev().collect_vec();
132 Self::builder()
133 .lpure(lpure)
134 .mls(&mls)
135 .build()
136 .expect("Unable to construct a `PureOrder` structure with decreasing-m order.")
137 }
138
139 #[must_use]
150 pub fn molden(lpure: u32) -> Self {
151 let lpure_i32 = i32::try_from(lpure).expect("`lpure` cannot be converted to `i32`.");
152 let mls = (0..=lpure_i32)
153 .flat_map(|absm| {
154 if absm == 0 {
155 vec![0]
156 } else {
157 vec![absm, -absm]
158 }
159 })
160 .collect_vec();
161 Self::builder()
162 .lpure(lpure)
163 .mls(&mls)
164 .build()
165 .expect("Unable to construct a `PureOrder` structure with Molden order.")
166 }
167
168 #[must_use]
174 pub fn verify(&self) -> bool {
175 let mls_set = self.mls.iter().collect::<HashSet<_>>();
176 let lpure = self.lpure;
177 mls_set.len() == self.ncomps() && mls_set.iter().all(|m| m.unsigned_abs() <= lpure)
178 }
179
180 pub fn iter(&'_ self) -> Iter<'_, i32> {
182 self.mls.iter()
183 }
184
185 pub fn ncomps(&self) -> usize {
187 let lpure = usize::try_from(self.lpure).unwrap_or_else(|_| {
188 panic!(
189 "Unable to convert the pure degree {} to `usize`.",
190 self.lpure
191 )
192 });
193 2 * lpure + 1
194 }
195
196 pub fn get_m_with_index(&self, i: usize) -> Option<i32> {
198 self.mls.get(i).cloned()
199 }
200}
201
202impl fmt::Display for PureOrder {
203 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204 writeln!(f, "Pure rank: {}", self.lpure)?;
205 writeln!(f, "Order:")?;
206 for m in self.iter() {
207 writeln!(f, " {m}")?;
208 }
209 Ok(())
210 }
211}
212
213impl fmt::Debug for PureOrder {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 writeln!(f, "Pure rank: {}", self.lpure)?;
216 writeln!(f, "Order:")?;
217 for m in self.iter() {
218 writeln!(f, " {m:?}")?;
219 }
220 Ok(())
221 }
222}
223
224impl PermutableCollection for PureOrder {
225 type Rank = usize;
226
227 fn get_perm_of(&self, other: &Self) -> Option<Permutation<Self::Rank>> {
228 let o_mls: HashMap<&i32, usize> = other
229 .mls
230 .iter()
231 .enumerate()
232 .map(|(i, o_m)| (o_m, i))
233 .collect();
234 let image_opt: Option<Vec<Self::Rank>> =
235 self.mls.iter().map(|s_m| o_mls.get(s_m).copied()).collect();
236 image_opt.and_then(|image| Permutation::from_image(image).ok())
237 }
238
239 fn permute(&self, perm: &Permutation<Self::Rank>) -> Result<Self, anyhow::Error> {
240 let mut p_pureorder = self.clone();
241 p_pureorder.permute_mut(perm)?;
242 Ok(p_pureorder)
243 }
244
245 fn permute_mut(&mut self, perm: &Permutation<Self::Rank>) -> Result<(), anyhow::Error> {
246 permute_inplace(&mut self.mls, perm);
247 Ok(())
248 }
249}
250
251#[derive(Clone, Builder, PartialEq, Eq, Hash)]
257pub struct CartOrder {
258 #[builder(setter(custom))]
260 pub cart_tuples: Vec<(u32, u32, u32)>,
261
262 pub lcart: u32,
264}
265
266impl CartOrderBuilder {
267 fn cart_tuples(&mut self, cart_tuples: &[(u32, u32, u32)]) -> &mut Self {
268 let lcart = self.lcart.expect("`lcart` has not been set.");
269 assert!(
270 cart_tuples.iter().all(|(lx, ly, lz)| lx + ly + lz == lcart),
271 "Inconsistent total Cartesian orders between components."
272 );
273 assert_eq!(
274 cart_tuples.len(),
275 ((lcart + 1) * (lcart + 2)).div_euclid(2) as usize,
276 "Unexpected number of components for `lcart` = {}.",
277 lcart
278 );
279 self.cart_tuples = Some(cart_tuples.to_vec());
280 self
281 }
282}
283
284impl CartOrder {
285 pub fn builder() -> CartOrderBuilder {
287 CartOrderBuilder::default()
288 }
289
290 pub fn new(cart_tuples: &[(u32, u32, u32)]) -> Result<Self, anyhow::Error> {
298 let first_tuple = cart_tuples
299 .first()
300 .ok_or(format_err!("No Cartesian tuples found."))?;
301 let lcart = first_tuple.0 + first_tuple.1 + first_tuple.2;
302 let cart_order = CartOrder::builder()
303 .lcart(lcart)
304 .cart_tuples(cart_tuples)
305 .build()
306 .map_err(|err| format_err!(err))?;
307 ensure!(cart_order.verify(), "Invalid `CartOrder`.");
308 Ok(cart_order)
309 }
310
311 #[must_use]
321 pub fn lex(lcart: u32) -> Self {
322 let mut cart_tuples =
323 Vec::with_capacity(((lcart + 1) * (lcart + 2)).div_euclid(2) as usize);
324 for lx in (0..=lcart).rev() {
325 for ly in (0..=(lcart - lx)).rev() {
326 cart_tuples.push((lx, ly, lcart - lx - ly));
327 }
328 }
329 Self::builder()
330 .lcart(lcart)
331 .cart_tuples(&cart_tuples)
332 .build()
333 .expect("Unable to construct a `CartOrder` structure with lexicographic order.")
334 }
335
336 #[must_use]
350 pub fn molden(lcart: u32) -> Self {
351 assert!(lcart <= 4, "`lcart` > 4 is not specified by Molden.");
352 let cart_tuples: Vec<(u32, u32, u32)> = match lcart {
353 0 => vec![(0, 0, 0)],
354 1 => vec![(1, 0, 0), (0, 1, 0), (0, 0, 1)],
355 2 => vec![
356 (2, 0, 0),
357 (0, 2, 0),
358 (0, 0, 2),
359 (1, 1, 0),
360 (1, 0, 1),
361 (0, 1, 1),
362 ],
363 3 => vec![
364 (3, 0, 0),
365 (0, 3, 0),
366 (0, 0, 3),
367 (1, 2, 0),
368 (2, 1, 0),
369 (2, 0, 1),
370 (1, 0, 2),
371 (0, 1, 2),
372 (0, 2, 1),
373 (1, 1, 1),
374 ],
375 4 => vec![
376 (4, 0, 0),
377 (0, 4, 0),
378 (0, 0, 4),
379 (3, 1, 0),
380 (3, 0, 1),
381 (1, 3, 0),
382 (0, 3, 1),
383 (1, 0, 3),
384 (0, 1, 3),
385 (2, 2, 0),
386 (2, 0, 2),
387 (0, 2, 2),
388 (2, 1, 1),
389 (1, 2, 1),
390 (1, 1, 2),
391 ],
392 _ => panic!("`lcart` > 4 is not specified by Molden."),
393 };
394 Self::builder()
395 .lcart(lcart)
396 .cart_tuples(&cart_tuples)
397 .build()
398 .expect("Unable to construct a `CartOrder` structure with Molden order.")
399 }
400
401 #[must_use]
411 pub fn qchem(lcart: u32) -> Self {
412 let cart_tuples: Vec<(u32, u32, u32)> = if lcart > 0 {
413 (0..3)
414 .product_repeat(lcart as usize)
415 .filter_map(|tup| {
416 let mut tup_sorted = tup.clone();
417 tup_sorted.sort_unstable();
418 tup_sorted.reverse();
419 if tup == tup_sorted {
420 let lcartqns = tup.iter().collect::<Counter<_>>();
421 Some((
422 <usize as TryInto<u32>>::try_into(*(lcartqns.get(&0).unwrap_or(&0)))
423 .expect("Unable to convert Cartesian x-exponent to `u32`."),
424 <usize as TryInto<u32>>::try_into(*(lcartqns.get(&1).unwrap_or(&0)))
425 .expect("Unable to convert Cartesian y-exponent to `u32`."),
426 <usize as TryInto<u32>>::try_into(*(lcartqns.get(&2).unwrap_or(&0)))
427 .expect("Unable to convert Cartesian z-exponent to `u32`."),
428 ))
429 } else {
430 None
431 }
432 })
433 .collect()
434 } else {
435 vec![(0, 0, 0)]
436 };
437 Self::builder()
438 .lcart(lcart)
439 .cart_tuples(&cart_tuples)
440 .build()
441 .expect("Unable to construct a `CartOrder` structure with Q-Chem order.")
442 }
443
444 #[must_use]
450 pub fn verify(&self) -> bool {
451 let cart_tuples_set = self.cart_tuples.iter().collect::<HashSet<_>>();
452 let lcart = self.lcart;
453 cart_tuples_set.len() == self.ncomps()
454 && cart_tuples_set
455 .iter()
456 .all(|(lx, ly, lz)| lx + ly + lz == lcart)
457 }
458
459 pub fn iter(&'_ self) -> Iter<'_, (u32, u32, u32)> {
461 self.cart_tuples.iter()
462 }
463
464 pub fn ncomps(&self) -> usize {
466 let lcart = usize::try_from(self.lcart).unwrap_or_else(|_| {
467 panic!(
468 "Unable to convert the Cartesian degree {} to `usize`.",
469 self.lcart
470 )
471 });
472 ((lcart + 1) * (lcart + 2)).div_euclid(2)
473 }
474
475 pub fn get_cart_tuple_with_index(&self, i: usize) -> Option<(u32, u32, u32)> {
477 self.cart_tuples.get(i).cloned()
478 }
479}
480
481impl fmt::Display for CartOrder {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 writeln!(f, "Cartesian rank: {}", self.lcart)?;
484 writeln!(f, "Order:")?;
485 for cart_tuple in self.iter() {
486 writeln!(f, " {}", cart_tuple_to_str(cart_tuple, true))?;
487 }
488 Ok(())
489 }
490}
491
492impl fmt::Debug for CartOrder {
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 writeln!(f, "Cartesian rank: {}", self.lcart)?;
495 writeln!(f, "Order:")?;
496 for cart_tuple in self.iter() {
497 writeln!(f, " {cart_tuple:?}")?;
498 }
499 Ok(())
500 }
501}
502
503impl PermutableCollection for CartOrder {
504 type Rank = usize;
505
506 fn get_perm_of(&self, other: &Self) -> Option<Permutation<Self::Rank>> {
507 let o_cart_tuples: HashMap<&(u32, u32, u32), usize> = other
508 .cart_tuples
509 .iter()
510 .enumerate()
511 .map(|(i, o_cart_tuple)| (o_cart_tuple, i))
512 .collect();
513 let image_opt: Option<Vec<Self::Rank>> = self
514 .cart_tuples
515 .iter()
516 .map(|s_cart_tuple| o_cart_tuples.get(s_cart_tuple).copied())
517 .collect();
518 image_opt.and_then(|image| Permutation::from_image(image).ok())
519 }
520
521 fn permute(&self, perm: &Permutation<Self::Rank>) -> Result<Self, anyhow::Error> {
522 let mut p_cartorder = self.clone();
523 p_cartorder.permute_mut(perm)?;
524 Ok(p_cartorder)
525 }
526
527 fn permute_mut(&mut self, perm: &Permutation<Self::Rank>) -> Result<(), anyhow::Error> {
528 permute_inplace(&mut self.cart_tuples, perm);
529 Ok(())
530 }
531}
532
533pub(crate) fn cart_tuple_to_str(cart_tuple: &(u32, u32, u32), flat: bool) -> String {
546 if cart_tuple.0 + cart_tuple.1 + cart_tuple.2 == 0u32 {
547 "1".to_string()
548 } else {
549 let cart_array = [cart_tuple.0, cart_tuple.1, cart_tuple.2];
550 let carts = ["x", "y", "z"];
551 Itertools::intersperse(
552 cart_array.iter().enumerate().map(|(i, &l)| {
553 if flat {
554 carts[i].repeat(l as usize)
555 } else {
556 match l.cmp(&1) {
557 Ordering::Greater => format!("{}^{l}", carts[i]),
558 Ordering::Equal => carts[i].to_string(),
559 Ordering::Less => String::new(),
560 }
561 }
562 }),
563 String::new(),
564 )
565 .collect::<String>()
566 }
567}
568
569#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
578pub enum SpinorParticleType {
579 Fermion(Option<SpinorBalanceSymmetry>),
582
583 Antifermion(Option<SpinorBalanceSymmetry>),
586}
587
588impl fmt::Display for SpinorParticleType {
589 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
590 match self {
591 SpinorParticleType::Fermion(None) => {
592 write!(f, "fermion")
593 }
594 SpinorParticleType::Fermion(Some(sbs)) => {
595 write!(f, "fermion ({sbs})")
596 }
597 SpinorParticleType::Antifermion(None) => {
598 write!(f, "antifermion")
599 }
600 SpinorParticleType::Antifermion(Some(sbs)) => {
601 write!(f, "antifermion ({sbs})")
602 }
603 }
604 }
605}
606
607#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
612pub enum SpinorBalanceSymmetry {
613 KineticBalance,
615}
616
617impl fmt::Display for SpinorBalanceSymmetry {
618 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619 match self {
620 SpinorBalanceSymmetry::KineticBalance => {
621 write!(f, "σ·p")
622 }
623 }
624 }
625}
626
627#[derive(Clone, Builder, PartialEq, Eq, Hash)]
629pub struct SpinorOrder {
630 #[builder(setter(custom))]
632 pub two_j: u32,
633
634 #[builder(setter(custom))]
636 two_mjs: Vec<i32>,
637
638 pub even: bool,
642
643 pub particle_type: SpinorParticleType,
645}
646
647impl SpinorOrderBuilder {
648 fn two_j(&mut self, two_j: u32) -> &mut Self {
649 if two_j.rem_euclid(2) != 1 {
650 panic!("`two_j` must be odd.")
651 }
652 self.two_j = Some(two_j);
653 self
654 }
655
656 fn two_mjs(&mut self, two_mjs: &[i32]) -> &mut Self {
657 let two_j = self.two_j.expect("`two_j` has not been set.");
658 let max_two_m = two_mjs
659 .iter()
660 .map(|two_m| two_m.unsigned_abs())
661 .max()
662 .expect("The maximum |2m| value could not be determined.");
663 assert_eq!(
664 max_two_m, two_j,
665 "The maximum |2m| value does not equal 2j = {two_j}."
666 );
667 assert_eq!(
668 two_mjs.len(),
669 (two_j + 1) as usize,
670 "The number of 2m values specified does not equal 2j + 1 = {}",
671 two_j + 1
672 );
673 self.two_mjs = Some(two_mjs.to_vec());
674 self
675 }
676}
677
678impl SpinorOrder {
679 pub fn builder() -> SpinorOrderBuilder {
681 SpinorOrderBuilder::default()
682 }
683
684 pub fn new(
687 two_mjs: &[i32],
688 even: bool,
689 particle_type: SpinorParticleType,
690 ) -> Result<Self, anyhow::Error> {
691 let two_j = two_mjs
692 .iter()
693 .map(|two_m| two_m.unsigned_abs())
694 .max()
695 .ok_or_else(|| format_err!("The maximum |2m| value could not be determined."))?;
696 let spinor_order = SpinorOrder::builder()
697 .two_j(two_j)
698 .two_mjs(two_mjs)
699 .even(even)
700 .particle_type(particle_type)
701 .build()
702 .map_err(|err| format_err!(err))?;
703 ensure!(spinor_order.verify(), "Invalid `SpinorOrder`.");
704 Ok(spinor_order)
705 }
706
707 #[must_use]
721 pub fn increasingm(two_j: u32, even: bool, particle_type: SpinorParticleType) -> Self {
722 let two_j_i32 = i32::try_from(two_j).expect("`two_j` cannot be converted to `i32`.");
723 let two_mjs = (-two_j_i32..=two_j_i32).step_by(2).collect_vec();
724 Self::builder()
725 .two_j(two_j)
726 .two_mjs(&two_mjs)
727 .even(even)
728 .particle_type(particle_type)
729 .build()
730 .expect("Unable to construct a `SpinorOrder` structure with increasing-m order.")
731 }
732
733 #[must_use]
747 pub fn decreasingm(two_j: u32, even: bool, particle_type: SpinorParticleType) -> Self {
748 let two_j_i32 = i32::try_from(two_j).expect("`two_j` cannot be converted to `i32`.");
749 let two_mjs = (-two_j_i32..=two_j_i32).rev().step_by(2).collect_vec();
750 Self::builder()
751 .two_j(two_j)
752 .two_mjs(&two_mjs)
753 .even(even)
754 .particle_type(particle_type)
755 .build()
756 .expect("Unable to construct a `SpinorOrder` structure with decreasing-m order.")
757 }
758
759 #[must_use]
772 pub fn molden(two_j: u32, even: bool, particle_type: SpinorParticleType) -> Self {
773 let two_j_i32 = i32::try_from(two_j).expect("`two_j` cannot be converted to `i32`.");
774 let two_mjs = (1..=two_j_i32)
775 .step_by(2)
776 .flat_map(|abs_twom| vec![abs_twom, -abs_twom])
777 .collect_vec();
778 Self::builder()
779 .two_j(two_j)
780 .two_mjs(&two_mjs)
781 .even(even)
782 .particle_type(particle_type)
783 .build()
784 .expect("Unable to construct a `SpinorOrder` structure with Molden order.")
785 }
786
787 #[must_use]
793 pub fn verify(&self) -> bool {
794 let two_mjs_set = self.two_mjs.iter().collect::<HashSet<_>>();
795 let two_j = self.two_j;
796 two_mjs_set.len() == self.ncomps()
797 && two_mjs_set
798 .iter()
799 .all(|two_m| two_m.unsigned_abs() <= two_j)
800 }
801
802 pub fn iter(&'_ self) -> Iter<'_, i32> {
804 self.two_mjs.iter()
805 }
806
807 pub fn ncomps(&self) -> usize {
809 let two_j = usize::try_from(self.two_j).unwrap_or_else(|_| {
810 panic!(
811 "Unable to convert the two-j value {} to `usize`.",
812 self.two_j
813 )
814 });
815 two_j + 1
816 }
817
818 pub fn get_two_m_with_index(&self, i: usize) -> Option<i32> {
820 self.two_mjs.get(i).cloned()
821 }
822
823 pub fn l(&self) -> u32 {
826 if self.two_j.rem_euclid(4) == 1 {
827 if self.even {
829 (self.two_j - 1).div_euclid(2)
830 } else {
831 (self.two_j + 1).div_euclid(2)
832 }
833 } else {
834 assert_eq!(self.two_j.rem_euclid(4), 3);
835 if self.even {
837 (self.two_j + 1).div_euclid(2)
838 } else {
839 (self.two_j - 1).div_euclid(2)
840 }
841 }
842 }
843
844 pub fn a(&self) -> i8 {
849 if self.two_j.rem_euclid(4) == 1 {
850 if self.even { -1 } else { 1 }
852 } else {
853 assert_eq!(self.two_j.rem_euclid(4), 3);
854 if self.even { 1 } else { -1 }
856 }
857 }
858}
859
860impl fmt::Display for SpinorOrder {
861 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
862 writeln!(
863 f,
864 "Angular momentum: {}/2 ({}; l = {}), {}",
865 self.two_j,
866 if self.a() == 1 { "+" } else { "-" },
867 self.l(),
868 self.particle_type,
869 )?;
870 writeln!(f, "Order:")?;
871 for two_m in self.iter() {
872 writeln!(f, " {two_m}/2")?;
873 }
874 Ok(())
875 }
876}
877
878impl fmt::Debug for SpinorOrder {
879 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
880 writeln!(
881 f,
882 "Angular momentum: {}/2 ({}; l = {}), {}",
883 self.two_j,
884 if self.a() == 1 { "+" } else { "-" },
885 self.l(),
886 self.particle_type,
887 )?;
888 writeln!(f, "Order:")?;
889 for two_m in self.iter() {
890 writeln!(f, " {two_m:?}/2")?;
891 }
892 Ok(())
893 }
894}
895
896impl PermutableCollection for SpinorOrder {
897 type Rank = usize;
898
899 fn get_perm_of(&self, other: &Self) -> Option<Permutation<Self::Rank>> {
900 let o_two_mjs: HashMap<&i32, usize> = other
901 .two_mjs
902 .iter()
903 .enumerate()
904 .map(|(i, o_two_m)| (o_two_m, i))
905 .collect();
906 let image_opt: Option<Vec<Self::Rank>> = self
907 .two_mjs
908 .iter()
909 .map(|s_two_m| o_two_mjs.get(s_two_m).copied())
910 .collect();
911 image_opt.and_then(|image| Permutation::from_image(image).ok())
912 }
913
914 fn permute(&self, perm: &Permutation<Self::Rank>) -> Result<Self, anyhow::Error> {
915 let mut p_pureorder = self.clone();
916 p_pureorder.permute_mut(perm)?;
917 Ok(p_pureorder)
918 }
919
920 fn permute_mut(&mut self, perm: &Permutation<Self::Rank>) -> Result<(), anyhow::Error> {
921 permute_inplace(&mut self.two_mjs, perm);
922 Ok(())
923 }
924}
925
926#[derive(Clone, PartialEq, Eq, Hash, Debug)]
933pub enum ShellOrder {
934 Pure(PureOrder),
937
938 Cart(CartOrder),
941
942 Spinor(SpinorOrder),
945}
946
947impl fmt::Display for ShellOrder {
948 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
949 match self {
950 ShellOrder::Pure(pure_order) => write!(
951 f,
952 "Pure ({}) ({})",
953 if pure_order.even { "g" } else { "u" },
954 pure_order.iter().map(|m| m.to_string()).join(", ")
955 ),
956 ShellOrder::Cart(cart_order) => write!(
957 f,
958 "Cart ({}) ({})",
959 if cart_order.lcart % 2 == 0 { "g" } else { "u" },
960 cart_order
961 .iter()
962 .map(|cart_tuple| { cart_tuple_to_str(cart_tuple, true) })
963 .join(", ")
964 ),
965 ShellOrder::Spinor(spinor_order) => write!(
966 f,
967 "Spinor ({}; l = {}), {}, ({})",
968 if spinor_order.a() == 1 { "+" } else { "-" },
969 spinor_order.l(),
970 spinor_order.particle_type,
971 spinor_order
972 .iter()
973 .map(|two_m| format!("{two_m}/2"))
974 .join(", ")
975 ),
976 }
977 }
978}
979
980#[derive(Clone, Builder, PartialEq, Eq, Hash, Debug)]
986pub struct BasisShell {
987 #[builder(setter(custom))]
992 pub l: u32,
993
994 #[builder(setter(custom))]
996 pub shell_order: ShellOrder,
997}
998
999impl BasisShellBuilder {
1000 fn l(&mut self, l: u32) -> &mut Self {
1001 match self.shell_order.as_ref() {
1002 Some(ShellOrder::Pure(pure_order)) => assert_eq!(pure_order.lpure, l),
1003 Some(ShellOrder::Cart(cart_order)) => assert_eq!(cart_order.lcart, l),
1004 Some(ShellOrder::Spinor(spinor_order)) => assert_eq!(spinor_order.two_j, l),
1005 None => {}
1006 }
1007 self.l = Some(l);
1008 self
1009 }
1010
1011 fn shell_order(&mut self, shl_ord: ShellOrder) -> &mut Self {
1012 match (&shl_ord, self.l) {
1013 (ShellOrder::Pure(pure_order), Some(l)) => assert_eq!(pure_order.lpure, l),
1014 (ShellOrder::Cart(cart_order), Some(l)) => assert_eq!(cart_order.lcart, l),
1015 (ShellOrder::Spinor(spinor_order), Some(l)) => assert_eq!(spinor_order.two_j, l),
1016 _ => {}
1017 }
1018 self.shell_order = Some(shl_ord);
1019 self
1020 }
1021}
1022
1023impl BasisShell {
1024 pub fn builder() -> BasisShellBuilder {
1030 BasisShellBuilder::default()
1031 }
1032
1033 pub fn new(l: u32, shl_ord: ShellOrder) -> Self {
1042 match &shl_ord {
1043 ShellOrder::Pure(pureorder) => assert_eq!(pureorder.lpure, l),
1044 ShellOrder::Cart(cartorder) => assert_eq!(cartorder.lcart, l),
1045 ShellOrder::Spinor(spinororder) => assert_eq!(spinororder.two_j, l),
1046 }
1047 BasisShell::builder()
1048 .l(l)
1049 .shell_order(shl_ord)
1050 .build()
1051 .expect("Unable to construct a `BasisShell`.")
1052 }
1053
1054 pub fn n_funcs(&self) -> usize {
1056 let lsize = self.l as usize;
1057 match self.shell_order {
1058 ShellOrder::Pure(_) => 2 * lsize + 1,
1059 ShellOrder::Cart(_) => ((lsize + 1) * (lsize + 2)).div_euclid(2),
1060 ShellOrder::Spinor(_) => lsize + 1,
1061 }
1062 }
1063}
1064
1065#[derive(Clone, Builder, PartialEq, Eq, Hash, Debug)]
1071pub struct BasisAtom<'a> {
1072 pub(crate) atom: &'a Atom,
1074
1075 #[builder(setter(custom))]
1077 pub(crate) basis_shells: Vec<BasisShell>,
1078}
1079
1080impl<'a> BasisAtomBuilder<'a> {
1081 pub(crate) fn basis_shells(&mut self, bss: &[BasisShell]) -> &mut Self {
1082 self.basis_shells = Some(bss.to_vec());
1083 self
1084 }
1085}
1086
1087impl<'a> BasisAtom<'a> {
1088 pub fn builder() -> BasisAtomBuilder<'a> {
1094 BasisAtomBuilder::default()
1095 }
1096
1097 pub fn new(atm: &'a Atom, bss: &[BasisShell]) -> Self {
1105 BasisAtom::builder()
1106 .atom(atm)
1107 .basis_shells(bss)
1108 .build()
1109 .expect("Unable to construct a `BasisAtom`.")
1110 }
1111
1112 fn n_funcs(&self) -> usize {
1114 self.basis_shells.iter().map(BasisShell::n_funcs).sum()
1115 }
1116
1117 fn shell_boundary_indices(&self) -> Vec<(usize, usize)> {
1120 self.basis_shells
1121 .iter()
1122 .scan(0, |acc, basis_shell| {
1123 let start_index = *acc;
1124 *acc += basis_shell.n_funcs();
1125 Some((start_index, *acc))
1126 })
1127 .collect::<Vec<_>>()
1128 }
1129}
1130
1131#[derive(Clone, Builder)]
1138pub struct BasisAngularOrder<'a> {
1139 #[builder(setter(custom))]
1141 pub(crate) basis_atoms: Vec<BasisAtom<'a>>,
1142}
1143
1144impl<'a> BasisAngularOrderBuilder<'a> {
1145 pub(crate) fn basis_atoms(&mut self, batms: &[BasisAtom<'a>]) -> &mut Self {
1146 self.basis_atoms = Some(batms.to_vec());
1147 self
1148 }
1149}
1150
1151impl<'a> BasisAngularOrder<'a> {
1152 #[must_use]
1158 pub fn builder() -> BasisAngularOrderBuilder<'a> {
1159 BasisAngularOrderBuilder::default()
1160 }
1161
1162 pub fn new(batms: &[BasisAtom<'a>]) -> Self {
1169 BasisAngularOrder::builder()
1170 .basis_atoms(batms)
1171 .build()
1172 .expect("Unable to construct a `BasisAngularOrder`.")
1173 }
1174
1175 pub fn n_atoms(&self) -> usize {
1177 self.basis_atoms.len()
1178 }
1179
1180 pub fn n_funcs(&self) -> usize {
1182 self.basis_atoms.iter().map(BasisAtom::n_funcs).sum()
1183 }
1184
1185 pub fn atom_boundary_indices(&self) -> Vec<(usize, usize)> {
1188 self.basis_atoms
1189 .iter()
1190 .scan(0, |acc, basis_atom| {
1191 let start_index = *acc;
1192 *acc += basis_atom.n_funcs();
1193 Some((start_index, *acc))
1194 })
1195 .collect::<Vec<_>>()
1196 }
1197
1198 pub fn shell_boundary_indices(&self) -> Vec<(usize, usize)> {
1201 let atom_boundary_indices = self.atom_boundary_indices();
1202 self.basis_atoms
1203 .iter()
1204 .zip(atom_boundary_indices)
1205 .flat_map(|(basis_atom, (atom_start, _))| {
1206 basis_atom
1207 .shell_boundary_indices()
1208 .iter()
1209 .map(|(shell_start, shell_end)| {
1210 (shell_start + atom_start, shell_end + atom_start)
1211 })
1212 .collect::<Vec<_>>()
1213 })
1214 .collect::<Vec<_>>()
1215 }
1216
1217 pub fn basis_shells(&self) -> impl Iterator<Item = &BasisShell> + '_ {
1219 self.basis_atoms
1220 .iter()
1221 .flat_map(|basis_atom| basis_atom.basis_shells.iter())
1222 }
1223
1224 #[cfg(test)]
1252 pub(crate) fn get_perm_of_functions_fixed_shells(
1253 &self,
1254 other: &Self,
1255 ) -> Result<Permutation<usize>, anyhow::Error> {
1256 if self.n_funcs() == other.n_funcs() && self.n_atoms() == other.n_atoms() {
1257 let s_shell_boundaries = self.shell_boundary_indices();
1258 let o_shell_boundaries = other.shell_boundary_indices();
1259 if s_shell_boundaries.len() == o_shell_boundaries.len() {
1260 izip!(
1261 self.basis_shells(),
1262 other.basis_shells(),
1263 s_shell_boundaries.iter(),
1264 o_shell_boundaries.iter()
1265 )
1266 .map(|(s_bs, o_bs, (s_start, s_end), (o_start, o_end))| {
1267 if (s_start, s_end) == (o_start, o_end) {
1268 let s_shl_ord = &s_bs.shell_order;
1269 let o_shl_ord = &o_bs.shell_order;
1270 match (s_shl_ord, o_shl_ord) {
1271 (ShellOrder::Pure(s_po), ShellOrder::Pure(o_po)) => Ok(
1272 s_po.get_perm_of(o_po)
1273 .unwrap()
1274 .image()
1275 .iter()
1276 .map(|x| s_start + x)
1277 .collect_vec(),
1278 ),
1279 (ShellOrder::Cart(s_co), ShellOrder::Cart(o_co)) => Ok(
1280 s_co.get_perm_of(o_co)
1281 .unwrap()
1282 .image()
1283 .iter()
1284 .map(|x| s_start + x)
1285 .collect_vec(),
1286 ),
1287 _ => Err(format_err!("At least one pair of corresponding shells have mismatched pure/cart.")),
1288 }
1289 } else {
1290 Err(format_err!("At least one pair of corresponding shells have mismatched boundary indices."))
1291 }
1292 })
1293 .collect::<Result<Vec<_>, _>>()
1294 .and_then(|image_by_shells| {
1295 let flattened_image = image_by_shells.into_iter().flatten().collect_vec();
1296 Permutation::from_image(flattened_image)
1297 })
1298 } else {
1299 Err(format_err!("Mismatched numbers of shells."))
1300 }
1301 } else {
1302 Err(format_err!("Mismatched numbers of basis functions."))
1303 }
1304 }
1305}
1306
1307impl<'a> PermutableCollection for BasisAngularOrder<'a> {
1308 type Rank = usize;
1309
1310 fn get_perm_of(&self, other: &Self) -> Option<Permutation<Self::Rank>> {
1321 let o_basis_atoms: HashMap<&BasisAtom, usize> = other
1322 .basis_atoms
1323 .iter()
1324 .enumerate()
1325 .map(|(i, basis_atom)| (basis_atom, i))
1326 .collect();
1327 let image_opt: Option<Vec<Self::Rank>> = self
1328 .basis_atoms
1329 .iter()
1330 .map(|s_basis_atom| o_basis_atoms.get(s_basis_atom).copied())
1331 .collect();
1332 image_opt.and_then(|image| Permutation::from_image(image).ok())
1333 }
1334
1335 fn permute(&self, perm: &Permutation<Self::Rank>) -> Result<Self, anyhow::Error> {
1336 let mut p_bao = self.clone();
1337 p_bao.permute_mut(perm)?;
1338 Ok(p_bao)
1339 }
1340
1341 fn permute_mut(&mut self, perm: &Permutation<Self::Rank>) -> Result<(), anyhow::Error> {
1342 permute_inplace(&mut self.basis_atoms, perm);
1343 Ok(())
1344 }
1345}
1346
1347impl<'a> fmt::Display for BasisAngularOrder<'a> {
1348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1349 let order_length = self
1350 .basis_shells()
1351 .map(|v| v.shell_order.to_string().chars().count())
1352 .max()
1353 .unwrap_or(20);
1354 let atom_index_length = self.n_atoms().to_string().chars().count();
1355 writeln!(f, "{}", "┈".repeat(17 + atom_index_length + order_length))?;
1356 writeln!(f, " {:>atom_index_length$} Atom Shell Order", "#")?;
1357 writeln!(f, "{}", "┈".repeat(17 + atom_index_length + order_length))?;
1358 for (atm_i, batm) in self.basis_atoms.iter().enumerate() {
1359 let atm = batm.atom;
1360 for (shl_i, bshl) in batm.basis_shells.iter().enumerate() {
1361 if shl_i == 0 {
1362 writeln!(
1363 f,
1364 " {:>atom_index_length$} {:<4} {:<5} {:<order_length$}",
1365 atm_i,
1366 atm.atomic_symbol,
1367 match &bshl.shell_order {
1368 ShellOrder::Pure(_) | ShellOrder::Cart(_) => ANGMOM_LABELS
1369 .get(usize::try_from(bshl.l).unwrap_or_else(|err| panic!("{err}")))
1370 .copied()
1371 .unwrap_or(&bshl.l.to_string())
1372 .to_string(),
1373 ShellOrder::Spinor(spinororder) => format!("{}/2", spinororder.two_j),
1374 },
1375 bshl.shell_order
1376 )?;
1377 } else {
1378 writeln!(
1379 f,
1380 " {:>atom_index_length$} {:<4} {:<5} {:<order_length$}",
1381 "",
1382 "",
1383 match &bshl.shell_order {
1384 ShellOrder::Pure(_) | ShellOrder::Cart(_) => ANGMOM_LABELS
1385 .get(usize::try_from(bshl.l).unwrap_or_else(|err| panic!("{err}")))
1386 .copied()
1387 .unwrap_or(&bshl.l.to_string())
1388 .to_string(),
1389 ShellOrder::Spinor(spinororder) => format!("{}/2", spinororder.two_j),
1390 },
1391 bshl.shell_order
1392 )?;
1393 }
1394 }
1395 }
1396 writeln!(f, "{}", "┈".repeat(17 + atom_index_length + order_length))?;
1397 Ok(())
1398 }
1399}
1400
1401impl<'a> PartialEq for BasisAngularOrder<'a> {
1402 fn eq(&self, other: &Self) -> bool {
1403 self.basis_atoms == other.basis_atoms
1404 }
1405}
1406
1407impl<'a> Eq for BasisAngularOrder<'a> {}
1408
1409impl<'a> Hash for BasisAngularOrder<'a> {
1410 fn hash<H: Hasher>(&self, state: &mut H) {
1411 self.basis_atoms.hash(state);
1412 }
1413}
1414
1415impl<'a> fmt::Debug for BasisAngularOrder<'a> {
1416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1417 writeln!(f, "{:?}", self.basis_atoms)
1418 }
1419}