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    log_overlap_eigenvalues, EigenvalueComparisonMode, Orbit, Overlap, ProjectionDecomposition,
18    RepAnalysis,
19};
20use crate::chartab::chartab_group::CharacterProperties;
21use crate::chartab::SubspaceDecomposable;
22use crate::drivers::representation_analysis::angular_function::{
23    find_angular_function_representation, find_spinor_function_representation,
24    AngularFunctionRepAnalysisParams,
25};
26use crate::drivers::representation_analysis::{
27    fn_construct_magnetic_group, fn_construct_unitary_group, log_cc_transversal,
28    CharacterTableDisplay, MagneticSymmetryAnalysisKind,
29};
30use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
31use crate::drivers::QSym2Driver;
32use crate::group::{GroupProperties, MagneticRepresentedGroup, UnitaryRepresentedGroup};
33use crate::io::format::{
34    log_subtitle, nice_bool, qsym2_output, write_subtitle, write_title, QSym2Output,
35};
36use crate::sandbox::target::real_space_function::real_space_function_analysis::RealSpaceFunctionSymmetryOrbit;
37use crate::sandbox::target::real_space_function::RealSpaceFunction;
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    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
325// ------
326// Driver
327// ------
328
329// ~~~~~~~~~~~~~~~~~
330// Struct definition
331// ~~~~~~~~~~~~~~~~~
332
333/// Driver structure for performing representation analysis on real-space functions.
334#[derive(Clone, Builder)]
335#[builder(build_fn(validate = "Self::validate"))]
336pub struct RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
337where
338    G: SymmetryGroupProperties + Clone,
339    G::CharTab: SubspaceDecomposable<T>,
340    T: ComplexFloat + Lapack,
341    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
342    F: Clone + Fn(&Point3<f64>) -> T,
343{
344    /// The control parameters for real-space function representation analysis.
345    parameters: &'a RealSpaceFunctionRepAnalysisParams<<T as ComplexFloat>::Real>,
346
347    /// The real-space function to be analysed.
348    real_space_function: &'a RealSpaceFunction<T, F>,
349
350    /// The result from symmetry-group detection that will then be used to construct the full group
351    /// for the definition and analysis of the real-space function.
352    symmetry_group: &'a SymmetryGroupDetectionResult,
353
354    /// The weight used in the evaluation of the inner products between real-space functions.
355    weight: &'a Array1<T>,
356
357    /// The control parameters for symmetry analysis of angular functions.
358    angular_function_parameters: &'a AngularFunctionRepAnalysisParams,
359
360    /// The result of the real-space function representation analysis.
361    #[builder(setter(skip), default = "None")]
362    result: Option<RealSpaceFunctionRepAnalysisResult<'a, G, T, F>>,
363}
364
365impl<'a, G, T, F> RealSpaceFunctionRepAnalysisDriverBuilder<'a, G, T, F>
366where
367    G: SymmetryGroupProperties + Clone,
368    G::CharTab: SubspaceDecomposable<T>,
369    T: ComplexFloat + Lapack,
370    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
371    F: Clone + Fn(&Point3<f64>) -> T,
372{
373    fn validate(&self) -> Result<(), String> {
374        let _ = self
375            .real_space_function
376            .ok_or("No real-space function specified.".to_string())?;
377
378        let params = self.parameters.ok_or(
379            "No real-space function representation analysis parameters found.".to_string(),
380        )?;
381
382        let sym_res = self
383            .symmetry_group
384            .ok_or("No symmetry group information found.".to_string())?;
385
386        let sym = if params.use_magnetic_group.is_some() {
387            sym_res
388                .magnetic_symmetry
389                .as_ref()
390                .ok_or("Magnetic symmetry requested for representation analysis, but no magnetic symmetry found.")?
391        } else {
392            &sym_res.unitary_symmetry
393        };
394
395        if sym.is_infinite() && params.infinite_order_to_finite.is_none() {
396            Err(
397                format!(
398                    "Representation analysis cannot be performed using the entirety of the infinite group `{}`. \
399                    Consider setting the parameter `infinite_order_to_finite` to restrict to a finite subgroup instead.",
400                    sym.group_name.as_ref().expect("No symmetry group name found.")
401                )
402            )
403        } else {
404            Ok(())
405        }
406    }
407}
408
409// ~~~~~~~~~~~~~~~~~~~~~~
410// Struct implementations
411// ~~~~~~~~~~~~~~~~~~~~~~
412
413// Generic for all symmetry groups G and determinant numeric type T
414// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
415
416impl<'a, G, T, F> RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
417where
418    G: SymmetryGroupProperties + Clone,
419    G::CharTab: SubspaceDecomposable<T>,
420    T: ComplexFloat + Lapack,
421    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
422    F: Clone + Fn(&Point3<f64>) -> T,
423{
424    /// Returns a builder to construct a [`RealSpaceFunctionRepAnalysisDriver`] structure.
425    pub fn builder() -> RealSpaceFunctionRepAnalysisDriverBuilder<'a, G, T, F> {
426        RealSpaceFunctionRepAnalysisDriverBuilder::default()
427    }
428}
429
430// Specific for unitary-represented symmetry groups, but generic for function numeric type T
431// '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
432
433impl<'a, T, F> RealSpaceFunctionRepAnalysisDriver<'a, UnitaryRepresentedSymmetryGroup, T, F>
434where
435    T: ComplexFloat + Lapack + Sync + Send,
436    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug + Sync + Send,
437    for<'b> Complex<f64>: Mul<&'b T, Output = Complex<f64>>,
438    F: Clone + Fn(&Point3<f64>) -> T,
439{
440    fn_construct_unitary_group!(
441        /// Constructs the unitary-represented group (which itself can be unitary or magnetic) ready
442        /// for real-space function representation analysis.
443        construct_unitary_group
444    );
445}
446
447// Specific for magnetic-represented symmetry groups, but generic for function numeric type T
448// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
449
450impl<'a, T, F> RealSpaceFunctionRepAnalysisDriver<'a, MagneticRepresentedSymmetryGroup, T, F>
451where
452    T: ComplexFloat + Lapack + Sync + Send,
453    <T as ComplexFloat>::Real: From<f64> + Sync + Send + fmt::LowerExp + fmt::Debug,
454    for<'b> Complex<f64>: Mul<&'b T, Output = Complex<f64>>,
455    F: Clone + Fn(&Point3<f64>) -> T,
456{
457    fn_construct_magnetic_group!(
458        /// Constructs the magnetic-represented group (which itself can only be magnetic) ready for
459        /// real-space function corepresentation analysis.
460        construct_magnetic_group
461    );
462}
463
464// Specific for unitary-represented and magnetic-represented symmetry groups and determinant numeric types f64 and C128
465// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
466
467#[duplicate_item(
468    duplicate!{
469        [ dtype_nested; [f64]; [Complex<f64>] ]
470        [
471            gtype_ [ UnitaryRepresentedSymmetryGroup ]
472            dtype_ [ dtype_nested ]
473            doc_sub_ [ "Performs representation analysis using a unitary-represented group and stores the result." ]
474            analyse_fn_ [ analyse_representation ]
475            construct_group_ [ self.construct_unitary_group()? ]
476            calc_projections_ [
477                log_subtitle("Real-space function projection decompositions");
478                qsym2_output!("");
479                qsym2_output!("  Projections are defined w.r.t. the following inner product:");
480                qsym2_output!("    {}", real_space_function_orbit.origin().overlap_definition());
481                qsym2_output!("");
482                real_space_function_orbit
483                    .projections_to_string(
484                        &real_space_function_orbit.calc_projection_compositions()?,
485                        params.integrality_threshold,
486                    )
487                    .log_output_display();
488                qsym2_output!("");
489            ]
490        ]
491    }
492    duplicate!{
493        [ dtype_nested; [f64]; [Complex<f64>] ]
494        [
495            gtype_ [ MagneticRepresentedSymmetryGroup ]
496            dtype_ [ dtype_nested ]
497            doc_sub_ [ "Performs corepresentation analysis using a magnetic-represented group and stores the result." ]
498            analyse_fn_ [ analyse_corepresentation ]
499            construct_group_ [ self.construct_magnetic_group()? ]
500            calc_projections_ [ ]
501        ]
502    }
503)]
504impl<'a, F> RealSpaceFunctionRepAnalysisDriver<'a, gtype_, dtype_, F>
505where
506    F: Clone + Sync + Send + Fn(&Point3<f64>) -> dtype_,
507{
508    #[doc = doc_sub_]
509    fn analyse_fn_(&mut self) -> Result<(), anyhow::Error> {
510        let params = self.parameters;
511        let group = construct_group_;
512        log_cc_transversal(&group);
513        let _ = find_angular_function_representation(&group, self.angular_function_parameters);
514        if group.is_double_group() {
515            let _ = find_spinor_function_representation(&group, self.angular_function_parameters);
516        }
517
518        let mut real_space_function_orbit = RealSpaceFunctionSymmetryOrbit::builder()
519            .origin(self.real_space_function)
520            .group(&group)
521            .integrality_threshold(params.integrality_threshold)
522            .linear_independence_threshold(params.linear_independence_threshold)
523            .symmetry_transformation_kind(params.symmetry_transformation_kind.clone())
524            .eigenvalue_comparison_mode(params.eigenvalue_comparison_mode.clone())
525            .build()?;
526        let real_space_function_symmetry = real_space_function_orbit
527            .calc_smat(Some(self.weight), None, params.use_cayley_table)
528            .and_then(|real_space_function_orb| real_space_function_orb.normalise_smat())
529            .map_err(|err| err.to_string())
530            .and_then(|real_space_function_orb| {
531                real_space_function_orb
532                    .calc_xmat(false)
533                    .map_err(|err| err.to_string())?;
534                if params.write_overlap_eigenvalues {
535                    if let Some(smat_eigvals) = real_space_function_orb.smat_eigvals.as_ref() {
536                        log_overlap_eigenvalues(
537                            "Real-space function orbit overlap eigenvalues",
538                            smat_eigvals,
539                            params.linear_independence_threshold,
540                            &params.eigenvalue_comparison_mode,
541                        );
542                        qsym2_output!("");
543                    }
544                }
545                real_space_function_orb
546                    .analyse_rep()
547                    .map_err(|err| err.to_string())
548            });
549
550        {
551            calc_projections_
552        }
553
554        let result = RealSpaceFunctionRepAnalysisResult::builder()
555            .parameters(params)
556            .real_space_function(self.real_space_function)
557            .group(group)
558            .real_space_function_symmetry(real_space_function_symmetry)
559            .build()?;
560        self.result = Some(result);
561
562        Ok(())
563    }
564}
565
566// ~~~~~~~~~~~~~~~~~~~~~
567// Trait implementations
568// ~~~~~~~~~~~~~~~~~~~~~
569
570// Generic for all symmetry groups G and determinant numeric type T
571// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
572
573impl<'a, G, T, F> fmt::Display for RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
574where
575    G: SymmetryGroupProperties + Clone,
576    G::CharTab: SubspaceDecomposable<T>,
577    T: ComplexFloat + Lapack,
578    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
579    F: Clone + Fn(&Point3<f64>) -> T,
580{
581    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582        write_title(f, "Real-Space Function Symmetry Analysis")?;
583        writeln!(f)?;
584        writeln!(f, "{}", self.parameters)?;
585        Ok(())
586    }
587}
588
589impl<'a, G, T, F> fmt::Debug for RealSpaceFunctionRepAnalysisDriver<'a, G, T, F>
590where
591    G: SymmetryGroupProperties + Clone,
592    G::CharTab: SubspaceDecomposable<T>,
593    T: ComplexFloat + Lapack,
594    <T as ComplexFloat>::Real: From<f64> + fmt::LowerExp + fmt::Debug,
595    F: Clone + Fn(&Point3<f64>) -> T,
596{
597    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
598        writeln!(f, "{self}")
599    }
600}
601
602// Specific for unitary/magnetic-represented groups and function numeric type f64/Complex<f64>
603// '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
604
605#[duplicate_item(
606    duplicate!{
607        [ dtype_nested; [f64]; [Complex<f64>] ]
608        [
609            gtype_ [ UnitaryRepresentedSymmetryGroup ]
610            dtype_ [ dtype_nested ]
611            analyse_fn_ [ analyse_representation ]
612        ]
613    }
614    duplicate!{
615        [ dtype_nested; [f64]; [Complex<f64>] ]
616        [
617            gtype_ [ MagneticRepresentedSymmetryGroup ]
618            dtype_ [ dtype_nested ]
619            analyse_fn_ [ analyse_corepresentation ]
620        ]
621    }
622)]
623impl<'a, F> QSym2Driver for RealSpaceFunctionRepAnalysisDriver<'a, gtype_, dtype_, F>
624where
625    F: Clone + Sync + Send + Fn(&Point3<f64>) -> dtype_,
626{
627    type Params = RealSpaceFunctionRepAnalysisParams<f64>;
628
629    type Outcome = RealSpaceFunctionRepAnalysisResult<'a, gtype_, dtype_, F>;
630
631    fn result(&self) -> Result<&Self::Outcome, anyhow::Error> {
632        self.result
633            .as_ref()
634            .ok_or_else(|| format_err!("No real-space function analysis results found."))
635    }
636
637    fn run(&mut self) -> Result<(), anyhow::Error> {
638        self.log_output_display();
639        self.analyse_fn_()?;
640        self.result()?.log_output_display();
641        Ok(())
642    }
643}