qsym2/target/vibration/
vibration_transformation.rs1use ndarray::{Array2, Axis, LinalgScalar, ScalarOperand, concatenate, s};
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 ComplexConjugationTransformable, SpatialUnitaryTransformable, SpinUnitaryTransformable,
13 SymmetryTransformable, TimeReversalTransformable, TransformationError,
14 assemble_sh_rotation_3d_matrices, permute_array_by_atoms,
15};
16use crate::target::vibration::VibrationalCoordinate;
17
18impl<'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(|tmats| {
36 tmats
37 .iter()
38 .map(|tmat| tmat.mapv(|x| x.into()))
39 .collect::<Vec<_>>()
40 })
41 .next()
42 .ok_or_else(|| {
43 TransformationError(
44 "Unable to obtain the spherical-harmonic transformation matrices.".to_string(),
45 )
46 })?;
47 let pbao = if let Some(p) = perm {
48 vib_bao
49 .permute(p)
50 .map_err(|err| TransformationError(err.to_string()))?
51 } else {
52 vib_bao.clone()
53 };
54 let old_coeff = &self.coefficients;
55 let p_coeff = if let Some(p) = perm {
56 permute_array_by_atoms(old_coeff, p, &[Axis(0)], &vib_bao)
57 } else {
58 old_coeff.clone()
59 };
60 let t_p_blocks = pbao
61 .shell_boundary_indices()
62 .into_iter()
63 .zip(tmats.iter())
64 .map(|((shl_start, shl_end), tmat)| tmat.dot(&p_coeff.slice(s![shl_start..shl_end])))
65 .collect::<Vec<_>>();
66 let new_coefficients = concatenate(
67 Axis(0),
68 &t_p_blocks
69 .iter()
70 .map(|t_p_block| t_p_block.view())
71 .collect::<Vec<_>>(),
72 )
73 .expect("Unable to concatenate the transformed rows for the various atoms.");
74 self.coefficients = new_coefficients;
75 Ok(self)
76 }
77}
78
79impl<'a, T> SpinUnitaryTransformable for VibrationalCoordinate<'a, T>
84where
85 T: ComplexFloat + Lapack,
86{
87 fn transform_spin_mut(
92 &mut self,
93 _dmat: &Array2<Complex<f64>>,
94 ) -> Result<&mut Self, TransformationError> {
95 Ok(self)
96 }
97}
98
99impl<'a, T> ComplexConjugationTransformable for VibrationalCoordinate<'a, T>
104where
105 T: ComplexFloat + Lapack,
106{
107 fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
109 self.coefficients.mapv_inplace(|x| x.conj());
110 Ok(self)
111 }
112}
113
114impl<'a, T> TimeReversalTransformable for VibrationalCoordinate<'a, T>
118where
119 T: ComplexFloat + Lapack,
120{
121 fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
124 self.transform_cc_mut()
125 }
126}
127
128impl<'a, T> SymmetryTransformable for VibrationalCoordinate<'a, T>
132where
133 T: ComplexFloat + Lapack,
134 VibrationalCoordinate<'a, T>: SpatialUnitaryTransformable + TimeReversalTransformable,
135{
136 fn sym_permute_sites_spatial(
137 &self,
138 symop: &SymmetryOperation,
139 ) -> Result<Permutation<usize>, TransformationError> {
140 symop
141 .act_permute(&self.mol.molecule_ordinary_atoms())
142 .ok_or(TransformationError(format!(
143 "Unable to determine the atom permutation corresponding to the operation `{symop}`."
144 )))
145 }
146}
147
148fn construct_vibration_bao(mol: &'_ Molecule) -> BasisAngularOrder<'_> {
153 let bsp_c = BasisShell::new(1, ShellOrder::Cart(CartOrder::lex(1)));
154 let batms = mol
155 .atoms
156 .iter()
157 .map(|atom| BasisAtom::new(atom, &[bsp_c.clone()]))
158 .collect::<Vec<_>>();
159 BasisAngularOrder::new(&batms)
160}