qsym2/bindings/python/representation_analysis/
vibrational_coordinate.rs1use std::path::PathBuf;
4
5use anyhow::format_err;
6use ndarray::{Array1, Array2};
7use num_complex::Complex;
8use numpy::{PyArray1, PyArray2, PyArrayMethods, ToPyArray};
9use pyo3::exceptions::{PyIOError, PyRuntimeError};
10use pyo3::prelude::*;
11
12use crate::analysis::EigenvalueComparisonMode;
13use crate::auxiliary::molecule::Molecule;
14use crate::drivers::QSym2Driver;
15use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
16use crate::drivers::representation_analysis::vibrational_coordinate::{
17 VibrationalCoordinateRepAnalysisDriver, VibrationalCoordinateRepAnalysisParams,
18};
19use crate::drivers::representation_analysis::{
20 CharacterTableDisplay, MagneticSymmetryAnalysisKind,
21};
22use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
23use crate::io::format::qsym2_output;
24use crate::io::{QSym2FileType, read_qsym2_binary};
25use crate::symmetry::symmetry_group::{
26 MagneticRepresentedSymmetryGroup, UnitaryRepresentedSymmetryGroup,
27};
28use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
29use crate::target::vibration::VibrationalCoordinateCollection;
30
31type C128 = Complex<f64>;
32
33#[pyclass]
40#[derive(Clone)]
41pub struct PyVibrationalCoordinateCollectionReal {
42 coefficients: Array2<f64>,
44
45 frequencies: Array1<f64>,
47
48 #[pyo3(get)]
50 threshold: f64,
51}
52
53#[pymethods]
54impl PyVibrationalCoordinateCollectionReal {
55 #[new]
63 fn new(
64 coefficients: Bound<'_, PyArray2<f64>>,
65 frequencies: Bound<'_, PyArray1<f64>>,
66 threshold: f64,
67 ) -> Self {
68 Self {
69 coefficients: coefficients.to_owned_array(),
70 frequencies: frequencies.to_owned_array(),
71 threshold,
72 }
73 }
74
75 #[getter]
76 fn coefficients<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArray2<f64>>> {
77 Ok(self.coefficients.to_pyarray(py))
78 }
79
80 #[getter]
81 fn frequencies<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArray1<f64>>> {
82 Ok(self.frequencies.to_pyarray(py))
83 }
84}
85
86impl PyVibrationalCoordinateCollectionReal {
87 fn to_qsym2<'b, 'a: 'b>(
102 &'b self,
103 mol: &'a Molecule,
104 ) -> Result<VibrationalCoordinateCollection<'b, f64>, anyhow::Error> {
105 VibrationalCoordinateCollection::<f64>::builder()
106 .mol(mol)
107 .coefficients(self.coefficients.clone())
108 .frequencies(self.frequencies.clone())
109 .threshold(self.threshold)
110 .build()
111 .map_err(|err| format_err!(err))
112 }
113}
114
115#[pyclass]
124#[derive(Clone)]
125pub struct PyVibrationalCoordinateCollectionComplex {
126 coefficients: Array2<C128>,
128
129 frequencies: Array1<C128>,
131
132 #[pyo3(get)]
134 threshold: f64,
135}
136
137#[pymethods]
138impl PyVibrationalCoordinateCollectionComplex {
139 #[new]
148 fn new(
149 coefficients: Bound<'_, PyArray2<C128>>,
150 frequencies: Bound<'_, PyArray1<C128>>,
151 threshold: f64,
152 ) -> Self {
153 Self {
154 coefficients: coefficients.to_owned_array(),
155 frequencies: frequencies.to_owned_array(),
156 threshold,
157 }
158 }
159
160 #[getter]
161 fn coefficients<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArray2<C128>>> {
162 Ok(self.coefficients.to_pyarray(py))
163 }
164
165 #[getter]
166 fn frequencies<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArray1<C128>>> {
167 Ok(self.frequencies.to_pyarray(py))
168 }
169}
170
171impl PyVibrationalCoordinateCollectionComplex {
172 fn to_qsym2<'b, 'a: 'b>(
187 &'b self,
188 mol: &'a Molecule,
189 ) -> Result<VibrationalCoordinateCollection<'b, C128>, anyhow::Error> {
190 VibrationalCoordinateCollection::<C128>::builder()
191 .mol(mol)
192 .coefficients(self.coefficients.clone())
193 .frequencies(self.frequencies.clone())
194 .threshold(self.threshold)
195 .build()
196 .map_err(|err| format_err!(err))
197 }
198}
199
200#[derive(FromPyObject)]
207pub enum PyVibrationalCoordinateCollection {
208 Real(PyVibrationalCoordinateCollectionReal),
210
211 Complex(PyVibrationalCoordinateCollectionComplex),
213}
214
215#[allow(clippy::too_many_arguments)]
262#[pyfunction]
263#[pyo3(signature = (
264 inp_sym,
265 pyvibs,
266 integrality_threshold,
267 linear_independence_threshold,
268 orbit_overlap_symmetry_threshold,
269 use_magnetic_group,
270 use_double_group,
271 use_cayley_table,
272 symmetry_transformation_kind,
273 eigenvalue_comparison_mode,
274 write_character_table=true,
275 infinite_order_to_finite=None,
276 angular_function_integrality_threshold=1e-7,
277 angular_function_linear_independence_threshold=1e-7,
278 angular_function_orbit_overlap_symmetry_threshold=1e-7,
279 angular_function_max_angular_momentum=2
280))]
281pub fn rep_analyse_vibrational_coordinate_collection(
282 py: Python<'_>,
283 inp_sym: PathBuf,
284 pyvibs: PyVibrationalCoordinateCollection,
285 integrality_threshold: f64,
286 linear_independence_threshold: f64,
287 orbit_overlap_symmetry_threshold: f64,
288 use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
289 use_double_group: bool,
290 use_cayley_table: bool,
291 symmetry_transformation_kind: SymmetryTransformationKind,
292 eigenvalue_comparison_mode: EigenvalueComparisonMode,
293 write_character_table: bool,
294 infinite_order_to_finite: Option<u32>,
295 angular_function_integrality_threshold: f64,
296 angular_function_linear_independence_threshold: f64,
297 angular_function_orbit_overlap_symmetry_threshold: f64,
298 angular_function_max_angular_momentum: u32,
299) -> PyResult<()> {
300 py.detach(|| {
301 let pd_res: SymmetryGroupDetectionResult =
302 read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
303 .map_err(|err| PyIOError::new_err(err.to_string()))?;
304
305 let mut file_name = inp_sym.to_path_buf();
306 file_name.set_extension(QSym2FileType::Sym.ext());
307 qsym2_output!(
308 "Symmetry-group detection results read in from {}.",
309 file_name.display(),
310 );
311 qsym2_output!("");
312
313 let mol = &pd_res.pre_symmetry.recentred_molecule;
314
315 let afa_params = AngularFunctionRepAnalysisParams::builder()
316 .integrality_threshold(angular_function_integrality_threshold)
317 .linear_independence_threshold(angular_function_linear_independence_threshold)
318 .orbit_overlap_symmetry_threshold(angular_function_orbit_overlap_symmetry_threshold)
319 .max_angular_momentum(angular_function_max_angular_momentum)
320 .build()
321 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
322 match &pyvibs {
323 PyVibrationalCoordinateCollection::Real(pyvibs_r) => {
324 let vibs_r = pyvibs_r
325 .to_qsym2(mol)
326 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
327 let vca_params = VibrationalCoordinateRepAnalysisParams::<f64>::builder()
328 .integrality_threshold(integrality_threshold)
329 .linear_independence_threshold(linear_independence_threshold)
330 .orbit_overlap_symmetry_threshold(orbit_overlap_symmetry_threshold)
331 .use_magnetic_group(use_magnetic_group.clone())
332 .use_double_group(use_double_group)
333 .use_cayley_table(use_cayley_table)
334 .symmetry_transformation_kind(symmetry_transformation_kind)
335 .eigenvalue_comparison_mode(eigenvalue_comparison_mode)
336 .write_character_table(if write_character_table {
337 Some(CharacterTableDisplay::Symbolic)
338 } else {
339 None
340 })
341 .infinite_order_to_finite(infinite_order_to_finite)
342 .build()
343 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
344 match &use_magnetic_group {
345 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
346 let mut vca_driver = VibrationalCoordinateRepAnalysisDriver::<
347 MagneticRepresentedSymmetryGroup,
348 f64,
349 >::builder()
350 .parameters(&vca_params)
351 .angular_function_parameters(&afa_params)
352 .vibrational_coordinate_collection(&vibs_r)
353 .symmetry_group(&pd_res)
354 .build()
355 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
356 vca_driver
357 .run()
358 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
359 }
360 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
361 let mut vca_driver = VibrationalCoordinateRepAnalysisDriver::<
362 UnitaryRepresentedSymmetryGroup,
363 f64,
364 >::builder()
365 .parameters(&vca_params)
366 .angular_function_parameters(&afa_params)
367 .vibrational_coordinate_collection(&vibs_r)
368 .symmetry_group(&pd_res)
369 .build()
370 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
371 vca_driver
372 .run()
373 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
374 }
375 };
376 }
377 PyVibrationalCoordinateCollection::Complex(pyvibs_c) => {
378 let vibs_c = pyvibs_c
379 .to_qsym2(mol)
380 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
381 let vca_params = VibrationalCoordinateRepAnalysisParams::<f64>::builder()
382 .integrality_threshold(integrality_threshold)
383 .linear_independence_threshold(linear_independence_threshold)
384 .orbit_overlap_symmetry_threshold(orbit_overlap_symmetry_threshold)
385 .use_magnetic_group(use_magnetic_group.clone())
386 .use_double_group(use_double_group)
387 .use_cayley_table(use_cayley_table)
388 .symmetry_transformation_kind(symmetry_transformation_kind)
389 .eigenvalue_comparison_mode(eigenvalue_comparison_mode)
390 .write_character_table(if write_character_table {
391 Some(CharacterTableDisplay::Symbolic)
392 } else {
393 None
394 })
395 .infinite_order_to_finite(infinite_order_to_finite)
396 .build()
397 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
398 match &use_magnetic_group {
399 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
400 let mut vca_driver = VibrationalCoordinateRepAnalysisDriver::<
401 MagneticRepresentedSymmetryGroup,
402 C128,
403 >::builder()
404 .parameters(&vca_params)
405 .angular_function_parameters(&afa_params)
406 .vibrational_coordinate_collection(&vibs_c)
407 .symmetry_group(&pd_res)
408 .build()
409 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
410 vca_driver
411 .run()
412 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
413 }
414 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
415 let mut vca_driver = VibrationalCoordinateRepAnalysisDriver::<
416 UnitaryRepresentedSymmetryGroup,
417 C128,
418 >::builder()
419 .parameters(&vca_params)
420 .angular_function_parameters(&afa_params)
421 .vibrational_coordinate_collection(&vibs_c)
422 .symmetry_group(&pd_res)
423 .build()
424 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
425 vca_driver
426 .run()
427 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
428 }
429 };
430 }
431 }
432 Ok(())
433 })
434}