qsym2/sandbox/bindings/python/representation_analysis/
real_space_function.rs1use std::path::PathBuf;
4
5use duplicate::duplicate_item;
6use nalgebra::Point3;
7use num::Complex;
8use numpy::{PyArray1, PyArray2, PyArrayMethods};
9use pyo3::exceptions::{PyIOError, PyRuntimeError};
10use pyo3::prelude::*;
11use pyo3::types::PyFunction;
12
13use crate::analysis::EigenvalueComparisonMode;
14use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
15use crate::drivers::representation_analysis::{
16 CharacterTableDisplay, MagneticSymmetryAnalysisKind,
17};
18use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
19use crate::drivers::QSym2Driver;
20use crate::io::format::qsym2_output;
21use crate::io::{read_qsym2_binary, QSym2FileType};
22use crate::sandbox::drivers::representation_analysis::real_space_function::{
23 RealSpaceFunctionRepAnalysisDriver, RealSpaceFunctionRepAnalysisParams,
24};
25use crate::sandbox::target::real_space_function::RealSpaceFunction;
26use crate::symmetry::symmetry_group::{
27 MagneticRepresentedSymmetryGroup, UnitaryRepresentedSymmetryGroup,
28};
29use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
30
31#[duplicate_item(
36 [
37 dtype_ [ f64 ]
38 doc_sub_ [ "Python-exposed function to perform representation symmetry analysis for real-valued real-space functions and log the result via the `qsym2-output` logger at the `INFO` level." ]
39 func_sub_ [ "* `function` - A Python function callable on three Cartesian coordinates to give a scalar value. Python type: `Callable[[float, float, float], float]`." ]
40 rep_analyse_real_space_function_ [ rep_analyse_real_space_function_real ]
41 ]
42 [
43 dtype_ [ Complex<f64> ]
44 doc_sub_ [ "Python-exposed function to perform representation symmetry analysis for complex-valued real-space functions and log the result via the `qsym2-output` logger at the `INFO` level." ]
45 func_sub_ [ "* `function` - A Python function callable on three Cartesian coordinates to give a scalar value. Python type: `Callable[[float, float, float], complex]`." ]
46 rep_analyse_real_space_function_ [ rep_analyse_real_space_function_complex ]
47 ]
48)]
49#[doc = doc_sub_]
50#[doc = func_sub_]
57#[pyfunction]
95#[pyo3(signature = (
96 inp_sym,
97 function,
98 integrality_threshold,
99 linear_independence_threshold,
100 use_magnetic_group,
101 use_double_group,
102 use_cayley_table,
103 symmetry_transformation_kind,
104 eigenvalue_comparison_mode,
105 grid_points,
106 weight,
107 write_overlap_eigenvalues=true,
108 write_character_table=true,
109 infinite_order_to_finite=None,
110 angular_function_integrality_threshold=1e-7,
111 angular_function_linear_independence_threshold=1e-7,
112 angular_function_max_angular_momentum=2
113))]
114pub fn rep_analyse_real_space_function_(
115 py: Python<'_>,
116 inp_sym: PathBuf,
117 function: Py<PyFunction>,
118 integrality_threshold: f64,
119 linear_independence_threshold: f64,
120 use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
121 use_double_group: bool,
122 use_cayley_table: bool,
123 symmetry_transformation_kind: SymmetryTransformationKind,
124 eigenvalue_comparison_mode: EigenvalueComparisonMode,
125 grid_points: Bound<'_, PyArray2<f64>>,
126 weight: Bound<'_, PyArray1<dtype_>>,
127 write_overlap_eigenvalues: bool,
128 write_character_table: bool,
129 infinite_order_to_finite: Option<u32>,
130 angular_function_integrality_threshold: f64,
131 angular_function_linear_independence_threshold: f64,
132 angular_function_max_angular_momentum: u32,
133) -> PyResult<()> {
134 let pd_res: SymmetryGroupDetectionResult =
135 read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
136 .map_err(|err| PyIOError::new_err(err.to_string()))?;
137
138 let mut file_name = inp_sym.to_path_buf();
139 file_name.set_extension(QSym2FileType::Sym.ext());
140 qsym2_output!(
141 "Symmetry-group detection results read in from {}.",
142 file_name.display(),
143 );
144 qsym2_output!("");
145
146 let afa_params = AngularFunctionRepAnalysisParams::builder()
147 .integrality_threshold(angular_function_integrality_threshold)
148 .linear_independence_threshold(angular_function_linear_independence_threshold)
149 .max_angular_momentum(angular_function_max_angular_momentum)
150 .build()
151 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
152 let real_space_function_params = RealSpaceFunctionRepAnalysisParams::<f64>::builder()
153 .integrality_threshold(integrality_threshold)
154 .linear_independence_threshold(linear_independence_threshold)
155 .use_magnetic_group(use_magnetic_group.clone())
156 .use_double_group(use_double_group)
157 .use_cayley_table(use_cayley_table)
158 .symmetry_transformation_kind(symmetry_transformation_kind)
159 .eigenvalue_comparison_mode(eigenvalue_comparison_mode)
160 .write_overlap_eigenvalues(write_overlap_eigenvalues)
161 .write_character_table(if write_character_table {
162 Some(CharacterTableDisplay::Symbolic)
163 } else {
164 None
165 })
166 .infinite_order_to_finite(infinite_order_to_finite)
167 .build()
168 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
169
170 let weight = weight.to_owned_array();
171 let grid_array = grid_points.to_owned_array();
172 if grid_array.shape()[0] != 3 {
173 return Err(PyRuntimeError::new_err(
174 "The grid point array does not have the expected dimensions of 3 × N.",
175 ));
176 }
177 let grid_points = grid_array
178 .columns()
179 .into_iter()
180 .map(|col| Point3::new(col[0], col[1], col[2]))
181 .collect::<Vec<_>>();
182
183 let real_space_function = RealSpaceFunction::<dtype_, _>::builder()
184 .function(|pt| {
185 Python::with_gil(|py_inner| {
186 let res = function.call1(py_inner, (pt.x, pt.y, pt.z)).expect(
187 "Unable to apply the real-space function on the specified coordinates.",
188 );
189 res.extract::<dtype_>(py_inner)
190 .expect("Unable to extract the result from the real-space function call.")
191 })
192 })
193 .grid_points(grid_points)
194 .build()
195 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
196
197 match &use_magnetic_group {
198 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
199 let mut real_space_function_driver = RealSpaceFunctionRepAnalysisDriver::<
200 MagneticRepresentedSymmetryGroup,
201 dtype_,
202 _,
203 >::builder()
204 .parameters(&real_space_function_params)
205 .angular_function_parameters(&afa_params)
206 .real_space_function(&real_space_function)
207 .weight(&weight)
208 .symmetry_group(&pd_res)
209 .build()
210 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
211 py.allow_threads(|| {
212 real_space_function_driver
213 .run()
214 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
215 })?
216 }
217 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
218 let mut real_space_function_driver = RealSpaceFunctionRepAnalysisDriver::<
219 UnitaryRepresentedSymmetryGroup,
220 dtype_,
221 _,
222 >::builder()
223 .parameters(&real_space_function_params)
224 .angular_function_parameters(&afa_params)
225 .real_space_function(&real_space_function)
226 .weight(&weight)
227 .symmetry_group(&pd_res)
228 .build()
229 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
230 py.allow_threads(|| {
231 real_space_function_driver
232 .run()
233 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
234 })?
235 }
236 };
237
238 Ok(())
239}