qsym2/target/vibration/
vibration_transformation.rs

1//! Implementation of symmetry transformations for vibrational coordinates.
2
3use ndarray::{concatenate, s, Array2, Axis, LinalgScalar, ScalarOperand};
4use ndarray_linalg::types::Lapack;
5use num_complex::{Complex, ComplexFloat};
6
7use crate::auxiliary::molecule::Molecule;
8use crate::basis::ao::{BasisAngularOrder, BasisAtom, BasisShell, CartOrder, ShellOrder};
9use crate::permutation::{IntoPermutation, PermutableCollection, Permutation};
10use crate::symmetry::symmetry_element::SymmetryOperation;
11use crate::symmetry::symmetry_transformation::{
12    assemble_sh_rotation_3d_matrices, permute_array_by_atoms, ComplexConjugationTransformable,
13    SpatialUnitaryTransformable, SpinUnitaryTransformable, SymmetryTransformable,
14    TimeReversalTransformable, TransformationError,
15};
16use crate::target::vibration::VibrationalCoordinate;
17
18// ---------------------------
19// SpatialUnitaryTransformable
20// ---------------------------
21impl<'a, T> SpatialUnitaryTransformable for VibrationalCoordinate<'a, T>
22where
23    T: ComplexFloat + LinalgScalar + ScalarOperand + Copy + Lapack,
24    f64: Into<T>,
25{
26    fn transform_spatial_mut(
27        &mut self,
28        rmat: &Array2<f64>,
29        perm: Option<&Permutation<usize>>,
30    ) -> Result<&mut Self, TransformationError> {
31        let vib_bao = construct_vibration_bao(self.mol);
32        let tmats: Vec<Array2<T>> = assemble_sh_rotation_3d_matrices(&vib_bao, rmat, perm)
33            .map_err(|err| TransformationError(err.to_string()))?
34            .iter()
35            .map(|tmat| tmat.map(|&x| x.into()))
36            .collect();
37        let pbao = if let Some(p) = perm {
38            vib_bao
39                .permute(p)
40                .map_err(|err| TransformationError(err.to_string()))?
41        } else {
42            vib_bao.clone()
43        };
44        let old_coeff = &self.coefficients;
45        let p_coeff = if let Some(p) = perm {
46            permute_array_by_atoms(old_coeff, p, &[Axis(0)], &vib_bao)
47        } else {
48            old_coeff.clone()
49        };
50        let t_p_blocks = pbao
51            .shell_boundary_indices()
52            .into_iter()
53            .zip(tmats.iter())
54            .map(|((shl_start, shl_end), tmat)| tmat.dot(&p_coeff.slice(s![shl_start..shl_end])))
55            .collect::<Vec<_>>();
56        let new_coefficients = concatenate(
57            Axis(0),
58            &t_p_blocks
59                .iter()
60                .map(|t_p_block| t_p_block.view())
61                .collect::<Vec<_>>(),
62        )
63        .expect("Unable to concatenate the transformed rows for the various atoms.");
64        self.coefficients = new_coefficients;
65        Ok(self)
66    }
67}
68
69// ------------------------
70// SpinUnitaryTransformable
71// ------------------------
72
73impl<'a, T> SpinUnitaryTransformable for VibrationalCoordinate<'a, T>
74where
75    T: ComplexFloat + Lapack,
76{
77    /// Performs a spin transformation in-place.
78    ///
79    /// This has no effects on the vibrational coordinate as vibrational coordinates are entirely
80    /// spatial.
81    fn transform_spin_mut(
82        &mut self,
83        _dmat: &Array2<Complex<f64>>,
84    ) -> Result<&mut Self, TransformationError> {
85        Ok(self)
86    }
87}
88
89// -------------------------------
90// ComplexConjugationTransformable
91// -------------------------------
92
93impl<'a, T> ComplexConjugationTransformable for VibrationalCoordinate<'a, T>
94where
95    T: ComplexFloat + Lapack,
96{
97    /// Performs a complex conjugation in-place.
98    fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
99        self.coefficients.mapv_inplace(|x| x.conj());
100        Ok(self)
101    }
102}
103
104// -------------------------
105// TimeReversalTransformable
106// -------------------------
107impl<'a, T> TimeReversalTransformable for VibrationalCoordinate<'a, T>
108where
109    T: ComplexFloat + Lapack,
110{
111    /// Provides a custom implementation of time reversal where the components of the vibrational
112    /// coordinates are complex-conjugated to respect the antiunitarity of time reversal.
113    fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
114        self.transform_cc_mut()
115    }
116}
117
118// ---------------------
119// SymmetryTransformable
120// ---------------------
121impl<'a, T> SymmetryTransformable for VibrationalCoordinate<'a, T>
122where
123    T: ComplexFloat + Lapack,
124    VibrationalCoordinate<'a, T>: SpatialUnitaryTransformable + TimeReversalTransformable,
125{
126    fn sym_permute_sites_spatial(
127        &self,
128        symop: &SymmetryOperation,
129    ) -> Result<Permutation<usize>, TransformationError> {
130        symop
131            .act_permute(&self.mol.molecule_ordinary_atoms())
132            .ok_or(TransformationError(format!(
133            "Unable to determine the atom permutation corresponding to the operation `{symop}`."
134        )))
135    }
136}
137
138// ---------
139// Functions
140// ---------
141
142fn construct_vibration_bao(mol: &Molecule) -> BasisAngularOrder {
143    let bsp_c = BasisShell::new(1, ShellOrder::Cart(CartOrder::lex(1)));
144    let batms = mol
145        .atoms
146        .iter()
147        .map(|atom| BasisAtom::new(atom, &[bsp_c.clone()]))
148        .collect::<Vec<_>>();
149    BasisAngularOrder::new(&batms)
150}