qsym2/interfaces/input/
mod.rs

1//! Human-readable QSym² input configuration.
2
3use std::path::PathBuf;
4
5use anyhow::{self, bail, Context};
6use log;
7use serde::{Deserialize, Serialize};
8
9use crate::auxiliary::molecule::Molecule;
10use crate::drivers::molecule_symmetrisation_bootstrap::MoleculeSymmetrisationBootstrapDriver;
11use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
12use crate::drivers::symmetry_group_detection::{
13    SymmetryGroupDetectionDriver, SymmetryGroupDetectionParams,
14};
15use crate::drivers::QSym2Driver;
16use crate::interfaces::input::analysis::{
17    AnalysisTarget, SlaterDeterminantSource, SlaterDeterminantSourceHandle,
18};
19#[cfg(feature = "qchem")]
20use crate::interfaces::input::analysis::{
21    VibrationalCoordinateSource, VibrationalCoordinateSourceHandle,
22};
23use crate::interfaces::InputHandle;
24use crate::io::format::qsym2_output;
25#[allow(unused_imports)]
26use crate::io::{read_qsym2_binary, QSym2FileType};
27
28pub mod analysis;
29pub mod ao_basis;
30
31#[cfg(test)]
32#[path = "input_tests.rs"]
33mod input_tests;
34
35// ===============
36// Driver controls
37// ===============
38
39// -------------------------------
40// SymmetryGroupDetectionInputKind
41// -------------------------------
42
43/// Enumerated type representing possible input kinds for symmetry-group detection from a YAML
44/// input file.
45#[derive(Clone, Serialize, Deserialize)]
46pub enum SymmetryGroupDetectionInputKind {
47    /// Variant indicating that the parameters for the symmetry-group detection driver will be
48    /// specified.
49    Parameters(SymmetryGroupDetectionParams),
50
51    /// Variant indicating that the symmetry-group detection results will be read in from a `QSym2`
52    /// [`QSym2FileType::Sym`] binary file. The associated string gives the name of the file without
53    /// its `.qsym2.sym` extension.
54    FromFile(PathBuf),
55}
56
57impl Default for SymmetryGroupDetectionInputKind {
58    fn default() -> Self {
59        SymmetryGroupDetectionInputKind::Parameters(SymmetryGroupDetectionParams::default())
60    }
61}
62
63// ==========
64// Main input
65// ==========
66
67/// Structure containing `QSym2` input parameters which can be serialised into and deserialised
68/// from a YAML input file.
69#[derive(Clone, Serialize, Deserialize)]
70pub struct Input {
71    /// Specification for symmetry-group detection. This either specifies the parameters for
72    /// symmetry-group detection, or the name of a [`QSym2FileType::Sym`] binary file containing the
73    /// symmetry-group detection results (without the `.qsym2.sym` extension).
74    pub symmetry_group_detection: SymmetryGroupDetectionInputKind,
75
76    /// Specification for analysis target.
77    pub analysis_targets: Vec<AnalysisTarget>,
78}
79
80impl InputHandle for Input {
81    /// Handles the main input structure.
82    fn handle(&self) -> Result<(), anyhow::Error> {
83        let pd_params_inp = &self.symmetry_group_detection;
84        let mut afa_params = AngularFunctionRepAnalysisParams::default();
85        self.analysis_targets.iter().map(|target| match target {
86            AnalysisTarget::MolecularSymmetry {
87                xyz,
88                symmetrisation,
89            } => {
90                qsym2_output!("");
91                log::debug!("Analysis target: Molecular symmetry");
92                let mol = Molecule::from_xyz(xyz, 1e-7);
93                if let Some(msb_params) = symmetrisation.as_ref() {
94                    log::debug!("Performing molecule symmetrisation by bootstrapping...");
95                    let mut msb_driver = MoleculeSymmetrisationBootstrapDriver::builder()
96                        .parameters(msb_params)
97                        .molecule(&mol)
98                        .build()
99                        .with_context(|| "Unable to construct a molecule symmetrisation by bootstrapping driver while handling molecular symmetry analysis target")?;
100                    msb_driver
101                        .run()
102                        .with_context(|| "Unable to execute the molecule symmetrisation by bootstrapping driver successfully while handling molecular symmetry analysis target")?;
103                    log::debug!("Performing molecule symmetrisation by bootstrapping... Done.");
104
105                    qsym2_output!("Performing symmetry-group detection on the symmetrised system...");
106                    qsym2_output!("");
107                    let pd_params = match pd_params_inp {
108                        SymmetryGroupDetectionInputKind::Parameters(pd_params) => pd_params,
109                        SymmetryGroupDetectionInputKind::FromFile(_) => {
110                            bail!(
111                                "It is pointless to provide a pre-calculated symmetry-group \
112                                detection result when only symmetry-group detection is required."
113                            )
114                        }
115                    };
116                    log::debug!(
117                        "Molecular symmetry group will be identified based on specified parameters."
118                    );
119                    let symmol = &msb_driver.result()?.symmetrised_molecule;
120                    let mut pd_driver = SymmetryGroupDetectionDriver::builder()
121                        .parameters(pd_params)
122                        .molecule(Some(symmol))
123                        .build()
124                        .with_context(|| "Unable to construct a symmetry-group detection driver while handling molecular symmetry analysis target")?;
125                    let pd_res = pd_driver
126                        .run()
127                        .with_context(|| "Unable to execute the symmetry-group detection driver successfully while handling molecular symmetry analysis target");
128                    qsym2_output!("Performing symmetry-group detection on the symmetrised system... Done.");
129                    qsym2_output!("");
130                    pd_res
131                } else {
132                    log::debug!("Performing symmetry-group detection without symmetrisation...");
133                    let pd_params = match pd_params_inp {
134                        SymmetryGroupDetectionInputKind::Parameters(pd_params) => pd_params,
135                        SymmetryGroupDetectionInputKind::FromFile(_) => {
136                            bail!(
137                                "It is pointless to provide a pre-calculated symmetry-group \
138                                detection result when only symmetry-group detection is required."
139                            )
140                        }
141                    };
142                    log::debug!(
143                        "Molecular symmetry group will be identified based on specified parameters."
144                    );
145                    let mut pd_driver = SymmetryGroupDetectionDriver::builder()
146                        .parameters(pd_params)
147                        .xyz(Some(xyz.into()))
148                        .build()
149                        .with_context(|| "Unable to construct a symmetry-group detection driver while handling molecular symmetry analysis target")?;
150                    let pd_res = pd_driver
151                        .run()
152                        .with_context(|| "Unable to execute the symmetry-group detection driver successfully while handling molecular symmetry analysis target");
153                    log::debug!("Performing symmetry-group detection without symmetrisation... Done.");
154                    pd_res
155                }
156            }
157            AnalysisTarget::RealSlaterDeterminant(sd_control) => {
158                qsym2_output!("");
159                log::debug!("Analysis target: Slater determinant");
160                let sd_source = &sd_control.source;
161                let sda_params = &sd_control.control;
162                afa_params.linear_independence_threshold = sda_params.linear_independence_threshold;
163                afa_params.integrality_threshold = sda_params.integrality_threshold;
164                match sd_source {
165                    #[cfg(feature = "qchem")]
166                    SlaterDeterminantSource::QChemArchive(qchemarchive_sd_source) => {
167                        log::debug!("Slater determinant source: Q-Chem archive");
168                        qchemarchive_sd_source
169                            .sd_source_handle(&pd_params_inp, &afa_params, &sda_params)
170                            .map(|_| ())
171                    }
172                    SlaterDeterminantSource::Binaries(binaries_sd_source) => {
173                        log::debug!("Slater determinant source: binary files");
174                        binaries_sd_source
175                            .sd_source_handle(&pd_params_inp, &afa_params, &sda_params)
176                            .map(|_| ())
177                    }
178                }
179            }
180            #[cfg(feature = "qchem")]
181            AnalysisTarget::VibrationalCoordinates(vc_control) => {
182                qsym2_output!("");
183                log::debug!("Analysis target: vibrational coordinates");
184                let vc_source = &vc_control.source;
185                let vca_params = &vc_control.control;
186                afa_params.linear_independence_threshold = vca_params.linear_independence_threshold;
187                afa_params.integrality_threshold = vca_params.integrality_threshold;
188                match vc_source {
189                    VibrationalCoordinateSource::QChemArchive(qchemarchive_vc_source) => {
190                        log::debug!("Vibrational coordinate source: Q-Chem archive");
191                        qchemarchive_vc_source
192                            .vc_source_handle(&pd_params_inp, &afa_params, &vca_params)
193                            .map(|_| ())
194                    }
195                }
196            }
197        }).collect::<Result<_, _>>()
198    }
199}
200
201impl Default for Input {
202    fn default() -> Self {
203        Input {
204            symmetry_group_detection: SymmetryGroupDetectionInputKind::default(),
205            analysis_targets: AnalysisTarget::all_default(),
206        }
207    }
208}