qsym2/bindings/python/projection/
density.rs1use std::path::PathBuf;
4
5use indexmap::IndexMap;
6use itertools::Itertools;
7use num_complex::Complex;
8use pyo3::exceptions::{PyIOError, PyRuntimeError};
9use pyo3::{IntoPyObjectExt, prelude::*};
10
11use crate::bindings::python::integrals::PyBasisAngularOrder;
12use crate::bindings::python::projection::PyProjectionTarget;
13use crate::bindings::python::representation_analysis::density::{
14 PyDensity, PyDensityComplex, PyDensityReal,
15};
16use crate::drivers::QSym2Driver;
17use crate::drivers::projection::density::{DensityProjectionDriver, DensityProjectionParams};
18use crate::drivers::representation_analysis::{
19 CharacterTableDisplay, MagneticSymmetryAnalysisKind,
20};
21use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
22use crate::io::format::qsym2_output;
23use crate::io::{QSym2FileType, read_qsym2_binary};
24use crate::symmetry::symmetry_group::UnitaryRepresentedSymmetryGroup;
25use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
26use crate::target::density::Density;
27
28type C128 = Complex<f64>;
29
30#[allow(clippy::type_complexity, clippy::too_many_arguments)]
67#[pyfunction]
68#[pyo3(signature = (
69 inp_sym,
70 pydens,
71 projection_targets,
72 pybao,
73 use_magnetic_group,
74 use_double_group,
75 symmetry_transformation_kind,
76 write_character_table=true,
77 infinite_order_to_finite=None,
78))]
79pub fn project_densities(
80 py: Python<'_>,
81 inp_sym: PathBuf,
82 pydens: Vec<(String, PyDensity)>,
83 projection_targets: Vec<PyProjectionTarget>,
84 pybao: &PyBasisAngularOrder,
85 use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
86 use_double_group: bool,
87 symmetry_transformation_kind: SymmetryTransformationKind,
88 write_character_table: bool,
89 infinite_order_to_finite: Option<u32>,
90) -> PyResult<Vec<(String, IndexMap<String, Py<PyAny>>)>> {
91 let pd_res: SymmetryGroupDetectionResult =
92 read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
93 .map_err(|err| PyIOError::new_err(err.to_string()))?;
94
95 let mut file_name = inp_sym.to_path_buf();
96 file_name.set_extension(QSym2FileType::Sym.ext());
97 qsym2_output!(
98 "Symmetry-group detection results read in from {}.",
99 file_name.display(),
100 );
101 qsym2_output!("");
102
103 let mol = &pd_res.pre_symmetry.recentred_molecule;
104 let bao = pybao
105 .to_qsym2(mol)
106 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
107
108 let symbolic_projection_targets = projection_targets
109 .iter()
110 .filter_map(|pt| match pt {
111 PyProjectionTarget::Symbolic(symbolic_pt) => Some(symbolic_pt.clone()),
112 PyProjectionTarget::Numeric(_) => None,
113 })
114 .collect_vec();
115 let numeric_projection_targets = projection_targets
116 .iter()
117 .filter_map(|pt| match pt {
118 PyProjectionTarget::Symbolic(_) => None,
119 PyProjectionTarget::Numeric(numeric_pt) => Some(*numeric_pt),
120 })
121 .collect_vec();
122
123 let dp_params = DensityProjectionParams::builder()
124 .symbolic_projection_targets(Some(symbolic_projection_targets))
125 .numeric_projection_targets(Some(numeric_projection_targets))
126 .use_magnetic_group(use_magnetic_group.clone())
127 .use_double_group(use_double_group)
128 .symmetry_transformation_kind(symmetry_transformation_kind)
129 .write_character_table(if write_character_table {
130 Some(CharacterTableDisplay::Symbolic)
131 } else {
132 None
133 })
134 .infinite_order_to_finite(infinite_order_to_finite)
135 .build()
136 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
137
138 let any_complex = pydens
139 .iter()
140 .any(|(_, pyden)| matches!(pyden, PyDensity::Complex(_)));
141
142 let projected_densities = if !any_complex {
143 let dens = pydens
145 .iter()
146 .map(|(_, pyden)| match pyden {
147 PyDensity::Real(pyden_r) => pyden_r.to_qsym2(&bao, mol),
148 PyDensity::Complex(_) => panic!("Unexpected complex density."),
149 })
150 .collect::<Result<Vec<_>, _>>()
151 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
152 let dens_ref = dens
153 .iter()
154 .zip(pydens.iter())
155 .map(|(den, (desc, _))| (desc.clone(), den))
156 .collect::<Vec<_>>();
157
158 let projected_densities = match &use_magnetic_group {
159 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
160 return Err(PyRuntimeError::new_err(
161 "Projection using corepresentations is not yet supported.",
162 ));
163 }
164 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
165 let mut dp_driver =
166 DensityProjectionDriver::<UnitaryRepresentedSymmetryGroup, f64>::builder()
167 .parameters(&dp_params)
168 .densities(dens_ref)
169 .symmetry_group(&pd_res)
170 .build()
171 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
172 py.detach(|| {
173 dp_driver
174 .run()
175 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
176 })?;
177 dp_driver
178 .result()
179 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
180 .projected_densities()
181 .clone()
182 }
183 };
184 projected_densities
185 .into_iter()
186 .map(|(name, projected_dens)| {
187 (
188 name,
189 projected_dens
190 .into_iter()
191 .map(|(row, den_res)| {
192 (
193 row.to_string(),
194 den_res
195 .and_then(|den| {
196 let pyden = PyDensityReal {
197 complex_symmetric: den.complex_symmetric(),
198 density_matrix: den.density_matrix().clone(),
199 threshold: den.threshold(),
200 };
201 pyden.into_py_any(py).map_err(|err| err.to_string())
202 })
203 .expect("Unable to convert a projected density into a Python object."),
204 )
205 })
206 .collect::<IndexMap<_, _>>(),
207 )
208 })
209 .collect::<Vec<_>>()
210 } else {
211 let dens: Vec<Density<C128>> = pydens
213 .iter()
214 .map(|(_, pyden)| match pyden {
215 PyDensity::Real(pyden_r) => pyden_r.to_qsym2(&bao, mol).map(|den_r| den_r.into()),
216 PyDensity::Complex(pyden_c) => pyden_c.to_qsym2(&bao, mol),
217 })
218 .collect::<Result<Vec<_>, _>>()
219 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
220 let dens_ref = dens
221 .iter()
222 .zip(pydens.iter())
223 .map(|(den, (desc, _))| (desc.clone(), den))
224 .collect::<Vec<_>>();
225
226 let projected_densities = match &use_magnetic_group {
227 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
228 return Err(PyRuntimeError::new_err(
229 "Projection using corepresentations is not yet supported.",
230 ));
231 }
232 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
233 let mut dp_driver =
234 DensityProjectionDriver::<UnitaryRepresentedSymmetryGroup, C128>::builder()
235 .parameters(&dp_params)
236 .densities(dens_ref)
237 .symmetry_group(&pd_res)
238 .build()
239 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
240 py.detach(|| {
241 dp_driver
242 .run()
243 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
244 })?;
245 dp_driver
246 .result()
247 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
248 .projected_densities()
249 .clone()
250 }
251 };
252 projected_densities
253 .into_iter()
254 .map(|(name, projected_dens)| {
255 (
256 name,
257 projected_dens
258 .into_iter()
259 .map(|(row, den_res)| {
260 (
261 row.to_string(),
262 den_res
263 .and_then(|den| {
264 let pyden = PyDensityComplex {
265 complex_symmetric: den.complex_symmetric(),
266 density_matrix: den.density_matrix().clone(),
267 threshold: den.threshold(),
268 };
269 pyden.into_py_any(py).map_err(|err| err.to_string())
270 })
271 .expect("Unable to convert a projected density into a Python object."),
272 )
273 })
274 .collect::<IndexMap<_, _>>(),
275 )
276 })
277 .collect::<Vec<_>>()
278 };
279 Ok(projected_densities)
280}