qsym2/sandbox/drivers/representation_analysis/
real_space_function.rs

1//! Sandbox driver for symmetry analysis of RealSpaceFunctiones.
2
3use std::fmt;
4use std::ops::Mul;
5
6use anyhow::{self, bail, format_err};
7use derive_builder::Builder;
8use duplicate::duplicate_item;
9use nalgebra::Point3;
10use ndarray::Array1;
11use ndarray_linalg::types::Lapack;
12use num_complex::{Complex, ComplexFloat};
13use num_traits::Float;
14use serde::{Deserialize, Serialize};
15
16use crate::analysis::{
17    EigenvalueComparisonMode, Orbit, Overlap, ProjectionDecomposition, RepAnalysis,
18    log_overlap_eigenvalues,
19};
20use crate::chartab::SubspaceDecomposable;
21use crate::chartab::chartab_group::CharacterProperties;
22use crate::drivers::QSym2Driver;
23use crate::drivers::representation_analysis::angular_function::{
24    AngularFunctionRepAnalysisParams, find_angular_function_representation,
25    find_spinor_function_representation,
26};
27use crate::drivers::representation_analysis::{
28    CharacterTableDisplay, MagneticSymmetryAnalysisKind, fn_construct_magnetic_group,
29    fn_construct_unitary_group, log_cc_transversal,
30};
31use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
32use crate::group::{GroupProperties, MagneticRepresentedGroup, UnitaryRepresentedGroup};
33use crate::io::format::{
34    QSym2Output, log_subtitle, nice_bool, qsym2_output, write_subtitle, write_title,
35};
36use crate::sandbox::target::real_space_function::RealSpaceFunction;
37use crate::sandbox::target::real_space_function::real_space_function_analysis::RealSpaceFunctionSymmetryOrbit;
38use crate::symmetry::symmetry_group::{
39    MagneticRepresentedSymmetryGroup, SymmetryGroupProperties, UnitaryRepresentedSymmetryGroup,
40};
41use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
42
43#[cfg(test)]
44#[path = "real_space_function_tests.rs"]
45mod real_space_function_tests;
46
47// ==================
48// Struct definitions
49// ==================
50
51// ----------
52// Parameters
53// ----------
54
55const fn default_true() -> bool {
56    true
57}
58const fn default_symbolic() -> Option<CharacterTableDisplay> {
59    Some(CharacterTableDisplay::Symbolic)
60}
61
62/// Structure containing control parameters for real-space function representation analysis.
63#[derive(Clone, Builder, Debug, Serialize, Deserialize)]
64pub struct RealSpaceFunctionRepAnalysisParams<T: From<f64>> {
65    /// Threshold for checking if subspace multiplicities are integral.
66    pub integrality_threshold: T,
67
68    /// Threshold for determining zero eigenvalues in the orbit overlap matrix.
69    pub linear_independence_threshold: T,
70
71    /// Option indicating if the magnetic group is to be used for symmetry analysis, and if so,
72    /// whether unitary representations or unitary-antiunitary corepresentations should be used.
73    #[builder(default = "None")]
74    #[serde(default)]
75    pub use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
76
77    /// Boolean indicating if the double group is to be used for symmetry analysis.
78    #[builder(default = "false")]
79    #[serde(default)]
80    pub use_double_group: bool,
81
82    /// Boolean indicating if the Cayley table of the group, if available, should be used to speed
83    /// up the computation of orbit overlap matrices.
84    #[builder(default = "true")]
85    #[serde(default = "default_true")]
86    pub use_cayley_table: bool,
87
88    /// The kind of symmetry transformation to be applied on the reference real-space function to
89    /// generate the orbit for symmetry analysis.
90    #[builder(default = "SymmetryTransformationKind::Spatial")]
91    #[serde(default)]
92    pub symmetry_transformation_kind: SymmetryTransformationKind,
93
94    /// Option indicating if the character table of the group used for symmetry analysis is to be
95    /// printed out.
96    #[builder(default = "Some(CharacterTableDisplay::Symbolic)")]
97    #[serde(default = "default_symbolic")]
98    pub write_character_table: Option<CharacterTableDisplay>,
99
100    /// Boolean indicating if the eigenvalues of the orbit overlap matrix are to be printed out.
101    #[builder(default = "true")]
102    #[serde(default = "default_true")]
103    pub write_overlap_eigenvalues: bool,
104
105    /// The comparison mode for filtering out orbit overlap eigenvalues.
106    #[builder(default = "EigenvalueComparisonMode::Modulus")]
107    #[serde(default)]
108    pub eigenvalue_comparison_mode: EigenvalueComparisonMode,
109
110    /// The finite order to which any infinite-order symmetry element is reduced, so that a finite
111    /// subgroup of an infinite group can be used for the symmetry analysis.
112    #[builder(default = "None")]
113    #[serde(default)]
114    pub infinite_order_to_finite: Option<u32>,
115}
116
117impl<T> RealSpaceFunctionRepAnalysisParams<T>
118where
119    T: Float + From<f64>,
120{
121    /// Returns a builder to construct a [`RealSpaceFunctionRepAnalysisParams`] structure.
122    pub fn builder() -> RealSpaceFunctionRepAnalysisParamsBuilder<T> {
123        RealSpaceFunctionRepAnalysisParamsBuilder::default()
124    }
125}
126
127impl Default for RealSpaceFunctionRepAnalysisParams<f64> {
128    fn default() -> Self {
129        Self::builder()
130            .integrality_threshold(1e-7)
131            .linear_independence_threshold(1e-7)
132            .build()
133            .expect("Unable to construct a default `RealSpaceFunctionRepAnalysisParams<f64>`.")
134    }
135}
136
137impl<T> fmt::Display for RealSpaceFunctionRepAnalysisParams<T>
138where
139    T: From<f64> + fmt::LowerExp + fmt::Debug,
140{
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        writeln!(
143            f,
144            "Integrality threshold: {:.3e}",
145            self.integrality_threshold
146        )?;
147        writeln!(
148            f,
149            "Linear independence threshold: {:.3e}",
150            self.linear_independence_threshold
151        )?;
152        writeln!(
153            f,
154            "Orbit eigenvalue comparison mode: {}",
155            self.eigenvalue_comparison_mode
156        )?;
157        writeln!(
158            f,
159            "Write overlap eigenvalues: {}",
160            nice_bool(self.write_overlap_eigenvalues)
161        )?;
162        writeln!(f)?;
163        writeln!(
164            f,
165            "Use magnetic group for analysis: {}",
166            match self.use_magnetic_group {
167                None => "no",
168                Some(MagneticSymmetryAnalysisKind::Representation) =>
169                    "yes, using unitary representations",
170                Some(MagneticSymmetryAnalysisKind::Corepresentation) =>
171                    "yes, using magnetic corepresentations",
172            }
173        )?;
174        writeln!(
175            f,
176            "Use double group for analysis: {}",
177            nice_bool(self.use_double_group)
178        )?;
179        writeln!(
180            f,
181            "Use Cayley table for orbit overlap matrices: {}",
182            nice_bool(self.use_cayley_table)
183        )?;
184        if let Some(finite_order) = self.infinite_order_to_finite {
185            writeln!(f, "Infinite order to finite: {finite_order}")?;
186        }
187        writeln!(
188            f,
189            "Symmetry transformation kind: {}",
190            self.symmetry_transformation_kind
191        )?;
192        writeln!(f)?;
193        writeln!(
194            f,
195            "Write character table: {}",
196            if let Some(chartab_display) = self.write_character_table.as_ref() {
197                format!("yes, {}", chartab_display.to_string().to_lowercase())
198            } else {
199                "no".to_string()
200            }
201        )?;
202
203        Ok(())
204    }
205}
206
207// ------
208// Result
209// ------
210
211/// Structure to contain real-space function representation analysis results.
212#[derive(Clone, Builder)]
213pub struct RealSpaceFunctionRepAnalysisResult<'a, G, T, F>
214where
215    G: SymmetryGroupProperties + Clone,
216    G::CharTab: SubspaceDecomposable<T>,
217    T: ComplexFloat + Lapack,
218    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
219    F: Clone + Fn(&Point3<f64>) -> T,
220{
221    /// The control parameters used to obtain this set of real-space function representation
222    /// analysis results.
223    parameters: &'a RealSpaceFunctionRepAnalysisParams<<T as ComplexFloat>::Real>,
224
225    /// The RealSpaceFunction being analysed.
226    real_space_function: &'a RealSpaceFunction<T, F>,
227
228    /// The group used for the representation analysis.
229    group: G,
230
231    /// The deduced symmetry of the real-space function.
232    real_space_function_symmetry:
233        Result<<G::CharTab as SubspaceDecomposable<T>>::Decomposition, String>,
234}
235
236impl<'a, G, T, F> RealSpaceFunctionRepAnalysisResult<'a, G, T, F>
237where
238    G: SymmetryGroupProperties + Clone,
239    G::CharTab: SubspaceDecomposable<T>,
240    T: ComplexFloat + Lapack,
241    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
242    F: Clone + Fn(&Point3<f64>) -> T,
243{
244    /// Returns a builder to construct a new [`RealSpaceFunctionRepAnalysisResultBuilder`] structure.
245    pub fn builder() -> RealSpaceFunctionRepAnalysisResultBuilder<'a, G, T, F> {
246        RealSpaceFunctionRepAnalysisResultBuilder::default()
247    }
248}
249
250impl<'a, G, T, F> fmt::Display for RealSpaceFunctionRepAnalysisResult<'a, G, T, F>
251where
252    G: SymmetryGroupProperties + Clone,
253    G::CharTab: SubspaceDecomposable<T>,
254    T: ComplexFloat + Lapack,
255    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug + fmt::Display,
256    F: Clone + Fn(&Point3<f64>) -> T,
257{
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        write_subtitle(f, "Orbit-based symmetry analysis results")?;
260        writeln!(f)?;
261        writeln!(
262            f,
263            "> Group: {} ({})",
264            self.group
265                .finite_subgroup_name()
266                .map(|subgroup_name| format!("{} > {}", self.group.name(), subgroup_name))
267                .unwrap_or(self.group.name()),
268            self.group.group_type().to_string().to_lowercase()
269        )?;
270        writeln!(f)?;
271        writeln!(f, "> Overall real-space function result")?;
272        writeln!(
273            f,
274            "  Grid size: {} {}",
275            self.real_space_function.grid_points().len(),
276            if self.real_space_function.grid_points().len() == 1 {
277                "point"
278            } else {
279                "points"
280            }
281        )?;
282        writeln!(
283            f,
284            "  Symmetry: {}",
285            self.real_space_function_symmetry
286                .as_ref()
287                .map(|s| s.to_string())
288                .unwrap_or_else(|err| format!("-- ({err})"))
289        )?;
290        writeln!(f)?;
291
292        Ok(())
293    }
294}
295
296impl<'a, G, T, F> fmt::Debug for RealSpaceFunctionRepAnalysisResult<'a, G, T, F>
297where
298    G: SymmetryGroupProperties + Clone,
299    G::CharTab: SubspaceDecomposable<T>,
300    T: ComplexFloat + Lapack,
301    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug + fmt::Display,
302    F: Clone + Fn(&Point3<f64>) -> T,
303{
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        writeln!(f, "{self}")
306    }
307}
308
309impl<'a, G, T, F> RealSpaceFunctionRepAnalysisResult<'a, G, T, F>
310where
311    G: SymmetryGroupProperties + Clone,
312    G::CharTab: SubspaceDecomposable<T>,
313    T: ComplexFloat + Lapack,
314    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug + fmt::Display,
315    F: Clone + Fn(&Point3<f64>) -> T,
316{
317    /// Returns the real-space function symmetry obtained from the analysis result.
318    pub fn real_space_function_symmetry(
319        &self,
320    ) -> &Result<<G::CharTab as SubspaceDecomposable<T>>::Decomposition, String> {
321        &self.real_space_function_symmetry
322    }
323
324    /// Returns the parameters used for the representation analysis.
325    pub fn parameters(&self) -> &RealSpaceFunctionRepAnalysisParams<<T as ComplexFloat>::Real> {
326        self.parameters
327    }
328}
329
330// ------
331// Driver
332// ------
333
334// ~~~~~~~~~~~~~~~~~
335// Struct definition
336// ~~~~~~~~~~~~~~~~~
337
338/// Driver structure for performing representation analysis on real-space functions.
339#[derive(Clone, Builder)]
340#[builder(build_fn(validate = "Self::validate"))]
341pub struct RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
342where
343    G: SymmetryGroupProperties + Clone,
344    G::CharTab: SubspaceDecomposable<T>,
345    T: ComplexFloat + Lapack,
346    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
347    F: Clone + Fn(&Point3<f64>) -> T,
348{
349    /// The control parameters for real-space function representation analysis.
350    parameters: &'a RealSpaceFunctionRepAnalysisParams<<T as ComplexFloat>::Real>,
351
352    /// The real-space function to be analysed.
353    real_space_function: &'a RealSpaceFunction<T, F>,
354
355    /// The result from symmetry-group detection that will then be used to construct the full group
356    /// for the definition and analysis of the real-space function.
357    symmetry_group: &'a SymmetryGroupDetectionResult,
358
359    /// The weight used in the evaluation of the inner products between real-space functions.
360    weight: &'a Array1<T>,
361
362    /// The control parameters for symmetry analysis of angular functions.
363    angular_function_parameters: &'a AngularFunctionRepAnalysisParams,
364
365    /// The result of the real-space function representation analysis.
366    #[builder(setter(skip), default = "None")]
367    result: Option<RealSpaceFunctionRepAnalysisResult<'a, G, T, F>>,
368}
369
370impl<'a, G, T, F> RealSpaceFunctionRepAnalysisDriverBuilder<'a, G, T, F>
371where
372    G: SymmetryGroupProperties + Clone,
373    G::CharTab: SubspaceDecomposable<T>,
374    T: ComplexFloat + Lapack,
375    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
376    F: Clone + Fn(&Point3<f64>) -> T,
377{
378    fn validate(&self) -> Result<(), String> {
379        let _ = self
380            .real_space_function
381            .ok_or("No real-space function specified.".to_string())?;
382
383        let params = self.parameters.ok_or(
384            "No real-space function representation analysis parameters found.".to_string(),
385        )?;
386
387        let sym_res = self
388            .symmetry_group
389            .ok_or("No symmetry group information found.".to_string())?;
390
391        let sym = if params.use_magnetic_group.is_some() {
392            sym_res
393                .magnetic_symmetry
394                .as_ref()
395                .ok_or("Magnetic symmetry requested for representation analysis, but no magnetic symmetry found.")?
396        } else {
397            &sym_res.unitary_symmetry
398        };
399
400        if sym.is_infinite() && params.infinite_order_to_finite.is_none() {
401            Err(format!(
402                "Representation analysis cannot be performed using the entirety of the infinite group `{}`. \
403                    Consider setting the parameter `infinite_order_to_finite` to restrict to a finite subgroup instead.",
404                sym.group_name
405                    .as_ref()
406                    .expect("No symmetry group name found.")
407            ))
408        } else {
409            Ok(())
410        }
411    }
412}
413
414// ~~~~~~~~~~~~~~~~~~~~~~
415// Struct implementations
416// ~~~~~~~~~~~~~~~~~~~~~~
417
418// Generic for all symmetry groups G and determinant numeric type T
419// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
420
421impl<'a, G, T, F> RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
422where
423    G: SymmetryGroupProperties + Clone,
424    G::CharTab: SubspaceDecomposable<T>,
425    T: ComplexFloat + Lapack,
426    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
427    F: Clone + Fn(&Point3<f64>) -> T,
428{
429    /// Returns a builder to construct a [`RealSpaceFunctionRepAnalysisDriver`] structure.
430    pub fn builder() -> RealSpaceFunctionRepAnalysisDriverBuilder<'a, G, T, F> {
431        RealSpaceFunctionRepAnalysisDriverBuilder::default()
432    }
433}
434
435// Specific for unitary-represented symmetry groups, but generic for function numeric type T
436// '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
437
438impl<'a, T, F> RealSpaceFunctionRepAnalysisDriver<'a, UnitaryRepresentedSymmetryGroup, T, F>
439where
440    T: ComplexFloat + Lapack + Sync + Send,
441    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug + Sync + Send,
442    for<'b> Complex<f64>: Mul<&'b T, Output = Complex<f64>>,
443    F: Clone + Fn(&Point3<f64>) -> T,
444{
445    fn_construct_unitary_group!(
446        /// Constructs the unitary-represented group (which itself can be unitary or magnetic) ready
447        /// for real-space function representation analysis.
448        construct_unitary_group
449    );
450}
451
452// Specific for magnetic-represented symmetry groups, but generic for function numeric type T
453// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
454
455impl<'a, T, F> RealSpaceFunctionRepAnalysisDriver<'a, MagneticRepresentedSymmetryGroup, T, F>
456where
457    T: ComplexFloat + Lapack + Sync + Send,
458    <T as ComplexFloat>::Real: From<f64> + Sync + Send + fmt::LowerExp + fmt::Debug,
459    for<'b> Complex<f64>: Mul<&'b T, Output = Complex<f64>>,
460    F: Clone + Fn(&Point3<f64>) -> T,
461{
462    fn_construct_magnetic_group!(
463        /// Constructs the magnetic-represented group (which itself can only be magnetic) ready for
464        /// real-space function corepresentation analysis.
465        construct_magnetic_group
466    );
467}
468
469// Specific for unitary-represented and magnetic-represented symmetry groups and determinant numeric types f64 and C128
470// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
471
472#[duplicate_item(
473    duplicate!{
474        [ dtype_nested; [f64]; [Complex<f64>] ]
475        [
476            gtype_ [ UnitaryRepresentedSymmetryGroup ]
477            dtype_ [ dtype_nested ]
478            doc_sub_ [ "Performs representation analysis using a unitary-represented group and stores the result." ]
479            analyse_fn_ [ analyse_representation ]
480            construct_group_ [ self.construct_unitary_group()? ]
481            calc_projections_ [
482                log_subtitle("Real-space function projection decompositions");
483                qsym2_output!("");
484                qsym2_output!("  Projections are defined w.r.t. the following inner product:");
485                qsym2_output!("    {}", real_space_function_orbit.origin().overlap_definition());
486                qsym2_output!("");
487                real_space_function_orbit
488                    .projections_to_string(
489                        &real_space_function_orbit.calc_projection_compositions()?,
490                        params.integrality_threshold,
491                    )
492                    .log_output_display();
493                qsym2_output!("");
494            ]
495        ]
496    }
497    duplicate!{
498        [ dtype_nested; [f64]; [Complex<f64>] ]
499        [
500            gtype_ [ MagneticRepresentedSymmetryGroup ]
501            dtype_ [ dtype_nested ]
502            doc_sub_ [ "Performs corepresentation analysis using a magnetic-represented group and stores the result." ]
503            analyse_fn_ [ analyse_corepresentation ]
504            construct_group_ [ self.construct_magnetic_group()? ]
505            calc_projections_ [ ]
506        ]
507    }
508)]
509impl<'a, F> RealSpaceFunctionRepAnalysisDriver<'a, gtype_, dtype_, F>
510where
511    F: Clone + Sync + Send + Fn(&Point3<f64>) -> dtype_,
512{
513    #[doc = doc_sub_]
514    fn analyse_fn_(&mut self) -> Result<(), anyhow::Error> {
515        let params = self.parameters;
516        let group = construct_group_;
517        log_cc_transversal(&group);
518        let _ = find_angular_function_representation(&group, self.angular_function_parameters);
519        if group.is_double_group() {
520            let _ = find_spinor_function_representation(&group, self.angular_function_parameters);
521        }
522
523        let mut real_space_function_orbit = RealSpaceFunctionSymmetryOrbit::builder()
524            .origin(self.real_space_function)
525            .group(&group)
526            .integrality_threshold(params.integrality_threshold)
527            .linear_independence_threshold(params.linear_independence_threshold)
528            .symmetry_transformation_kind(params.symmetry_transformation_kind.clone())
529            .eigenvalue_comparison_mode(params.eigenvalue_comparison_mode.clone())
530            .build()?;
531        let real_space_function_symmetry = real_space_function_orbit
532            .calc_smat(Some(self.weight), None, params.use_cayley_table)
533            .and_then(|real_space_function_orb| real_space_function_orb.normalise_smat())
534            .map_err(|err| err.to_string())
535            .and_then(|real_space_function_orb| {
536                real_space_function_orb
537                    .calc_xmat(false)
538                    .map_err(|err| err.to_string())?;
539                if params.write_overlap_eigenvalues
540                    && let Some(smat_eigvals) = real_space_function_orb.smat_eigvals.as_ref()
541                {
542                    log_overlap_eigenvalues(
543                        "Real-space function orbit overlap eigenvalues",
544                        smat_eigvals,
545                        params.linear_independence_threshold,
546                        &params.eigenvalue_comparison_mode,
547                    );
548                    qsym2_output!("");
549                }
550                real_space_function_orb
551                    .analyse_rep()
552                    .map_err(|err| err.to_string())
553            });
554
555        {
556            calc_projections_
557        }
558
559        let result = RealSpaceFunctionRepAnalysisResult::builder()
560            .parameters(params)
561            .real_space_function(self.real_space_function)
562            .group(group)
563            .real_space_function_symmetry(real_space_function_symmetry)
564            .build()?;
565        self.result = Some(result);
566
567        Ok(())
568    }
569}
570
571// ~~~~~~~~~~~~~~~~~~~~~
572// Trait implementations
573// ~~~~~~~~~~~~~~~~~~~~~
574
575// Generic for all symmetry groups G and determinant numeric type T
576// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
577
578impl<'a, G, T, F> fmt::Display for RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
579where
580    G: SymmetryGroupProperties + Clone,
581    G::CharTab: SubspaceDecomposable<T>,
582    T: ComplexFloat + Lapack,
583    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
584    F: Clone + Fn(&Point3<f64>) -> T,
585{
586    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
587        write_title(f, "Real-Space Function Symmetry Analysis")?;
588        writeln!(f)?;
589        writeln!(f, "{}", self.parameters)?;
590        Ok(())
591    }
592}
593
594impl<'a, G, T, F> fmt::Debug for RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
595where
596    G: SymmetryGroupProperties + Clone,
597    G::CharTab: SubspaceDecomposable<T>,
598    T: ComplexFloat + Lapack,
599    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
600    F: Clone + Fn(&Point3<f64>) -> T,
601{
602    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
603        writeln!(f, "{self}")
604    }
605}
606
607// Specific for unitary/magnetic-represented groups and function numeric type f64/Complex<f64>
608// '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
609
610#[duplicate_item(
611    duplicate!{
612        [ dtype_nested; [f64]; [Complex<f64>] ]
613        [
614            gtype_ [ UnitaryRepresentedSymmetryGroup ]
615            dtype_ [ dtype_nested ]
616            analyse_fn_ [ analyse_representation ]
617        ]
618    }
619    duplicate!{
620        [ dtype_nested; [f64]; [Complex<f64>] ]
621        [
622            gtype_ [ MagneticRepresentedSymmetryGroup ]
623            dtype_ [ dtype_nested ]
624            analyse_fn_ [ analyse_corepresentation ]
625        ]
626    }
627)]
628impl<'a, F> QSym2Driver for RealSpaceFunctionRepAnalysisDriver<'a, gtype_, dtype_, F>
629where
630    F: Clone + Sync + Send + Fn(&Point3<f64>) -> dtype_,
631{
632    type Params = RealSpaceFunctionRepAnalysisParams<f64>;
633
634    type Outcome = RealSpaceFunctionRepAnalysisResult<'a, gtype_, dtype_, F>;
635
636    fn result(&self) -> Result<&Self::Outcome, anyhow::Error> {
637        self.result
638            .as_ref()
639            .ok_or_else(|| format_err!("No real-space function analysis results found."))
640    }
641
642    fn run(&mut self) -> Result<(), anyhow::Error> {
643        self.log_output_display();
644        self.analyse_fn_()?;
645        self.result()?.log_output_display();
646        Ok(())
647    }
648}