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