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 {
846 if self.two_j.rem_euclid(4) == 1 {
847 if self.even { 1 } else { -1 }
849 } else {
850 assert_eq!(self.two_j.rem_euclid(4), 3);
851 if self.even { -1 } else { 1 }
853 }
854 }
855}
856
857impl fmt::Display for SpinorOrder {
858 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
859 writeln!(
860 f,
861 "Angular momentum: {}/2 ({}; l = {}), {}",
862 self.two_j,
863 if self.a() == 1 { "+" } else { "-" },
864 self.l(),
865 self.particle_type,
866 )?;
867 writeln!(f, "Order:")?;
868 for two_m in self.iter() {
869 writeln!(f, " {two_m}/2")?;
870 }
871 Ok(())
872 }
873}
874
875impl fmt::Debug for SpinorOrder {
876 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
877 writeln!(
878 f,
879 "Angular momentum: {}/2 ({}; l = {}), {}",
880 self.two_j,
881 if self.a() == 1 { "+" } else { "-" },
882 self.l(),
883 self.particle_type,
884 )?;
885 writeln!(f, "Order:")?;
886 for two_m in self.iter() {
887 writeln!(f, " {two_m:?}/2")?;
888 }
889 Ok(())
890 }
891}
892
893impl PermutableCollection for SpinorOrder {
894 type Rank = usize;
895
896 fn get_perm_of(&self, other: &Self) -> Option<Permutation<Self::Rank>> {
897 let o_two_mjs: HashMap<&i32, usize> = other
898 .two_mjs
899 .iter()
900 .enumerate()
901 .map(|(i, o_two_m)| (o_two_m, i))
902 .collect();
903 let image_opt: Option<Vec<Self::Rank>> = self
904 .two_mjs
905 .iter()
906 .map(|s_two_m| o_two_mjs.get(s_two_m).copied())
907 .collect();
908 image_opt.and_then(|image| Permutation::from_image(image).ok())
909 }
910
911 fn permute(&self, perm: &Permutation<Self::Rank>) -> Result<Self, anyhow::Error> {
912 let mut p_pureorder = self.clone();
913 p_pureorder.permute_mut(perm)?;
914 Ok(p_pureorder)
915 }
916
917 fn permute_mut(&mut self, perm: &Permutation<Self::Rank>) -> Result<(), anyhow::Error> {
918 permute_inplace(&mut self.two_mjs, perm);
919 Ok(())
920 }
921}
922
923#[derive(Clone, PartialEq, Eq, Hash, Debug)]
930pub enum ShellOrder {
931 Pure(PureOrder),
934
935 Cart(CartOrder),
938
939 Spinor(SpinorOrder),
942}
943
944impl fmt::Display for ShellOrder {
945 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
946 match self {
947 ShellOrder::Pure(pure_order) => write!(
948 f,
949 "Pure ({}) ({})",
950 if pure_order.even { "g" } else { "u" },
951 pure_order.iter().map(|m| m.to_string()).join(", ")
952 ),
953 ShellOrder::Cart(cart_order) => write!(
954 f,
955 "Cart ({}) ({})",
956 if cart_order.lcart % 2 == 0 { "g" } else { "u" },
957 cart_order
958 .iter()
959 .map(|cart_tuple| { cart_tuple_to_str(cart_tuple, true) })
960 .join(", ")
961 ),
962 ShellOrder::Spinor(spinor_order) => write!(
963 f,
964 "Spinor ({}; l = {}), {}, ({})",
965 if spinor_order.a() == 1 { "+" } else { "-" },
966 spinor_order.l(),
967 spinor_order.particle_type,
968 spinor_order
969 .iter()
970 .map(|two_m| format!("{two_m}/2"))
971 .join(", ")
972 ),
973 }
974 }
975}
976
977#[derive(Clone, Builder, PartialEq, Eq, Hash, Debug)]
983pub struct BasisShell {
984 #[builder(setter(custom))]
989 pub l: u32,
990
991 #[builder(setter(custom))]
993 pub shell_order: ShellOrder,
994}
995
996impl BasisShellBuilder {
997 fn l(&mut self, l: u32) -> &mut Self {
998 match self.shell_order.as_ref() {
999 Some(ShellOrder::Pure(pure_order)) => assert_eq!(pure_order.lpure, l),
1000 Some(ShellOrder::Cart(cart_order)) => assert_eq!(cart_order.lcart, l),
1001 Some(ShellOrder::Spinor(spinor_order)) => assert_eq!(spinor_order.two_j, l),
1002 None => {}
1003 }
1004 self.l = Some(l);
1005 self
1006 }
1007
1008 fn shell_order(&mut self, shl_ord: ShellOrder) -> &mut Self {
1009 match (&shl_ord, self.l) {
1010 (ShellOrder::Pure(pure_order), Some(l)) => assert_eq!(pure_order.lpure, l),
1011 (ShellOrder::Cart(cart_order), Some(l)) => assert_eq!(cart_order.lcart, l),
1012 (ShellOrder::Spinor(spinor_order), Some(l)) => assert_eq!(spinor_order.two_j, l),
1013 _ => {}
1014 }
1015 self.shell_order = Some(shl_ord);
1016 self
1017 }
1018}
1019
1020impl BasisShell {
1021 pub fn builder() -> BasisShellBuilder {
1027 BasisShellBuilder::default()
1028 }
1029
1030 pub fn new(l: u32, shl_ord: ShellOrder) -> Self {
1039 match &shl_ord {
1040 ShellOrder::Pure(pureorder) => assert_eq!(pureorder.lpure, l),
1041 ShellOrder::Cart(cartorder) => assert_eq!(cartorder.lcart, l),
1042 ShellOrder::Spinor(spinororder) => assert_eq!(spinororder.two_j, l),
1043 }
1044 BasisShell::builder()
1045 .l(l)
1046 .shell_order(shl_ord)
1047 .build()
1048 .expect("Unable to construct a `BasisShell`.")
1049 }
1050
1051 pub fn n_funcs(&self) -> usize {
1053 let lsize = self.l as usize;
1054 match self.shell_order {
1055 ShellOrder::Pure(_) => 2 * lsize + 1,
1056 ShellOrder::Cart(_) => ((lsize + 1) * (lsize + 2)).div_euclid(2),
1057 ShellOrder::Spinor(_) => lsize + 1,
1058 }
1059 }
1060}
1061
1062#[derive(Clone, Builder, PartialEq, Eq, Hash, Debug)]
1068pub struct BasisAtom<'a> {
1069 pub(crate) atom: &'a Atom,
1071
1072 #[builder(setter(custom))]
1074 pub(crate) basis_shells: Vec<BasisShell>,
1075}
1076
1077impl<'a> BasisAtomBuilder<'a> {
1078 pub(crate) fn basis_shells(&mut self, bss: &[BasisShell]) -> &mut Self {
1079 self.basis_shells = Some(bss.to_vec());
1080 self
1081 }
1082}
1083
1084impl<'a> BasisAtom<'a> {
1085 pub fn builder() -> BasisAtomBuilder<'a> {
1091 BasisAtomBuilder::default()
1092 }
1093
1094 pub fn new(atm: &'a Atom, bss: &[BasisShell]) -> Self {
1102 BasisAtom::builder()
1103 .atom(atm)
1104 .basis_shells(bss)
1105 .build()
1106 .expect("Unable to construct a `BasisAtom`.")
1107 }
1108
1109 fn n_funcs(&self) -> usize {
1111 self.basis_shells.iter().map(BasisShell::n_funcs).sum()
1112 }
1113
1114 fn shell_boundary_indices(&self) -> Vec<(usize, usize)> {
1117 self.basis_shells
1118 .iter()
1119 .scan(0, |acc, basis_shell| {
1120 let start_index = *acc;
1121 *acc += basis_shell.n_funcs();
1122 Some((start_index, *acc))
1123 })
1124 .collect::<Vec<_>>()
1125 }
1126}
1127
1128#[derive(Clone, Builder)]
1135pub struct BasisAngularOrder<'a> {
1136 #[builder(setter(custom))]
1138 pub(crate) basis_atoms: Vec<BasisAtom<'a>>,
1139}
1140
1141impl<'a> BasisAngularOrderBuilder<'a> {
1142 pub(crate) fn basis_atoms(&mut self, batms: &[BasisAtom<'a>]) -> &mut Self {
1143 self.basis_atoms = Some(batms.to_vec());
1144 self
1145 }
1146}
1147
1148impl<'a> BasisAngularOrder<'a> {
1149 #[must_use]
1155 pub fn builder() -> BasisAngularOrderBuilder<'a> {
1156 BasisAngularOrderBuilder::default()
1157 }
1158
1159 pub fn new(batms: &[BasisAtom<'a>]) -> Self {
1166 BasisAngularOrder::builder()
1167 .basis_atoms(batms)
1168 .build()
1169 .expect("Unable to construct a `BasisAngularOrder`.")
1170 }
1171
1172 pub fn n_atoms(&self) -> usize {
1174 self.basis_atoms.len()
1175 }
1176
1177 pub fn n_funcs(&self) -> usize {
1179 self.basis_atoms.iter().map(BasisAtom::n_funcs).sum()
1180 }
1181
1182 pub fn atom_boundary_indices(&self) -> Vec<(usize, usize)> {
1185 self.basis_atoms
1186 .iter()
1187 .scan(0, |acc, basis_atom| {
1188 let start_index = *acc;
1189 *acc += basis_atom.n_funcs();
1190 Some((start_index, *acc))
1191 })
1192 .collect::<Vec<_>>()
1193 }
1194
1195 pub fn shell_boundary_indices(&self) -> Vec<(usize, usize)> {
1198 let atom_boundary_indices = self.atom_boundary_indices();
1199 self.basis_atoms
1200 .iter()
1201 .zip(atom_boundary_indices)
1202 .flat_map(|(basis_atom, (atom_start, _))| {
1203 basis_atom
1204 .shell_boundary_indices()
1205 .iter()
1206 .map(|(shell_start, shell_end)| {
1207 (shell_start + atom_start, shell_end + atom_start)
1208 })
1209 .collect::<Vec<_>>()
1210 })
1211 .collect::<Vec<_>>()
1212 }
1213
1214 pub fn basis_shells(&self) -> impl Iterator<Item = &BasisShell> + '_ {
1216 self.basis_atoms
1217 .iter()
1218 .flat_map(|basis_atom| basis_atom.basis_shells.iter())
1219 }
1220
1221 #[cfg(test)]
1249 pub(crate) fn get_perm_of_functions_fixed_shells(
1250 &self,
1251 other: &Self,
1252 ) -> Result<Permutation<usize>, anyhow::Error> {
1253 if self.n_funcs() == other.n_funcs() && self.n_atoms() == other.n_atoms() {
1254 let s_shell_boundaries = self.shell_boundary_indices();
1255 let o_shell_boundaries = other.shell_boundary_indices();
1256 if s_shell_boundaries.len() == o_shell_boundaries.len() {
1257 izip!(
1258 self.basis_shells(),
1259 other.basis_shells(),
1260 s_shell_boundaries.iter(),
1261 o_shell_boundaries.iter()
1262 )
1263 .map(|(s_bs, o_bs, (s_start, s_end), (o_start, o_end))| {
1264 if (s_start, s_end) == (o_start, o_end) {
1265 let s_shl_ord = &s_bs.shell_order;
1266 let o_shl_ord = &o_bs.shell_order;
1267 match (s_shl_ord, o_shl_ord) {
1268 (ShellOrder::Pure(s_po), ShellOrder::Pure(o_po)) => Ok(
1269 s_po.get_perm_of(o_po)
1270 .unwrap()
1271 .image()
1272 .iter()
1273 .map(|x| s_start + x)
1274 .collect_vec(),
1275 ),
1276 (ShellOrder::Cart(s_co), ShellOrder::Cart(o_co)) => Ok(
1277 s_co.get_perm_of(o_co)
1278 .unwrap()
1279 .image()
1280 .iter()
1281 .map(|x| s_start + x)
1282 .collect_vec(),
1283 ),
1284 _ => Err(format_err!("At least one pair of corresponding shells have mismatched pure/cart.")),
1285 }
1286 } else {
1287 Err(format_err!("At least one pair of corresponding shells have mismatched boundary indices."))
1288 }
1289 })
1290 .collect::<Result<Vec<_>, _>>()
1291 .and_then(|image_by_shells| {
1292 let flattened_image = image_by_shells.into_iter().flatten().collect_vec();
1293 Permutation::from_image(flattened_image)
1294 })
1295 } else {
1296 Err(format_err!("Mismatched numbers of shells."))
1297 }
1298 } else {
1299 Err(format_err!("Mismatched numbers of basis functions."))
1300 }
1301 }
1302}
1303
1304impl<'a> PermutableCollection for BasisAngularOrder<'a> {
1305 type Rank = usize;
1306
1307 fn get_perm_of(&self, other: &Self) -> Option<Permutation<Self::Rank>> {
1318 let o_basis_atoms: HashMap<&BasisAtom, usize> = other
1319 .basis_atoms
1320 .iter()
1321 .enumerate()
1322 .map(|(i, basis_atom)| (basis_atom, i))
1323 .collect();
1324 let image_opt: Option<Vec<Self::Rank>> = self
1325 .basis_atoms
1326 .iter()
1327 .map(|s_basis_atom| o_basis_atoms.get(s_basis_atom).copied())
1328 .collect();
1329 image_opt.and_then(|image| Permutation::from_image(image).ok())
1330 }
1331
1332 fn permute(&self, perm: &Permutation<Self::Rank>) -> Result<Self, anyhow::Error> {
1333 let mut p_bao = self.clone();
1334 p_bao.permute_mut(perm)?;
1335 Ok(p_bao)
1336 }
1337
1338 fn permute_mut(&mut self, perm: &Permutation<Self::Rank>) -> Result<(), anyhow::Error> {
1339 permute_inplace(&mut self.basis_atoms, perm);
1340 Ok(())
1341 }
1342}
1343
1344impl<'a> fmt::Display for BasisAngularOrder<'a> {
1345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1346 let order_length = self
1347 .basis_shells()
1348 .map(|v| v.shell_order.to_string().chars().count())
1349 .max()
1350 .unwrap_or(20);
1351 let atom_index_length = self.n_atoms().to_string().chars().count();
1352 writeln!(f, "{}", "┈".repeat(17 + atom_index_length + order_length))?;
1353 writeln!(f, " {:>atom_index_length$} Atom Shell Order", "#")?;
1354 writeln!(f, "{}", "┈".repeat(17 + atom_index_length + order_length))?;
1355 for (atm_i, batm) in self.basis_atoms.iter().enumerate() {
1356 let atm = batm.atom;
1357 for (shl_i, bshl) in batm.basis_shells.iter().enumerate() {
1358 if shl_i == 0 {
1359 writeln!(
1360 f,
1361 " {:>atom_index_length$} {:<4} {:<5} {:<order_length$}",
1362 atm_i,
1363 atm.atomic_symbol,
1364 match &bshl.shell_order {
1365 ShellOrder::Pure(_) | ShellOrder::Cart(_) => ANGMOM_LABELS
1366 .get(usize::try_from(bshl.l).unwrap_or_else(|err| panic!("{err}")))
1367 .copied()
1368 .unwrap_or(&bshl.l.to_string())
1369 .to_string(),
1370 ShellOrder::Spinor(spinororder) => format!("{}/2", spinororder.two_j),
1371 },
1372 bshl.shell_order
1373 )?;
1374 } else {
1375 writeln!(
1376 f,
1377 " {:>atom_index_length$} {:<4} {:<5} {:<order_length$}",
1378 "",
1379 "",
1380 match &bshl.shell_order {
1381 ShellOrder::Pure(_) | ShellOrder::Cart(_) => ANGMOM_LABELS
1382 .get(usize::try_from(bshl.l).unwrap_or_else(|err| panic!("{err}")))
1383 .copied()
1384 .unwrap_or(&bshl.l.to_string())
1385 .to_string(),
1386 ShellOrder::Spinor(spinororder) => format!("{}/2", spinororder.two_j),
1387 },
1388 bshl.shell_order
1389 )?;
1390 }
1391 }
1392 }
1393 writeln!(f, "{}", "┈".repeat(17 + atom_index_length + order_length))?;
1394 Ok(())
1395 }
1396}
1397
1398impl<'a> PartialEq for BasisAngularOrder<'a> {
1399 fn eq(&self, other: &Self) -> bool {
1400 self.basis_atoms == other.basis_atoms
1401 }
1402}
1403
1404impl<'a> Eq for BasisAngularOrder<'a> {}
1405
1406impl<'a> Hash for BasisAngularOrder<'a> {
1407 fn hash<H: Hasher>(&self, state: &mut H) {
1408 self.basis_atoms.hash(state);
1409 }
1410}
1411
1412impl<'a> fmt::Debug for BasisAngularOrder<'a> {
1413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1414 writeln!(f, "{:?}", self.basis_atoms)
1415 }
1416}