Source code for aiida_quantumespresso.calculations.functions.xspectra.get_powder_spectrum

# -*- coding: utf-8 -*-
"""CalcFunction to compute the powder spectrum of a set of XANES spectra from ``XspectraCalculation``."""
from aiida.common import ValidationError
from aiida.engine import calcfunction
from aiida.orm import XyData


@calcfunction
[docs]def get_powder_spectrum(**kwargs): # pylint: disable=too-many-statements """Combine the given spectra into a single "Powder" spectrum, representing the XAS of a powder sample. The function expects between 1 and 3 XyData nodes from ``XspectraCalculation`` whose polarisation vectors are the basis vectors of the original crystal structure (100, 010, 001). """ spectra = [node for node in kwargs.values() if isinstance(node, XyData)] vectors = [node.creator.res['xepsilon'] for node in spectra] if len(vectors) > 3: raise ValidationError(f'Expected between 1 and 3 XyData nodes as input, but {len(spectra)} were given.') # If the system is isochoric (e.g. a cubic system) then the three basis vectors are # equal to each other, thus we simply return the if len(vectors) == 1: vector = vectors[0] vector_string = f'{float(vector[0])} {float(vector[1])} {float(vector[2])}' if vector not in [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]: raise ValidationError( f'Polarisation vector ({vector_string}) does not correspond to a crystal basis vector (100, 010, 001)' ) powder_spectrum = spectra[0] powder_x = powder_spectrum.get_x()[1] powder_y = powder_spectrum.get_y()[0][1] powder_data = XyData() powder_data.set_x(powder_x, 'energy', 'eV') powder_data.set_y(powder_y, 'sigma', 'n/a') # if the system is dichoric (e.g. a hexagonal system) then the A and B periodic # dimensions are equal to each other by symmetry, thus the powder spectrum is simply # the average of 2x the 1 0 0 eps vector and 1x the 0 0 1 eps vector if len(vectors) == 2: # Since the individual vectors are labelled, we can extract just the spectra needed # to produce the powder and leave the rest spectrum_a = None spectrum_c = None for vector, spectrum in zip(vectors, spectra): vector_string = f'{float(vector[0])} {float(vector[1])} {float(vector[2])}' if vector in [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]: spectrum_a = spectrum elif vector == [0.0, 0.0, 1.0]: spectrum_c = spectrum else: raise ValidationError( f'Polarisation vector ({vector_string}) does not correspond to a crystal basis vector' ' (100, 010, 001)' ) if spectrum_a and not spectrum_c: raise ValidationError(f'Found no polarisation vector for the C-axis ([0, 0, 1]), found instead: {vectors}.') powder_x = spectrum_a.get_x()[1] yvals_a = spectrum_a.get_y()[0][1] yvals_c = spectrum_c.get_y()[0][1] powder_y = ((yvals_a * 2) + yvals_c) / 3 powder_data = XyData() powder_data.set_x(powder_x, 'energy', 'eV') powder_data.set_y(powder_y, 'sigma', 'n/a') # if the system is trichoric (e.g. a monoclinic system) then no periodic dimensions # are equal by symmetry, thus the powder spectrum is the average of the three basis # dipole vectors (1.0 0.0 0.0, 0.0 1.0 0.0, 0.0 0.0 1.0) if len(vectors) == 3: # Since the individual vectors are labelled, we can extract just the spectra needed to # produce the powder and leave the rest for vector, spectra in zip(vectors, spectra): vector_string = f'{float(vector[0])} {float(vector[1])} {float(vector[2])}' if vector == [1.0, 0.0, 0.0]: spectrum_a = spectra elif vector == [0.0, 1.0, 0.0]: spectrum_b = spectra elif vector == [0.0, 0.0, 1.0]: spectrum_c = spectra else: raise ValidationError( f'Polarisation vector ({vector_string}) does not correspond to a crystal basis vector' ' (100, 010, 001)' ) powder_x = spectrum_a.get_x()[1] yvals_a = spectrum_a.get_y()[0][1] yvals_b = spectrum_b.get_y()[0][1] yvals_c = spectrum_c.get_y()[0][1] powder_y = (yvals_a + yvals_b + yvals_c) / 3 powder_data = XyData() powder_data.set_x(powder_x, 'energy', 'eV') powder_data.set_y(powder_y, 'sigma', 'n/a') return powder_data