qsym2/target/vibration/
vibration_transformation.rs1use 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
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(|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
69impl<'a, T> SpinUnitaryTransformable for VibrationalCoordinate<'a, T>
74where
75 T: ComplexFloat + Lapack,
76{
77 fn transform_spin_mut(
82 &mut self,
83 _dmat: &Array2<Complex<f64>>,
84 ) -> Result<&mut Self, TransformationError> {
85 Ok(self)
86 }
87}
88
89impl<'a, T> ComplexConjugationTransformable for VibrationalCoordinate<'a, T>
94where
95 T: ComplexFloat + Lapack,
96{
97 fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
99 self.coefficients.mapv_inplace(|x| x.conj());
100 Ok(self)
101 }
102}
103
104impl<'a, T> TimeReversalTransformable for VibrationalCoordinate<'a, T>
108where
109 T: ComplexFloat + Lapack,
110{
111 fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
114 self.transform_cc_mut()
115 }
116}
117
118impl<'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
138fn 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}