Skip to main content

qsym2/bindings/python/representation_analysis/multideterminant/
multideterminant_eager_basis.rs

1//! Python bindings for QSym² symmetry analysis of multi-determinants with eager bases.
2
3use std::collections::HashSet;
4use std::path::PathBuf;
5
6use anyhow::bail;
7use num_complex::Complex;
8use numpy::PyArrayMethods;
9use pyo3::exceptions::{PyIOError, PyRuntimeError};
10use pyo3::prelude::*;
11
12use crate::analysis::EigenvalueComparisonMode;
13use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled};
14use crate::bindings::python::integrals::{
15    PyBasisAngularOrder, PyStructureConstraint,
16};
17use crate::bindings::python::representation_analysis::slater_determinant::PySlaterDeterminant;
18use crate::bindings::python::representation_analysis::{PyArray1RC, PyArray2RC};
19use crate::drivers::QSym2Driver;
20use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
21use crate::drivers::representation_analysis::multideterminant::{
22    MultiDeterminantRepAnalysisDriver, MultiDeterminantRepAnalysisParams,
23};
24use crate::drivers::representation_analysis::{
25    CharacterTableDisplay, MagneticSymmetryAnalysisKind,
26};
27use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
28use crate::io::format::qsym2_output;
29use crate::io::{QSym2FileType, read_qsym2_binary};
30use crate::symmetry::symmetry_group::{
31    MagneticRepresentedSymmetryGroup, UnitaryRepresentedSymmetryGroup,
32};
33use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
34use crate::target::determinant::SlaterDeterminant;
35use crate::target::noci::basis::EagerBasis;
36use crate::target::noci::multideterminant::MultiDeterminant;
37use crate::target::noci::multideterminants::MultiDeterminants;
38
39type C128 = Complex<f64>;
40
41/// Python-exposed function to perform representation symmetry analysis for real and complex
42/// multi-determinantal wavefunctions constructed from an eager basis of Slater determinants and log
43/// the result via the `qsym2-output` logger at the `INFO` level.
44///
45/// If `symmetry_transformation_kind` includes spin transformation, the provided
46/// multi-determinantal wavefunctions will be augmented to generalised spin constraint
47/// automatically.
48///
49/// # Arguments
50///
51/// * `inp_sym` - A path to the [`QSym2FileType::Sym`] file containing the symmetry-group detection
52/// result for the system. This will be used to construct abstract groups and character tables for
53/// representation analysis.
54/// * `pydets` - A list of Python-exposed Slater determinants whose coefficients are of type
55/// `float64` or `complex128`. These determinants serve as basis states for non-orthogonal
56/// configuration interaction to yield multi-determinantal wavefunctions, the symmetry of which will
57/// be analysed by this function.
58/// * `coefficients` - The coefficient matrix where each column gives the linear combination
59/// coefficients for one multi-determinantal wavefunction. The number of rows must match the number
60/// of determinants specified in `pydets`. The elements are of type `float64` or `complex128`.
61/// * `energies` - The `float64` or `complex128` energies of the multi-determinantal wavefunctions.
62/// The number of terms must match the number of columns of `coefficients`.
63/// * `pybaos` - Python-exposed structures containing basis angular order information, one for each
64/// explicit component per coefficient matrix.
65/// * `integrality_threshold` - The threshold for verifying if subspace multiplicities are
66/// integral.
67/// * `linear_independence_threshold` - The threshold for determining the linear independence
68/// subspace via the non-zero eigenvalues of the orbit overlap matrix.
69/// * `orbit_overlap_symmetry_threshold` - The threshold for checking the symmetry of the orbit
70/// overlap matrix.
71/// * `use_magnetic_group` - An option indicating if the magnetic group is to be used for symmetry
72/// analysis, and if so, whether unitary representations or unitary-antiunitary corepresentations
73/// should be used.
74/// * `use_double_group` - A boolean indicating if the double group of the prevailing symmetry
75/// group is to be used for representation analysis instead.
76/// * `use_cayley_table` - A boolean indicating if the Cayley table for the group, if available,
77/// should be used to speed up the calculation of orbit overlap matrices.
78/// * `symmetry_transformation_kind` - An enumerated type indicating the type of symmetry
79/// transformations to be performed on the origin determinant to generate the orbit. If this
80/// contains spin transformation, the multi-determinant will be augmented to generalised spin
81/// constraint automatically.
82/// * `eigenvalue_comparison_mode` - An enumerated type indicating the mode of comparison of orbit
83/// overlap eigenvalues with the specified `linear_independence_threshold`.
84/// * `sao` - The atomic-orbital overlap matrix whose elements are of type `float64` or
85/// `complex128`.
86/// * `sao_h` - The optional complex-symmetric atomic-orbital overlap matrix whose elements
87/// are of type `float64` or `complex128`. This is required if antiunitary symmetry operations are
88/// involved.
89/// * `write_overlap_eigenvalues` - A boolean indicating if the eigenvalues of the determinant
90/// orbit overlap matrix are to be written to the output.
91/// * `write_character_table` - A boolean indicating if the character table of the prevailing
92/// symmetry group is to be printed out.
93/// * `infinite_order_to_finite` - The finite order with which infinite-order generators are to be
94/// interpreted to form a finite subgroup of the prevailing infinite group. This finite subgroup
95/// will be used for symmetry analysis.
96/// * `angular_function_integrality_threshold` - The threshold for verifying if subspace
97/// multiplicities are integral for the symmetry analysis of angular functions.
98/// * `angular_function_linear_independence_threshold` - The threshold for determining the linear
99/// independence subspace via the non-zero eigenvalues of the orbit overlap matrix for the symmetry
100/// analysis of angular functions.
101/// * `angular_function_orbit_overlap_symmetry_threshold` - The threshold for checking the symmetry
102/// of the orbit overlap matrices for the symmetry analysis of angular functions.
103/// * `angular_function_max_angular_momentum` - The maximum angular momentum order to be used in
104/// angular function symmetry analysis.
105#[allow(clippy::too_many_arguments)]
106#[pyfunction]
107#[pyo3(signature = (
108    inp_sym,
109    pydets,
110    coefficients,
111    energies,
112    pybaos,
113    integrality_threshold,
114    linear_independence_threshold,
115    orbit_overlap_symmetry_threshold,
116    use_magnetic_group,
117    use_double_group,
118    use_cayley_table,
119    symmetry_transformation_kind,
120    eigenvalue_comparison_mode,
121    sao,
122    sao_h=None,
123    write_overlap_eigenvalues=true,
124    write_character_table=true,
125    infinite_order_to_finite=None,
126    angular_function_integrality_threshold=1e-7,
127    angular_function_linear_independence_threshold=1e-7,
128    angular_function_orbit_overlap_symmetry_threshold=1e-7,
129    angular_function_max_angular_momentum=2
130))]
131pub fn rep_analyse_multideterminants_eager_basis(
132    py: Python<'_>,
133    inp_sym: PathBuf,
134    pydets: Vec<PySlaterDeterminant>,
135    coefficients: PyArray2RC,
136    energies: PyArray1RC,
137    pybaos: Vec<PyBasisAngularOrder>,
138    integrality_threshold: f64,
139    linear_independence_threshold: f64,
140    orbit_overlap_symmetry_threshold: f64,
141    use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
142    use_double_group: bool,
143    use_cayley_table: bool,
144    symmetry_transformation_kind: SymmetryTransformationKind,
145    eigenvalue_comparison_mode: EigenvalueComparisonMode,
146    sao: PyArray2RC,
147    sao_h: Option<PyArray2RC>,
148    write_overlap_eigenvalues: bool,
149    write_character_table: bool,
150    infinite_order_to_finite: Option<u32>,
151    angular_function_integrality_threshold: f64,
152    angular_function_linear_independence_threshold: f64,
153    angular_function_orbit_overlap_symmetry_threshold: f64,
154    angular_function_max_angular_momentum: u32,
155) -> PyResult<()> {
156    // Read in point-group detection results
157    let pd_res: SymmetryGroupDetectionResult =
158        read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
159            .map_err(|err| PyIOError::new_err(err.to_string()))?;
160
161    let mut file_name = inp_sym.to_path_buf();
162    file_name.set_extension(QSym2FileType::Sym.ext());
163    qsym2_output!(
164        "Symmetry-group detection results read in from {}.",
165        file_name.display(),
166    );
167    qsym2_output!("");
168
169    // Set up basic parameters
170    let mol = &pd_res.pre_symmetry.recentred_molecule;
171
172    let baos = pybaos
173        .iter()
174        .map(|bao| {
175            bao.to_qsym2(mol)
176                .map_err(|err| PyRuntimeError::new_err(err.to_string()))
177        })
178        .collect::<Result<Vec<_>, _>>()?;
179    let baos_ref = baos.iter().collect::<Vec<_>>();
180    let augment_to_generalised = match symmetry_transformation_kind {
181        SymmetryTransformationKind::SpatialWithSpinTimeReversal
182        | SymmetryTransformationKind::Spin
183        | SymmetryTransformationKind::SpinSpatial => true,
184        SymmetryTransformationKind::Spatial => false,
185    };
186    let afa_params = AngularFunctionRepAnalysisParams::builder()
187        .integrality_threshold(angular_function_integrality_threshold)
188        .linear_independence_threshold(angular_function_linear_independence_threshold)
189        .orbit_overlap_symmetry_threshold(angular_function_orbit_overlap_symmetry_threshold)
190        .max_angular_momentum(angular_function_max_angular_momentum)
191        .build()
192        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
193    let mda_params = MultiDeterminantRepAnalysisParams::<f64>::builder()
194        .integrality_threshold(integrality_threshold)
195        .linear_independence_threshold(linear_independence_threshold)
196        .orbit_overlap_symmetry_threshold(orbit_overlap_symmetry_threshold)
197        .use_magnetic_group(use_magnetic_group.clone())
198        .use_double_group(use_double_group)
199        .use_cayley_table(use_cayley_table)
200        .symmetry_transformation_kind(symmetry_transformation_kind.clone())
201        .eigenvalue_comparison_mode(eigenvalue_comparison_mode)
202        .write_overlap_eigenvalues(write_overlap_eigenvalues)
203        .write_character_table(if write_character_table {
204            Some(CharacterTableDisplay::Symbolic)
205        } else {
206            None
207        })
208        .infinite_order_to_finite(infinite_order_to_finite)
209        .build()
210        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
211
212    let all_real = pydets
213        .iter()
214        .all(|pydet| matches!(pydet, PySlaterDeterminant::Real(_)));
215
216    let structure_constraints_set = pydets
217        .iter()
218        .map(|pydet| match pydet {
219            PySlaterDeterminant::Real(pydet) => pydet.structure_constraint().clone(),
220            PySlaterDeterminant::Complex(pydet) => pydet.structure_constraint().clone(),
221        })
222        .collect::<HashSet<_>>();
223    if structure_constraints_set.len() != 1 {
224        return Err(PyRuntimeError::new_err(
225            "Inconsistent structure constraints across origin determinants.`",
226        ));
227    };
228    let structure_constraint = structure_constraints_set
229        .iter()
230        .next()
231        .ok_or_else(|| PyRuntimeError::new_err("Unable to retrieve the structure constraint."))?;
232
233    // Decision tree:
234    // - all real numerical data?
235    //   + yes:
236    //     - structure_constraint:
237    //       + SpinConstraint:
238    //         - use_magnetic_group:
239    //           + Some(Corepresentation)
240    //           + Some(Representation) | None
241    //       + SpinOrbitCoupled: not supported
242    //   - no:
243    //     - structure_constraint:
244    //       + SpinConstraint:
245    //         - use_magnetic_group:
246    //           + Some(Corepresentation)
247    //           + Some(Representation) | None
248    //       + SpinOrbitCoupled:
249    //         - use_magnetic_group:
250    //           + Some(Corepresentation)
251    //           + Some(Representation) | None
252    match (all_real, &coefficients, &energies, &sao) {
253        (
254            true,
255            PyArray2RC::Real(pycoefficients_r),
256            PyArray1RC::Real(pyenergies_r),
257            PyArray2RC::Real(pysao_r),
258        ) => {
259            // Real numeric data type
260
261            if matches!(
262                structure_constraint,
263                PyStructureConstraint::SpinOrbitCoupled(_)
264            ) {
265                return Err(PyRuntimeError::new_err(
266                    "Real determinants cannot support spin--orbit-coupled structure constraint.",
267                ));
268            }
269
270            // Preparation
271            let sao_r = pysao_r.to_owned_array();
272            let coefficients_r = pycoefficients_r.to_owned_array();
273            let energies_r = pyenergies_r.to_owned_array();
274            let dets_r = if augment_to_generalised {
275                pydets
276                    .iter()
277                    .map(|pydet| {
278                        if let PySlaterDeterminant::Real(pydet_r) = pydet {
279                            pydet_r
280                                .to_qsym2(&baos_ref, mol)
281                                .map(|det_r| det_r.to_generalised())
282                        } else {
283                            bail!("Unexpected complex type for a Slater determinant.")
284                        }
285                    })
286                    .collect::<Result<Vec<_>, _>>()
287            } else {
288                pydets
289                    .iter()
290                    .map(|pydet| {
291                        if let PySlaterDeterminant::Real(pydet_r) = pydet {
292                            pydet_r.to_qsym2(&baos_ref, mol)
293                        } else {
294                            bail!("Unexpected complex type for a Slater determinant.")
295                        }
296                    })
297                    .collect::<Result<Vec<_>, _>>()
298            }
299            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
300
301            let n_energies = energies_r.len();
302            if coefficients_r.shape()[1] != n_energies {
303                return Err(PyRuntimeError::new_err(
304                    "Mismatched number of NOCI energies and number of NOCI states.",
305                ));
306            }
307
308            // Construct the eager basis
309            let eager_basis = EagerBasis::builder()
310                .elements(dets_r)
311                .build()
312                .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
313
314            // Construct the multi-determinantal wavefunctions
315            let multidets_vec = energies_r
316                .iter()
317                .zip(coefficients_r.columns())
318                .map(|(energy, coeffs)| {
319                    MultiDeterminant::builder()
320                        .basis(eager_basis.clone())
321                        .coefficients(coeffs.to_owned())
322                        .threshold(1e-7)
323                        .energy(Ok(*energy))
324                        .build()
325                })
326                .collect::<Result<Vec<_>, _>>()
327                .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
328            let multidets = MultiDeterminants::from_multideterminant_vec(
329                &multidets_vec.iter().collect::<Vec<_>>()
330            )
331            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
332
333            // Construct the driver
334            match &use_magnetic_group {
335                Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
336                    let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
337                        MagneticRepresentedSymmetryGroup,
338                        f64,
339                        _,
340                        SpinConstraint,
341                    >::builder()
342                    .parameters(&mda_params)
343                    .angular_function_parameters(&afa_params)
344                    .multidets(&multidets)
345                    .sao(&sao_r)
346                    .sao_h(None) // Real SAO.
347                    .symmetry_group(&pd_res)
348                    .build()
349                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
350
351                    // Run the driver
352                    py.detach(|| {
353                        mda_driver
354                            .run()
355                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))
356                    })?
357                }
358                Some(MagneticSymmetryAnalysisKind::Representation) | None => {
359                    let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
360                        UnitaryRepresentedSymmetryGroup,
361                        f64,
362                        _,
363                        SpinConstraint,
364                    >::builder()
365                    .parameters(&mda_params)
366                    .angular_function_parameters(&afa_params)
367                    .multidets(&multidets)
368                    .sao(&sao_r)
369                    .sao_h(None) // Real SAO.
370                    .symmetry_group(&pd_res)
371                    .build()
372                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
373
374                    // Run the driver
375                    py.detach(|| {
376                        mda_driver
377                            .run()
378                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))
379                    })?
380                }
381            }
382        }
383        (_, _, _, _) => {
384            // Complex numeric data type
385
386            // Preparation
387            let sao_c = match sao {
388                PyArray2RC::Real(pysao_r) => pysao_r.to_owned_array().mapv(Complex::from),
389                PyArray2RC::Complex(pysao_c) => pysao_c.to_owned_array(),
390            };
391            let sao_h_c = sao_h.map(|pysao_h| match pysao_h {
392                // sao_spatial_h must have the same reality as sao_spatial.
393                PyArray2RC::Real(pysao_h_r) => pysao_h_r.to_owned_array().mapv(Complex::from),
394                PyArray2RC::Complex(pysao_h_c) => pysao_h_c.to_owned_array(),
395            });
396            let coefficients_c = match coefficients {
397                PyArray2RC::Real(pycoefficients_r) => {
398                    pycoefficients_r.to_owned_array().mapv(Complex::from)
399                }
400                PyArray2RC::Complex(pycoefficients_c) => pycoefficients_c.to_owned_array(),
401            };
402            let energies_c = match energies {
403                PyArray1RC::Real(pyenergies_r) => pyenergies_r.to_owned_array().mapv(Complex::from),
404                PyArray1RC::Complex(pyenergies_c) => pyenergies_c.to_owned_array(),
405            };
406
407            match structure_constraint {
408                PyStructureConstraint::SpinConstraint(_) => {
409                    let dets_c = if augment_to_generalised {
410                        pydets
411                            .iter()
412                            .map(|pydet| match pydet {
413                                PySlaterDeterminant::Real(pydet_r) => pydet_r
414                                    .to_qsym2::<SpinConstraint>(&baos_ref, mol)
415                                    .map(|det_r| {
416                                        SlaterDeterminant::<C128, SpinConstraint>::from(det_r)
417                                            .to_generalised()
418                                    }),
419                                PySlaterDeterminant::Complex(pydet_c) => pydet_c
420                                    .to_qsym2::<SpinConstraint>(&baos_ref, mol)
421                                    .map(|det_c| det_c.to_generalised()),
422                            })
423                            .collect::<Result<Vec<_>, _>>()
424                    } else {
425                        pydets
426                            .iter()
427                            .map(|pydet| match pydet {
428                                PySlaterDeterminant::Real(pydet_r) => pydet_r
429                                    .to_qsym2::<SpinConstraint>(&baos_ref, mol)
430                                    .map(|det_r| {
431                                        SlaterDeterminant::<C128, SpinConstraint>::from(det_r)
432                                    }),
433                                PySlaterDeterminant::Complex(pydet_c) => {
434                                    pydet_c.to_qsym2::<SpinConstraint>(&baos_ref, mol)
435                                }
436                            })
437                            .collect::<Result<Vec<_>, _>>()
438                    }
439                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
440
441                    let n_energies = energies_c.len();
442                    if coefficients_c.shape()[1] != n_energies {
443                        return Err(PyRuntimeError::new_err(
444                            "Mismatched number of NOCI energies and number of NOCI states.",
445                        ));
446                    }
447
448                    // Construct the eager basis
449                    let eager_basis = EagerBasis::builder()
450                        .elements(dets_c)
451                        .build()
452                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
453
454                    // Construct the multi-determinantal wavefunctions
455                    let multidets_vec = energies_c
456                        .iter()
457                        .zip(coefficients_c.columns())
458                        .map(|(energy, coeffs)| {
459                            MultiDeterminant::builder()
460                                .basis(eager_basis.clone())
461                                .coefficients(coeffs.to_owned())
462                                .threshold(1e-7)
463                                .energy(Ok(*energy))
464                                .build()
465                        })
466                        .collect::<Result<Vec<_>, _>>()
467                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
468                    let multidets = MultiDeterminants::from_multideterminant_vec(
469                        &multidets_vec.iter().collect::<Vec<_>>()
470                    )
471                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
472
473                    // Construct the driver
474                    match &use_magnetic_group {
475                        Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
476                            let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
477                                MagneticRepresentedSymmetryGroup,
478                                C128,
479                                _,
480                                SpinConstraint,
481                            >::builder()
482                            .parameters(&mda_params)
483                            .angular_function_parameters(&afa_params)
484                            .multidets(&multidets)
485                            .sao(&sao_c)
486                            .sao_h(sao_h_c.as_ref())
487                            .symmetry_group(&pd_res)
488                            .build()
489                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
490
491                            // Run the driver
492                            py.detach(|| {
493                                mda_driver
494                                    .run()
495                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))
496                            })?
497                        }
498                        Some(MagneticSymmetryAnalysisKind::Representation) | None => {
499                            let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
500                                UnitaryRepresentedSymmetryGroup,
501                                C128,
502                                _,
503                                SpinConstraint,
504                            >::builder()
505                            .parameters(&mda_params)
506                            .angular_function_parameters(&afa_params)
507                            .multidets(&multidets)
508                            .sao(&sao_c)
509                            .sao_h(sao_h_c.as_ref())
510                            .symmetry_group(&pd_res)
511                            .build()
512                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
513
514                            // Run the driver
515                            py.detach(|| {
516                                mda_driver
517                                    .run()
518                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))
519                            })?
520                        }
521                    }
522                }
523                PyStructureConstraint::SpinOrbitCoupled(_) => {
524                    let dets_c = pydets
525                        .iter()
526                        .map(|pydet| match pydet {
527                            PySlaterDeterminant::Real(pydet_r) => pydet_r
528                                .to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
529                                .map(|det_r| {
530                                    SlaterDeterminant::<C128, SpinOrbitCoupled>::from(det_r)
531                                }),
532                            PySlaterDeterminant::Complex(pydet_c) => {
533                                pydet_c.to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
534                            }
535                        })
536                        .collect::<Result<Vec<_>, _>>()
537                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
538
539                    let n_energies = energies_c.len();
540                    if coefficients_c.shape()[1] != n_energies {
541                        return Err(PyRuntimeError::new_err(
542                            "Mismatched number of NOCI energies and number of NOCI states.",
543                        ));
544                    }
545
546                    // Construct the eager basis
547                    let eager_basis = EagerBasis::builder()
548                        .elements(dets_c)
549                        .build()
550                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
551
552                    // Construct the multi-determinantal wavefunctions
553                    let multidets_vec = energies_c
554                        .iter()
555                        .zip(coefficients_c.columns())
556                        .map(|(energy, coeffs)| {
557                            MultiDeterminant::builder()
558                                .basis(eager_basis.clone())
559                                .coefficients(coeffs.to_owned())
560                                .threshold(1e-7)
561                                .energy(Ok(*energy))
562                                .build()
563                        })
564                        .collect::<Result<Vec<_>, _>>()
565                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
566                    let multidets = MultiDeterminants::from_multideterminant_vec(
567                        &multidets_vec.iter().collect::<Vec<_>>()
568                    )
569                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
570
571                    // Construct the driver
572                    match &use_magnetic_group {
573                        Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
574                            let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
575                                MagneticRepresentedSymmetryGroup,
576                                C128,
577                                _,
578                                SpinOrbitCoupled,
579                            >::builder()
580                            .parameters(&mda_params)
581                            .angular_function_parameters(&afa_params)
582                            .multidets(&multidets)
583                            .sao(&sao_c)
584                            .sao_h(sao_h_c.as_ref())
585                            .symmetry_group(&pd_res)
586                            .build()
587                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
588
589                            // Run the driver
590                            py.detach(|| {
591                                mda_driver
592                                    .run()
593                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))
594                            })?
595                        }
596                        Some(MagneticSymmetryAnalysisKind::Representation) | None => {
597                            let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
598                                UnitaryRepresentedSymmetryGroup,
599                                C128,
600                                _,
601                                SpinOrbitCoupled,
602                            >::builder()
603                            .parameters(&mda_params)
604                            .angular_function_parameters(&afa_params)
605                            .multidets(&multidets)
606                            .sao(&sao_c)
607                            .sao_h(sao_h_c.as_ref())
608                            .symmetry_group(&pd_res)
609                            .build()
610                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
611
612                            // Run the driver
613                            py.detach(|| {
614                                mda_driver
615                                    .run()
616                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))
617                            })?
618                        }
619                    }
620                }
621            }
622        }
623    }
624    Ok(())
625}