qsym2/drivers/representation_analysis/
mod.rs

1//! Drivers for symmetry analysis via representation and corepresentation theories.
2
3use std::fmt;
4
5#[cfg(feature = "python")]
6use pyo3::prelude::*;
7use serde::{Deserialize, Serialize};
8
9use crate::basis::ao::BasisAngularOrder;
10use crate::group::class::ClassPropertiesSummary;
11use crate::io::format::{log_subtitle, qsym2_output, QSym2Output};
12
13pub mod angular_function;
14pub mod density;
15pub mod multideterminant;
16pub mod slater_determinant;
17pub mod vibrational_coordinate;
18
19// ================
20// Enum definitions
21// ================
22
23/// Enumerated type indicating the format of character table print-out.
24#[derive(Clone, Debug, Deserialize, Serialize)]
25pub enum CharacterTableDisplay {
26    /// Prints the character table symbolically showing explicitly the roots of unity.
27    Symbolic,
28
29    /// Prints the character table numerically where each character is a complex number.
30    Numerical,
31}
32
33impl fmt::Display for CharacterTableDisplay {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        match self {
36            CharacterTableDisplay::Symbolic => write!(f, "Symbolic"),
37            CharacterTableDisplay::Numerical => write!(f, "Numerical"),
38        }
39    }
40}
41
42/// Enumerated type indicating the type of magnetic symmetry to be used for representation
43/// analysis.
44#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
45#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
46pub enum MagneticSymmetryAnalysisKind {
47    /// Variant indicating that unitary representations should be used for magnetic symmetry
48    /// analysis.
49    Representation,
50
51    /// Variant indicating that magnetic corepresentations should be used for magnetic symmetry
52    /// analysis.
53    Corepresentation,
54}
55
56impl fmt::Display for MagneticSymmetryAnalysisKind {
57    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58        match self {
59            MagneticSymmetryAnalysisKind::Representation => write!(f, "Unitary representations"),
60            MagneticSymmetryAnalysisKind::Corepresentation => {
61                write!(f, "Magnetic corepresentations")
62            }
63        }
64    }
65}
66
67// =================
68// Utility functions
69// =================
70
71/// Logs basis angular order information nicely.
72///
73/// # Arguments
74///
75/// * `bao` - The basis angular order information structure.
76fn log_bao(bao: &BasisAngularOrder) {
77    log_subtitle("Basis angular order");
78    qsym2_output!("");
79    "The basis angular order information dictates how basis functions in each basis shell are transformed.\n\
80    It is important to check that this is consistent with the basis set being used, otherwise incorrect\n\
81    symmetry results will be obtained.".log_output_display();
82    bao.log_output_display();
83    qsym2_output!("");
84}
85
86/// Logs a conjugacy class transversal of a group nicely.
87///
88/// # Arguments
89///
90/// * `group` - A group for which a conjugacy class transversal should be logged nicely.
91pub(crate) fn log_cc_transversal<G>(group: &G)
92where
93    G: ClassPropertiesSummary,
94    G::GroupElement: fmt::Display,
95{
96    log_subtitle("Conjugacy class transversal");
97    qsym2_output!("");
98    group.class_transversal_to_string().log_output_display();
99    qsym2_output!("");
100}
101
102// =================
103// Macro definitions
104// =================
105
106macro_rules! fn_construct_unitary_group {
107    ( $(#[$meta:meta])* $vis:vis $func:ident ) => {
108        $(#[$meta])*
109        $vis fn $func(&self) -> Result<UnitaryRepresentedSymmetryGroup, anyhow::Error> {
110            let params = self.parameters;
111            let sym = match params.use_magnetic_group {
112                Some(MagneticSymmetryAnalysisKind::Representation) => self.symmetry_group
113                    .magnetic_symmetry
114                    .as_ref()
115                    .ok_or_else(|| {
116                        format_err!(
117                            "Magnetic symmetry requested for analysis, but no magnetic symmetry found."
118                        )
119                    })?,
120                Some(MagneticSymmetryAnalysisKind::Corepresentation) => bail!("Magnetic corepresentations requested, but unitary-represented group is being constructed."),
121                None => &self.symmetry_group.unitary_symmetry
122            };
123            let group = if params.use_double_group {
124                UnitaryRepresentedGroup::from_molecular_symmetry(sym, params.infinite_order_to_finite)?
125                    .to_double_group()?
126            } else {
127                UnitaryRepresentedGroup::from_molecular_symmetry(sym, params.infinite_order_to_finite)?
128            };
129
130            qsym2_output!(
131                "Unitary-represented group for representation analysis: {}",
132                group.name()
133            );
134            qsym2_output!("");
135            if let Some(chartab_display) = params.write_character_table.as_ref() {
136                log_subtitle("Character table of irreducible representations");
137                qsym2_output!("");
138                match chartab_display {
139                    CharacterTableDisplay::Symbolic => {
140                        group.character_table().log_output_debug();
141                        "Any `En` in a character value denotes the first primitive n-th root of unity:\n  \
142                        En = exp(2πi/n)".log_output_display();
143                    }
144                    CharacterTableDisplay::Numerical => group.character_table().log_output_display(),
145                }
146                qsym2_output!("");
147                qsym2_output!("The symbol `◈` indicates the principal class of the group.");
148                qsym2_output!("");
149                "Note 1: `FS` contains the classification of the irreps using the Frobenius--Schur indicator:\n  \
150                `r` = real: the irrep and its complex-conjugate partner are real and identical,\n  \
151                `c` = complex: the irrep and its complex-conjugate partner are complex and inequivalent,\n  \
152                `q` = quaternion: the irrep and its complex-conjugate partner are complex and equivalent.\n\n\
153                Note 2: The conjugacy classes are sorted according to the following order:\n  \
154                E -> C_n (n descending) -> C2 -> i -> S_n (n decending) -> σ\n  \
155                Within each order and power, elements with axes close to Cartesian axes are put first.\n  \
156                Within each equi-inclination from Cartesian axes, z-inclined axes are put first, then y, then x.\n\n\
157                Note 3: The Mulliken labels generated for the irreps in the table above are internally consistent.\n  \
158                However, certain labels might differ from those tabulated elsewhere using other conventions.\n  \
159                If need be, please check with other literature to ensure external consistency.".log_output_display();
160                qsym2_output!("");
161            }
162            Ok(group)
163        }
164    }
165}
166
167macro_rules! fn_construct_magnetic_group {
168    ( $(#[$meta:meta])* $vis:vis $func:ident ) => {
169        $(#[$meta])*
170        $vis fn $func(&self) -> Result<MagneticRepresentedSymmetryGroup, anyhow::Error> {
171            let params = self.parameters;
172            let sym = match params.use_magnetic_group {
173                Some(MagneticSymmetryAnalysisKind::Corepresentation) => self.symmetry_group
174                    .magnetic_symmetry
175                    .as_ref()
176                    .ok_or_else(|| {
177                        format_err!(
178                            "Magnetic symmetry requested for analysis, but no magnetic symmetry found."
179                        )
180                    })?,
181                Some(MagneticSymmetryAnalysisKind::Representation) => bail!("Unitary representations requested, but magnetic-represented group is being constructed."),
182                None => &self.symmetry_group.unitary_symmetry
183            };
184            let group = if params.use_double_group {
185                MagneticRepresentedGroup::from_molecular_symmetry(sym, params.infinite_order_to_finite)?
186                    .to_double_group()?
187            } else {
188                MagneticRepresentedGroup::from_molecular_symmetry(sym, params.infinite_order_to_finite)?
189            };
190
191            qsym2_output!(
192                "Magnetic-represented group for corepresentation analysis: {}",
193                group.name()
194            );
195            qsym2_output!("");
196
197            if let Some(chartab_display) = params.write_character_table.as_ref() {
198                log_subtitle("Character table of irreducible corepresentations");
199                qsym2_output!("");
200                match chartab_display {
201                    CharacterTableDisplay::Symbolic => {
202                        group.character_table().log_output_debug();
203                        "Any `En` in a character value denotes the first primitive n-th root of unity:\n  \
204                        En = exp(2πi/n)".log_output_display();
205                    }
206                    CharacterTableDisplay::Numerical => group.character_table().log_output_display(),
207                }
208                qsym2_output!("");
209                qsym2_output!("The symbol `◈` indicates the principal class of the group.");
210                qsym2_output!("");
211                "Note 1: The ircorep notation `D[Δ]` means that this ircorep is induced by the representation Δ\n  \
212                of the unitary halving subgroup. The exact nature of Δ determines the kind of D[Δ].\n\n\
213                Note 2: `IN` shows the intertwining numbers of the ircoreps which classify them into three kinds:\n  \
214                `1` = 1st kind: the ircorep is induced by a single irrep of the unitary halving subgroup once,\n  \
215                `4` = 2nd kind: the ircorep is induced by a single irrep of the unitary halving subgroup twice,\n  \
216                `2` = 3rd kind: the ircorep is induced by an irrep of the unitary halving subgroup and its Wigner conjugate.\n\n\
217                Note 3: Only unitary-represented elements are shown in the character table, as characters of\n  \
218                antiunitary-represented elements are not invariant under a change of basis.\n\n\
219                Refs:\n  \
220                Newmarch, J. D. & Golding, R. M. J. Math. Phys. 23, 695–704 (1982)\n  \
221                Bradley, C. J. & Davies, B. L. Rev. Mod. Phys. 40, 359–379 (1968)\n  \
222                Newmarch, J. D. J. Math. Phys. 24, 742–756 (1983)".log_output_display();
223                qsym2_output!("");
224            }
225
226            Ok(group)
227        }
228    }
229}
230
231pub(crate) use fn_construct_magnetic_group;
232pub(crate) use fn_construct_unitary_group;