qsym2/sandbox/target/real_space_function/
real_space_function_analysis.rs1use std::fmt;
4use std::ops::Mul;
5
6use anyhow::{self, Context, ensure, format_err};
7use approx;
8use derive_builder::Builder;
9use itertools::Itertools;
10use log;
11use nalgebra::Point3;
12use ndarray::{Array1, Array2, Axis, Ix1};
13use ndarray_linalg::{
14 UPLO,
15 eig::Eig,
16 eigh::Eigh,
17 types::{Lapack, Scalar},
18};
19use num_complex::{Complex, ComplexFloat};
20use num_traits::{Float, ToPrimitive, Zero};
21use rayon::prelude::*;
22
23use crate::analysis::{
24 EigenvalueComparisonMode, Orbit, OrbitIterator, Overlap, RepAnalysis, fn_calc_xmat_complex,
25 fn_calc_xmat_real,
26};
27use crate::auxiliary::misc::complex_modified_gram_schmidt;
28use crate::chartab::chartab_group::CharacterProperties;
29use crate::chartab::{DecompositionError, SubspaceDecomposable};
30use crate::io::format::{QSym2Output, log_subtitle, qsym2_output};
31use crate::sandbox::target::real_space_function::RealSpaceFunction;
32use crate::symmetry::symmetry_element::symmetry_operation::SpecialSymmetryTransformation;
33use crate::symmetry::symmetry_group::SymmetryGroupProperties;
34use crate::symmetry::symmetry_transformation::{SymmetryTransformable, SymmetryTransformationKind};
35
36impl<T, F> Overlap<T, Ix1> for RealSpaceFunction<T, F>
41where
42 T: ComplexFloat + Lapack + Send + Sync,
43 F: Clone + Sync + Send + Fn(&Point3<f64>) -> T,
44{
45 fn complex_symmetric(&self) -> bool {
46 false
47 }
48
49 fn overlap(
66 &self,
67 other: &Self,
68 metric: Option<&Array1<T>>,
69 _: Option<&Array1<T>>,
70 ) -> Result<T, anyhow::Error> {
71 let weight = metric.ok_or_else(|| {
72 format_err!("No weights found for `RealSpaceFunction` overlap calculation.")
73 })?;
74
75 ensure!(
76 self.grid_points.len() == other.grid_points.len(),
77 "Inconsistent number of grid points between `self` and `other`."
78 );
79 ensure!(
80 self.grid_points.len() == weight.len(),
81 "Inconsistent number of weight values and grid points."
82 );
83
84 let overlap = (0..weight.len())
85 .into_par_iter()
86 .map(|i| {
87 let s_pt = self.grid_points[i];
88 let o_pt = other.grid_points[i];
89 let w = weight[i];
90 match (self.complex_conjugated, other.complex_conjugated) {
91 (false, false) => self.function()(&s_pt).conj() * other.function()(&o_pt) * w,
92 (false, true) => {
93 self.function()(&s_pt).conj() * other.function()(&o_pt).conj() * w
94 }
95 (true, false) => self.function()(&s_pt) * other.function()(&o_pt) * w,
96 (true, true) => self.function()(&s_pt) * other.function()(&o_pt).conj() * w,
97 }
98 })
99 .sum();
100 Ok(overlap)
101 }
102
103 fn overlap_definition(&self) -> String {
105 let k = if self.complex_symmetric() { "κ " } else { "" };
106 format!("⟨{k}f_1|f_2⟩ = ∫ [{k}f_1(r)]* w(r) f_2(r) dr where w(r) is a required weight")
107 }
108}
109
110#[derive(Builder, Clone)]
121pub struct RealSpaceFunctionSymmetryOrbit<'a, G, T, F>
122where
123 G: SymmetryGroupProperties,
124 T: ComplexFloat + fmt::Debug + Lapack,
125 F: Fn(&Point3<f64>) -> T,
126 RealSpaceFunction<T, F>: SymmetryTransformable,
127{
128 group: &'a G,
130
131 origin: &'a RealSpaceFunction<T, F>,
133
134 pub(crate) linear_independence_threshold: <T as ComplexFloat>::Real,
136
137 orbit_overlap_symmetry_threshold: <T as ComplexFloat>::Real,
139
140 integrality_threshold: <T as ComplexFloat>::Real,
143
144 symmetry_transformation_kind: SymmetryTransformationKind,
147
148 #[builder(setter(skip), default = "None")]
150 smat: Option<Array2<T>>,
151
152 #[builder(setter(skip), default = "None")]
155 pub(crate) smat_eigvals: Option<Array1<T>>,
156
157 #[builder(setter(skip), default = "None")]
162 xmat: Option<Array2<T>>,
163
164 eigenvalue_comparison_mode: EigenvalueComparisonMode,
167}
168
169impl<'a, G, T, F> RealSpaceFunctionSymmetryOrbit<'a, G, T, F>
174where
175 G: SymmetryGroupProperties + Clone,
176 T: ComplexFloat + fmt::Debug + Lapack,
177 F: Clone + Fn(&Point3<f64>) -> T,
178 RealSpaceFunction<T, F>: SymmetryTransformable,
179{
180 pub fn builder() -> RealSpaceFunctionSymmetryOrbitBuilder<'a, G, T, F> {
182 RealSpaceFunctionSymmetryOrbitBuilder::default()
183 }
184}
185
186impl<'a, G, F> RealSpaceFunctionSymmetryOrbit<'a, G, f64, F>
187where
188 G: SymmetryGroupProperties + Clone,
189 F: Clone + Fn(&Point3<f64>) -> f64,
190{
191 fn_calc_xmat_real!(
192 pub calc_xmat
204 );
205}
206
207impl<'a, G, T, F> RealSpaceFunctionSymmetryOrbit<'a, G, Complex<T>, F>
208where
209 G: SymmetryGroupProperties + Clone,
210 T: Float + Scalar<Complex = Complex<T>>,
211 Complex<T>: ComplexFloat<Real = T> + Scalar<Real = T, Complex = Complex<T>> + Lapack,
212 F: Clone + Fn(&Point3<f64>) -> Complex<T>,
213 RealSpaceFunction<Complex<T>, F>: SymmetryTransformable + Overlap<Complex<T>, Ix1>,
214{
215 fn_calc_xmat_complex!(
216 pub calc_xmat
228 );
229}
230
231impl<'a, G, T, F> Orbit<G, RealSpaceFunction<T, F>> for RealSpaceFunctionSymmetryOrbit<'a, G, T, F>
240where
241 G: SymmetryGroupProperties + Clone,
242 T: ComplexFloat + fmt::Debug + Lapack,
243 F: Fn(&Point3<f64>) -> T,
244 RealSpaceFunction<T, F>: SymmetryTransformable,
245{
246 type OrbitIter = OrbitIterator<'a, G, RealSpaceFunction<T, F>>;
247
248 fn group(&self) -> &'a G {
249 self.group
250 }
251
252 fn origin(&self) -> &RealSpaceFunction<T, F> {
253 self.origin
254 }
255
256 fn iter(&self) -> Self::OrbitIter {
257 OrbitIterator::new(
258 self.group,
259 self.origin,
260 match self.symmetry_transformation_kind {
261 SymmetryTransformationKind::Spatial => {
262 |op, real_space_function| {
263 real_space_function.sym_transform_spatial(op).with_context(|| {
264 format!("Unable to apply `{op}` spatially on the origin real-space function")
265 })
266 }
267 }
268 SymmetryTransformationKind::SpatialWithSpinTimeReversal => {
269 |op, real_space_function| {
270 real_space_function.sym_transform_spatial_with_spintimerev(op).with_context(|| {
271 format!("Unable to apply `{op}` spatially (with spin-including time-reversal) on the origin real-space function")
272 })
273 }
274 }
275 SymmetryTransformationKind::Spin => |op, real_space_function| {
276 real_space_function.sym_transform_spin(op).with_context(|| {
277 format!(
278 "Unable to apply `{op}` spin-wise on the origin real-space function"
279 )
280 })
281 },
282 SymmetryTransformationKind::SpinSpatial => |op, real_space_function| {
283 real_space_function.sym_transform_spin_spatial(op).with_context(|| {
284 format!("Unable to apply `{op}` spin-spatially on the origin real-space function")
285 })
286 },
287 },
288 )
289 }
290}
291
292impl<'a, G, T, F> RepAnalysis<G, RealSpaceFunction<T, F>, T, Ix1>
297 for RealSpaceFunctionSymmetryOrbit<'a, G, T, F>
298where
299 G: SymmetryGroupProperties + Clone,
300 G::CharTab: SubspaceDecomposable<T>,
301 T: Lapack
302 + ComplexFloat<Real = <T as Scalar>::Real>
303 + fmt::Debug
304 + Send
305 + Sync
306 + Mul<<T as ComplexFloat>::Real, Output = T>,
307 <T as ComplexFloat>::Real: fmt::Debug
308 + Zero
309 + From<u16>
310 + ToPrimitive
311 + approx::RelativeEq<<T as ComplexFloat>::Real>
312 + approx::AbsDiffEq<Epsilon = <T as Scalar>::Real>,
313 F: Clone + Sync + Send + Fn(&Point3<f64>) -> T,
314 RealSpaceFunction<T, F>: SymmetryTransformable,
315{
316 fn set_smat(&mut self, smat: Array2<T>) {
317 self.smat = Some(smat)
318 }
319
320 fn smat(&self) -> Option<&Array2<T>> {
321 self.smat.as_ref()
322 }
323
324 fn xmat(&self) -> &Array2<T> {
325 self.xmat
326 .as_ref()
327 .expect("Orbit overlap orthogonalisation matrix not found.")
328 }
329
330 fn norm_preserving_scalar_map(&self, i: usize) -> Result<fn(T) -> T, anyhow::Error> {
331 if self.origin.complex_symmetric() {
332 Err(format_err!(
333 "`norm_preserving_scalar_map` is currently not implemented for complex symmetric overlaps."
334 ))
335 } else if self
336 .group
337 .get_index(i)
338 .unwrap_or_else(|| panic!("Group operation index `{i}` not found."))
339 .contains_time_reversal()
340 {
341 Ok(ComplexFloat::conj)
342 } else {
343 Ok(|x| x)
344 }
345 }
346
347 fn orbit_overlap_symmetry_threshold(&self) -> <T as ComplexFloat>::Real {
348 self.orbit_overlap_symmetry_threshold
349 }
350
351 fn integrality_threshold(&self) -> <T as ComplexFloat>::Real {
352 self.integrality_threshold
353 }
354
355 fn eigenvalue_comparison_mode(&self) -> &EigenvalueComparisonMode {
356 &self.eigenvalue_comparison_mode
357 }
358
359 fn analyse_rep(
372 &self,
373 ) -> Result<
374 <<G as CharacterProperties>::CharTab as SubspaceDecomposable<T>>::Decomposition,
375 DecompositionError,
376 > {
377 log::debug!("Analysing representation symmetry for a real-space function...");
378 let chis = self
379 .calc_characters()
380 .map_err(|err| DecompositionError(err.to_string()))?;
381 log::debug!("Characters calculated.");
382 log_subtitle("Real-space function orbit characters");
383 qsym2_output!("");
384 self.characters_to_string(&chis, self.integrality_threshold)
385 .log_output_display();
386 qsym2_output!("");
387
388 let res = self.group().character_table().reduce_characters(
389 &chis.iter().map(|(cc, chi)| (cc, *chi)).collect::<Vec<_>>(),
390 self.integrality_threshold(),
391 );
392 log::debug!("Characters reduced.");
393 log::debug!("Analysing representation symmetry for a real-space function... Done.");
394 res
395 }
396}