Exemple #1
0
def parse_bands_file(bands_lines):
    '''
    Parses the returned bands.1 and bands.2 file and returns a complete
    bandsData object. bands.1 has the form: k value, energy

    :param bands_lines: string of the read in bands file
    '''
    # TODO: not finished
    # read bands out of file:
    nrows = 0  # get number of rows (known form number of atom types
    bands_values = []  # init an array of arrays nkpoint * ...
    bands_labels = []  # label for each row.

    # fill and correct fermi energy.
    bands_values = []

    # TODO: we need to get the cell from StructureData node
    # and KpointsData node from inpxml
    fleur_bands = BandsData()
    # fleur_bands.set_cell(cell)
    #fleur_bands.set_kpoints(kpoints, cartesian=True)
    fleur_bands.set_bands(bands=bands_values, units='eV', labels=bands_labels)

    for line in bands_lines:
        pass
    return fleur_bands
Exemple #2
0
def bands_to_bandsdata(bands_info, kpoints, bands):
    """
    Convert the result of parser_dot_bands into a BandsData object

    :param bands_info: A dictionary of the informations of the bands file.
      contains field such as eferemi, units, cell
    :param kpoints: An array of the kpoints of the bands, rows are
      (kindex, kx, ky, kz, weight)
    :param bands: The actual bands array
    :return: A BandsData object
    :rtype: ``aiida.orm.bands.data.array.bands.BandsData``
    """

    bands_node = BandsData()

    # Extract the index of the kpoints
    kpn_array = np.asarray(kpoints)
    k_index = kpn_array[:, 0]

    # We need to restore the order of the kpoints
    k_sort = np.argsort(k_index)
    # Sort the kpn_array
    kpn_array = kpn_array[k_sort]

    _weights = kpn_array[:, -1]
    kpts = kpn_array[:, 1:-1]
    bands_node.set_kpoints(kpts, weights=_weights)

    # Sort the bands to match the order of the kpoints
    bands_array = np.asarray(bands)[k_sort]
    # We need to swap the axes from kpt,spin,engs to spin,kpt,engs
    bands_array = bands_array.swapaxes(0, 1)

    # Squeeze the first dimension e.g when there is a single spin
    if bands_array.shape[0] == 1:
        bands_array = bands_array[0]
    bands_array = bands_array * units['Eh']
    bands_info = dict(bands_info)  # Create a copy
    # Convert the units for the fermi energies
    if isinstance(bands_info['efermi'], list):
        bands_info['efermi'] = [x * units['Eh'] for x in bands_info['efermi']]
    else:
        bands_info['efermi'] = bands_info['efermi'] * units['Eh']

    bands_node.set_bands(bands_array, units="eV")
    # PBC is always true as this is PW DFT....
    bands_node.set_cell(bands_info['cell'], pbc=(True, True, True))

    # Store information from *.bands in the attributes
    # This is needs as we need to know the number of electrons
    # and the fermi energy
    for key, value in bands_info.items():
        bands_node.set_attribute(key, value)
    return bands_node
Exemple #3
0
    def test_bandsexport_single_kp(self):
        """
        Plot band for single k-point (issue #2462).
        """
        kpnts = KpointsData()
        kpnts.set_kpoints([[0., 0., 0.]])

        bands = BandsData()
        bands.set_kpointsdata(kpnts)
        bands.set_bands([[1.0, 2.0]])
        bands.store()

        # matplotlib
        options = [str(bands.id), '--format', 'mpl_singlefile']
        res = self.cli_runner.invoke(cmd_bands.bands_export,
                                     options,
                                     catch_exceptions=False)
        self.assertIn(
            b'p.scatter', res.stdout_bytes,
            'The string p.scatter was not found in the bands mpl export')

        # gnuplot
        with self.cli_runner.isolated_filesystem():
            options = [str(bands.id), '--format', 'gnuplot', '-o', 'bands.gnu']
            self.cli_runner.invoke(cmd_bands.bands_export,
                                   options,
                                   catch_exceptions=False)
            with open('bands.gnu', 'r') as gnu_file:
                res = gnu_file.read()
                self.assertIn(
                    'vectors nohead', res,
                    'The string "vectors nohead" was not found in the gnuplot script'
                )
Exemple #4
0
    def _has_empty_bands(self, bands_data: BandsData, thresh=0.005):
        """
        Check for the occupation of the BandsData

        There should be some empty bands if the calculation is a not a fixed occupation one.
        Otherwise, the final energy and forces are not reliable.
        """

        # Check if occupation is allowed to vary
        param = self.node.inputs.parameters.get_dict()['PARAM']
        # If it is a fixed occupation calculation we do not need to do anything about it....
        fix_occ = (param.get('fix_occupancy', False)
                   or param.get('metals_method', 'dm').lower() == 'none'
                   or param.get('elec_method', 'dm').lower() == 'none')
        if fix_occ:
            return True

        _, occ = bands_data.get_bands(also_occupations=True)

        nspin, nkppts, _ = occ.shape
        problems = []
        for ispin in range(nspin):
            for ikpts in range(nkppts):
                if occ[ispin, ikpts, -1] >= thresh:
                    problems.append((ispin, ikpts, occ[ispin, ikpts, -1]))
        if problems:
            for ispin, ikpts, val in problems:
                self.logger.warning(
                    "No empty bands for spin %d, kpoint %d - occ: %.5f", ispin,
                    ikpts, val)
            return False
        return True
Exemple #5
0
    def _parse_stdout(self, out_folder):
        """CP2K output parser"""

        from aiida.orm import BandsData, Dict

        # pylint: disable=protected-access

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE
        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        abs_fn = os.path.join(
            out_folder._repository._get_base_folder().abspath, fname)

        with io.open(abs_fn, mode="r", encoding="utf-8") as fobj:
            result_dict = parse_cp2k_output(fobj)

        if "nwarnings" not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        if "kpoint_data" in result_dict:
            bnds = BandsData()
            bnds.set_kpoints(result_dict["kpoint_data"]["kpoints"])
            bnds.labels = result_dict["kpoint_data"]["labels"]
            bnds.set_bands(
                result_dict["kpoint_data"]["bands"],
                units=result_dict["kpoint_data"]["bands_unit"],
            )
            self.out("output_bands", bnds)
            del result_dict["kpoint_data"]

        self.out("output_parameters", Dict(dict=result_dict))
Exemple #6
0
def create_aiida_bands_data(fleurinp, retrieved):
    """
    Creates :py:class:`aiida.orm.BandsData` object containing the kpoints and eigenvalues
    from the `banddos.hdf` file of the calculation

    :param fleurinp: :py:class:`~aiida_fleur.data.fleurinp.FleurinpData` for the calculation
    :param retrieved: :py:class:`aiida.orm.FolderData` for the bandstructure calculation

    :returns: :py:class:`aiida.orm.BandsData` for the bandstructure calculation

    :raises: ExitCode 300, banddos.hdf file is missing
    :raises: ExitCode 310, banddos.hdf reading failed
    :raises: ExitCode 320, reading kpointsdata from Fleurinp failed
    """
    from masci_tools.io.parsers.hdf5 import HDF5Reader, HDF5TransformationError
    from masci_tools.io.parsers.hdf5.recipes import FleurSimpleBands  #no projections only eigenvalues for now
    from aiida.engine import ExitCode

    try:
        kpoints = fleurinp.get_kpointsdata_ncf(only_used=True)
    except ValueError as exc:
        return ExitCode(
            320,
            message=f'Retrieving kpoints data from fleurinp failed with: {exc}'
        )

    if 'banddos.hdf' in retrieved.list_object_names():
        try:
            with retrieved.open('banddos.hdf', 'rb') as f:
                with HDF5Reader(f) as reader:
                    data, attributes = reader.read(recipe=FleurSimpleBands)
        except (HDF5TransformationError, ValueError) as exc:
            return ExitCode(310,
                            message=f'banddos.hdf reading failed with: {exc}')
    else:
        return ExitCode(300,
                        message='banddos.hdf file not in the retrieved files')

    bands = BandsData()
    bands.set_kpointsdata(kpoints)

    nkpts, nbands = attributes['nkpts'], attributes['nbands']
    eigenvalues = data['eigenvalues_up'].reshape((nkpts, nbands))
    if 'eigenvalues_down' in data:
        eigenvalues_dn = data['eigenvalues_down'].reshape((nkpts, nbands))
        eigenvalues = [eigenvalues, eigenvalues_dn]

    bands.set_bands(eigenvalues, units='eV')

    bands.label = 'output_banddos_wc_bands'
    bands.description = (
        'Contains BandsData for the bandstructure calculation')

    return bands
Exemple #7
0
    def _parse_stdout(self):
        """Advanced CP2K output file parser."""

        from aiida.orm import BandsData
        from aiida_cp2k.utils import parse_cp2k_output_advanced

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE  # pylint: disable=protected-access
        if fname not in self.retrieved.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        try:
            output_string = self.retrieved.get_object_content(fname)
        except IOError:
            return self.exit_codes.ERROR_OUTPUT_STDOUT_READ

        result_dict = parse_cp2k_output_advanced(output_string)

        # nwarnings is the last thing to be printed in th eCP2K output file:
        # if it is not there, CP2K didn't finish properly
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        if "aborted" in result_dict:
            return self.exit_codes.ERROR_OUTPUT_CONTAINS_ABORT

        # Compute the bandgap for Spin1 and Spin2 if eigen was parsed (works also with smearing!)
        if 'eigen_spin1_au' in result_dict:
            if result_dict['dft_type'] == "RKS":
                result_dict['eigen_spin2_au'] = result_dict['eigen_spin1_au']

            lumo_spin1_idx = result_dict['init_nel_spin1']
            lumo_spin2_idx = result_dict['init_nel_spin2']
            if (lumo_spin1_idx > len(result_dict['eigen_spin1_au'])-1) or \
               (lumo_spin2_idx > len(result_dict['eigen_spin2_au'])-1):
                #electrons jumped from spin1 to spin2 (or opposite): assume last eigen is lumo
                lumo_spin1_idx = len(result_dict['eigen_spin1_au']) - 1
                lumo_spin2_idx = len(result_dict['eigen_spin2_au']) - 1
            homo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx - 1]
            homo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx - 1]
            lumo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx]
            lumo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx]
            result_dict['bandgap_spin1_au'] = lumo_spin1 - homo_spin1
            result_dict['bandgap_spin2_au'] = lumo_spin2 - homo_spin2

        if "kpoint_data" in result_dict:
            bnds = BandsData()
            bnds.set_kpoints(result_dict["kpoint_data"]["kpoints"])
            bnds.labels = result_dict["kpoint_data"]["labels"]
            bnds.set_bands(
                result_dict["kpoint_data"]["bands"],
                units=result_dict["kpoint_data"]["bands_unit"],
            )
            self.out("output_bands", bnds)
            del result_dict["kpoint_data"]

        self.out("output_parameters", Dict(dict=result_dict))
        return None
    def test_valid_node():
        """Test that the correct exceptions are thrown for incompatible nodes."""
        from aiida.orm import ArrayData, BandsData

        # Invalid node type
        node = ArrayData().store()
        with pytest.raises(ValueError):
            get_highest_occupied_band(node)

        # The `occupations` array is missing
        node = BandsData()
        node.set_array('not_occupations', numpy.array([]))
        node.store()
        with pytest.raises(ValueError):
            get_highest_occupied_band(node)

        # The `occupations` array has incorrect shape
        node = BandsData()
        node.set_array('occupations', numpy.array([1., 1.]))
        node.store()
        with pytest.raises(ValueError):
            get_highest_occupied_band(node)
Exemple #9
0
    def _parse_stdout(self, out_folder):
        """Advanced CP2K output file parser"""

        from aiida.orm import BandsData
        from .parser_functions import parse_cp2k_output_advanced

        # pylint: disable=protected-access

        fname = self.node.process_class._DEFAULT_OUTPUT_FILE
        if fname not in out_folder._repository.list_object_names():
            raise OutputParsingError("Cp2k output file not retrieved")

        abs_fn = os.path.join(
            out_folder._repository._get_base_folder().abspath, fname)
        with io.open(abs_fn, mode="r", encoding="utf-8") as fobj:
            result_dict = parse_cp2k_output_advanced(fobj)

        # nwarnings is the last thing to be printed in th eCP2K output file:
        # if it is not there, CP2K didn't finish properly
        if 'nwarnings' not in result_dict:
            raise OutputParsingError("CP2K did not finish properly.")

        # Compute the bandgap for Spin1 and Spin2 if eigen was parsed (works also with smearing!)
        if 'eigen_spin1_au' in result_dict:
            if result_dict['dft_type'] == "RKS":
                result_dict['eigen_spin2_au'] = result_dict['eigen_spin1_au']

            lumo_spin1_idx = result_dict['init_nel_spin1']
            lumo_spin2_idx = result_dict['init_nel_spin2']
            if (lumo_spin1_idx > len(result_dict['eigen_spin1_au'])-1) or \
               (lumo_spin2_idx > len(result_dict['eigen_spin2_au'])-1):
                #electrons jumped from spin1 to spin2 (or opposite): assume last eigen is lumo
                lumo_spin1_idx = len(result_dict['eigen_spin1_au']) - 1
                lumo_spin2_idx = len(result_dict['eigen_spin2_au']) - 1
            homo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx - 1]
            homo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx - 1]
            lumo_spin1 = result_dict['eigen_spin1_au'][lumo_spin1_idx]
            lumo_spin2 = result_dict['eigen_spin2_au'][lumo_spin2_idx]
            result_dict['bandgap_spin1_au'] = lumo_spin1 - homo_spin1
            result_dict['bandgap_spin2_au'] = lumo_spin2 - homo_spin2

        if "kpoint_data" in result_dict:
            bnds = BandsData()
            bnds.set_kpoints(result_dict["kpoint_data"]["kpoints"])
            bnds.labels = result_dict["kpoint_data"]["labels"]
            bnds.set_bands(
                result_dict["kpoint_data"]["bands"],
                units=result_dict["kpoint_data"]["bands_unit"],
            )
            self.out("output_bands", bnds)
            del result_dict["kpoint_data"]

        self.out("output_parameters", Dict(dict=result_dict))
Exemple #10
0
def example_bands_v2(fresh_aiida_env):
    """Example eigen values and occupations"""
    from aiida.orm import BandsData
    bdata = BandsData()
    bdata.set_kpoints([[0, 0, 0], [0.25, 0.25, 0.25]])
    occ = np.array([[1, 1, 1, 0, 0, 0], [1, 1, 1, 0, 0, 0]], dtype=np.float64)
    eigen = np.array([[-1, -1, -1, 0, 0, 0], [-0.5, -0.5, -0.5, 0, 0, 0]])
    bdata.set_bands(bands=eigen, occupations=occ)
    return bdata
Exemple #11
0
def bands_from_castepbin(seedname, fmanager):
    """
    Acquire and prepare bands data from the castep_bin file instead
    """

    with fmanager.open(seedname + '.castep_bin', 'rb') as handle:
        binfile = CastepbinFile(fileobj=handle)

    bands_node = BandsData()
    kidx = binfile.kpoints_indices
    sort_idx = np.argsort(kidx)
    # Generated sorted arrays
    kpoints = binfile.kpoints[sort_idx, :]
    weights = binfile.kpoint_weights[sort_idx].astype(float)
    eigenvalues = binfile.eigenvalues[:, sort_idx, :]
    occupancies = binfile.occupancies[:, sort_idx, :]
    efermi = binfile.fermi_energy

    bands_node.set_kpoints(kpoints, weights=weights)
    bands_node.set_bands(eigenvalues, occupations=occupancies, units="eV")
    bands_node.set_cell(binfile.cell, pbc=(True, True, True))
    bands_node.set_attribute('efermi', efermi)

    return bands_node
Exemple #12
0
        def connect_structure_bands(strct):  # pylint: disable=unused-argument
            alat = 4.
            cell = np.array([
                [alat, 0., 0.],
                [0., alat, 0.],
                [0., 0., alat],
            ])

            kpnts = KpointsData()
            kpnts.set_cell(cell)
            kpnts.set_kpoints([[0., 0., 0.], [0.1, 0.1, 0.1]])

            bands = BandsData()
            bands.set_kpointsdata(kpnts)
            bands.set_bands([[1.0, 2.0], [3.0, 4.0]])
            return bands
    def test_spin_unpolarized():
        """Test the function for a non spin-polarized calculation meaning there will be a single spin channel."""
        from aiida.orm import BandsData

        occupations = numpy.array([
            [2., 2., 2., 2., 0.],
            [2., 2., 2., 2., 0.],
            [2., 2., 2., 2., 0.],
            [2., 2., 2., 2., 0.],
        ])

        bands = BandsData()
        bands.set_array('occupations', occupations)
        bands.store()
        h**o = get_highest_occupied_band(bands)
        assert h**o == 4
Exemple #14
0
        def connect_structure_bands(structure):
            alat = 4.
            cell = np.array([
                [alat, 0., 0.],
                [0., alat, 0.],
                [0., 0., alat],
            ])

            k = KpointsData()
            k.set_cell(cell)
            k.set_kpoints([[0., 0., 0.], [0.1, 0.1, 0.1]])

            b = BandsData()
            b.set_kpointsdata(k)
            b.set_bands([[1.0, 2.0], [3.0, 4.0]])

            return b
Exemple #15
0
    def test_spin_polarized(self, fixture_database):
        """Test the function for a spin-polarized calculation meaning there will be two spin channels."""
        from aiida.orm import BandsData

        occupations = numpy.array([
            [
                [2., 2., 2., 2., 0.],
                [2., 2., 2., 2., 0.],
            ],
            [
                [2., 2., 2., 2., 0.],
                [2., 2., 2., 2., 0.],
            ]
        ])

        bands = BandsData()
        bands.set_array('occupations', occupations)
        bands.store()
        h**o = get_highest_occupied_band(bands)
        assert h**o == 4
Exemple #16
0
    def _parse_gsr(self):
        """Abinit GSR parser."""
        # Output GSR Abinit NetCDF file - Default name is aiidao_GSR.nc
        fname = f'{self.node.get_attribute("prefix")}o_GSR.nc'
        # Absolute path of the folder in which aiidao_GSR.nc is stored
        path = self.node.get_remote_workdir()

        if fname not in self.retrieved.list_object_names():
            return self.exit_codes.ERROR_MISSING_OUTPUT_FILES

        with abilab.abiopen(path + '/' + fname) as gsr:
            gsr_data = {
                'abinit_version':
                gsr.abinit_version,
                'cart_stress_tensor':
                gsr.cart_stress_tensor.tolist(),
                'cart_stress_tensor' + UNITS_SUFFIX:
                DEFAULT_STRESS_UNITS,
                'is_scf_run':
                bool(gsr.is_scf_run),
                # 'cart_forces': gsr.cart_forces.tolist(),
                # 'cart_forces' + units_suffix: DEFAULT_FORCE_UNITS,
                'forces':
                gsr.cart_forces.tolist(),  # backwards compatibility
                'forces' + UNITS_SUFFIX:
                DEFAULT_FORCE_UNITS,
                'energy':
                float(gsr.energy),
                'energy' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_localpsp':
                float(gsr.energy_terms.e_localpsp),
                'e_localpsp' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_eigenvalues':
                float(gsr.energy_terms.e_eigenvalues),
                'e_eigenvalues' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_ewald':
                float(gsr.energy_terms.e_ewald),
                'e_ewald' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_hartree':
                float(gsr.energy_terms.e_hartree),
                'e_hartree' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_corepsp':
                float(gsr.energy_terms.e_corepsp),
                'e_corepsp' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_corepspdc':
                float(gsr.energy_terms.e_corepspdc),
                'e_corepspdc' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_kinetic':
                float(gsr.energy_terms.e_kinetic),
                'e_kinetic' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_nonlocalpsp':
                float(gsr.energy_terms.e_nonlocalpsp),
                'e_nonlocalpsp' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_entropy':
                float(gsr.energy_terms.e_entropy),
                'e_entropy' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'entropy':
                float(gsr.energy_terms.entropy),
                'entropy' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_xc':
                float(gsr.energy_terms.e_xc),
                'e_xc' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_xcdc':
                float(gsr.energy_terms.e_xcdc),
                'e_xcdc' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_paw':
                float(gsr.energy_terms.e_paw),
                'e_paw' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_pawdc':
                float(gsr.energy_terms.e_pawdc),
                'e_pawdc' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_elecfield':
                float(gsr.energy_terms.e_elecfield),
                'e_elecfield' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_magfield':
                float(gsr.energy_terms.e_magfield),
                'e_magfield' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_fermie':
                float(gsr.energy_terms.e_fermie),
                'e_fermie' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_sicdc':
                float(gsr.energy_terms.e_sicdc),
                'e_sicdc' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_exactX':
                float(gsr.energy_terms.e_exactX),
                'e_exactX' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'h0':
                float(gsr.energy_terms.h0),
                'h0' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_electronpositron':
                float(gsr.energy_terms.e_electronpositron),
                'e_electronpositron' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'edc_electronpositron':
                float(gsr.energy_terms.edc_electronpositron),
                'edc_electronpositron' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e0_electronpositron':
                float(gsr.energy_terms.e0_electronpositron),
                'e0_electronpositron' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'e_monopole':
                float(gsr.energy_terms.e_monopole),
                'e_monopole' + UNITS_SUFFIX:
                DEFAULT_ENERGY_UNITS,
                'pressure':
                float(gsr.pressure),
                'pressure' + UNITS_SUFFIX:
                DEFAULT_STRESS_UNITS
            }

            try:
                # will return an integer 0 if non-magnetic calculation is run; convert it to a float
                total_magnetization = float(gsr.ebands.get_collinear_mag())
                gsr_data['total_magnetization'] = total_magnetization
                gsr_data['total_magnetization' +
                         UNITS_SUFFIX] = DEFAULT_MAGNETIZATION_UNITS
            except ValueError as valerr:
                # get_collinear_mag will raise ValueError if it doesn't know what to do
                if 'Cannot calculate collinear magnetization' in valerr.args[
                        0]:
                    pass
                else:
                    raise valerr

            try:
                bands_data = BandsData()
                bands_data.set_kpoints(gsr.ebands.kpoints.get_cart_coords())
                bands_data.set_bands(np.array(gsr.ebands.eigens),
                                     units=str(gsr.ebands.eigens.unit))
                self.out('output_bands', bands_data)
            except:  # pylint: disable=bare-except
                pass

        self.out('output_parameters', Dict(dict=gsr_data))
Exemple #17
0
    def create_structure_bands():
        """Create bands structure object."""
        alat = 4.  # angstrom
        cell = [
            [
                alat,
                0.,
                0.,
            ],
            [
                0.,
                alat,
                0.,
            ],
            [
                0.,
                0.,
                alat,
            ],
        ]
        strct = StructureData(cell=cell)
        strct.append_atom(position=(0., 0., 0.), symbols='Fe')
        strct.append_atom(position=(alat / 2., alat / 2., alat / 2.),
                          symbols='O')
        strct.store()

        @calcfunction
        def connect_structure_bands(strct):  # pylint: disable=unused-argument
            alat = 4.
            cell = np.array([
                [alat, 0., 0.],
                [0., alat, 0.],
                [0., 0., alat],
            ])

            kpnts = KpointsData()
            kpnts.set_cell(cell)
            kpnts.set_kpoints([[0., 0., 0.], [0.1, 0.1, 0.1]])

            bands = BandsData()
            bands.set_kpointsdata(kpnts)
            bands.set_bands([[1.0, 2.0], [3.0, 4.0]])
            return bands

        bands = connect_structure_bands(strct)

        bands_isolated = BandsData()
        bands_isolated.store()

        # Create 2 groups and add the data to one of them
        g_ne = Group(label='non_empty_group')
        g_ne.store()
        g_ne.add_nodes(bands)
        g_ne.add_nodes(bands_isolated)

        g_e = Group(label='empty_group')
        g_e.store()

        return {
            DummyVerdiDataListable.NODE_ID_STR: bands.id,
            DummyVerdiDataListable.NON_EMPTY_GROUP_ID_STR: g_ne.id,
            DummyVerdiDataListable.EMPTY_GROUP_ID_STR: g_e.id
        }
    def test_threshold():
        """Test the `threshold` parameter."""
        from aiida.orm import BandsData

        threshold = 0.002

        bands = BandsData()
        bands.set_array('occupations',
                        numpy.array([[2., 2., 2., 2., 0.001, 0.0015]]))
        bands.store()

        # All bands above the LUMO (occupation of 0.001) are below `2 * threshold`
        h**o = get_highest_occupied_band(bands, threshold=threshold)
        assert h**o == 4

        bands = BandsData()
        bands.set_array('occupations',
                        numpy.array([[2., 2., 2., 2., 0.001, 0.003]]))
        bands.store()

        # A band above the LUMO (occupation of 0.001) has an occupation above `2 * threshold`
        with pytest.raises(ValueError):
            get_highest_occupied_band(bands, threshold=threshold)
Exemple #19
0
def band_parser_legacy(band_dat, band_kpt, special_points, structure):  # pylint: disable=too-many-locals
    """
    Parsers the bands output data, along with the special points retrieved
    from the input kpoints to construct a BandsData object which is then
    returned. Cannot handle discontinuities in the kpath, if two points are
    assigned to same spot only one will be passed. Used for wannier90 < 3.0
    :param band_dat: list of str with each str stores one line of aiida_band.dat file
    :param band_kpt: list of str with each str stores one line of aiida_band.kpt file
    :param special_points: special points to add labels to the bands a dictionary in
        the form expected in the input as described in the wannier90 documentation
    :return: BandsData object constructed from the input params,
        and a list contains warnings.
    """
    import numpy as np

    from aiida.orm import BandsData
    from aiida.orm import KpointsData

    warnings = []
    warnings.append((
        "Note: no file named SEEDNAME_band.labelinfo.dat found. "
        "You are probably using a version of Wannier90 before 3.0. "
        "There, the labels associated with each k-points were not printed in output "
        "and there were also cases in which points were not calculated "
        "(see issue #195 on the Wannier90 GitHub page). "
        "I will anyway try to do my best to assign labels, "
        "but the assignment might be wrong "
        "(especially if there are path discontinuities)."))

    # imports the data
    out_kpt = np.genfromtxt(band_kpt, skip_header=1, usecols=(0, 1, 2))
    out_dat = np.genfromtxt(band_dat, usecols=1)

    # reshaps the output bands
    out_dat = out_dat.reshape(len(out_kpt), (len(out_dat) // len(out_kpt)),
                              order="F")

    # finds expected points of discontinuity
    kpath = special_points['path']
    cont_break = [(i, (kpath[i - 1][1], kpath[i][0]))
                  for i in range(1, len(kpath))
                  if kpath[i - 1][1] != kpath[i][0]]

    # finds the special points
    special_points_dict = special_points['point_coords']
    # We set atol to 1e-5 because in the kpt file the coords are printed with fixed precision
    labels = [
        (i, k) for k in special_points_dict for i in range(len(out_kpt))
        if all(
            np.isclose(special_points_dict[k], out_kpt[i], rtol=0, atol=1.e-5))
    ]
    labels.sort()

    # Checks and appends labels if discontinuity
    appends = []
    for x in cont_break:
        # two cases the break is before or the break is after
        # if the break is before
        if labels[x[0]][1] != x[1][0]:
            # checks to see if the discontinuity was already there
            if labels[x[0] - 1] == x[1][0]:
                continue
            insert_point = x[0]
            new_label = x[1][0]
            kpoint = labels[x[0]][0] - 1
            appends += [[insert_point, new_label, kpoint]]
        # if the break is after
        if labels[x[0]][1] != x[1][1]:
            # checks to see if the discontinuity was already there
            if labels[x[0] + 1] == x[1][1]:
                continue
            insert_point = x[0] + 1
            new_label = x[1][1]
            kpoint = labels[x[0]][0] + 1
            appends += [[insert_point, new_label, kpoint]]
    appends.sort()

    for i, append in enumerate(appends):
        labels.insert(append[0] + i, (append[2], six.text_type(append[1])))
    bands = BandsData()
    k = KpointsData()
    k.set_cell_from_structure(structure)
    k.set_kpoints(out_kpt, cartesian=False)
    bands.set_kpointsdata(k)
    bands.set_bands(out_dat, units='eV')
    bands.labels = labels
    return bands, warnings
Exemple #20
0
def spin_dependent_subparser(out_info_dict):
    """This find the projection and bands arrays from the out_file and out_info_dict. Used to handle the different
    possible spin-cases in a convenient manner.

    :param out_info_dict: contains various technical internals useful in parsing
    :return: ProjectionData, BandsData parsed from out_file
    """

    out_file = out_info_dict['out_file']
    spin_down = out_info_dict['spin_down']
    od = out_info_dict  #using a shorter name for convenience
    #   regular expressions needed for later parsing
    WaveFraction1_re = re.compile(r'\=(.*?)\*')  # state composition 1
    WaveFractionremain_re = re.compile(r'\+(.*?)\*')  # state comp 2
    FunctionId_re = re.compile(r'\#(.*?)\]')  # state identity
    # primes arrays for the later parsing
    num_wfc = len(od['wfc_lines'])
    bands = np.zeros([od['k_states'], od['num_bands']])
    projection_arrays = np.zeros([od['k_states'], od['num_bands'], num_wfc])

    try:
        for i in range(od['k_states']):
            if spin_down:
                i += od['k_states']
            # grabs band energy
            for j in range(i * od['num_bands'], (i + 1) * od['num_bands'], 1):
                out_ind = od['e_lines'][j]
                try:
                    # post ~6.3 output format "e ="
                    val = out_file[out_ind].split()[2]
                    float(val)
                except ValueError:
                    # pre ~6.3 output format? "==== e("
                    val = out_file[out_ind].split()[4]
                bands[i % od['k_states']][j % od['num_bands']] = val
                #subloop grabs pdos
                wave_fraction = []
                wave_id = []
                for k in range(od['e_lines'][j] + 1, od['psi_lines'][j], 1):
                    out_line = out_file[k]
                    wave_fraction += WaveFraction1_re.findall(out_line)
                    wave_fraction += WaveFractionremain_re.findall(out_line)
                    wave_id += FunctionId_re.findall(out_line)
                if len(wave_id) != len(wave_fraction):
                    raise IndexError
                for l in range(len(wave_id)):
                    wave_id[l] = int(wave_id[l])
                    wave_fraction[l] = float(wave_fraction[l])
                    #sets relevant values in pdos_array
                    projection_arrays[i % od['k_states']][j % od['num_bands']][
                        wave_id[l] - 1] = wave_fraction[l]
    except IndexError:
        raise QEOutputParsingError(
            'the standard out file does not comply with the official documentation.'
        )

    bands_data = BandsData()
    # Attempts to retrieve the kpoints from the parent calc
    parent_calc = out_info_dict['parent_calc']
    try:
        parent_kpoints = parent_calc.get_incoming(
            link_label_filter='kpoints').one().node
    except ValueError:
        raise QEOutputParsingError(
            'The parent had no input kpoints! Cannot parse from this!')
    try:
        if len(od['k_vect']) != len(parent_kpoints.get_kpoints()):
            raise AttributeError
        bands_data.set_kpointsdata(parent_kpoints)
    except AttributeError:
        bands_data.set_kpoints(od['k_vect'].astype(float))

    bands_data.set_bands(bands, units='eV')

    orbitals = out_info_dict['orbitals']
    if len(orbitals) != np.shape(projection_arrays[0, 0, :])[0]:
        raise QEOutputParsingError('There was an internal parsing error, '
                                   ' the projection array shape does not agree'
                                   ' with the number of orbitals')
    projection_data = ProjectionData()
    projection_data.set_reference_bandsdata(bands_data)
    projections = [projection_arrays[:, :, i] for i in range(len(orbitals))]

    # Do the bands_check manually here
    for projection in projections:
        if np.shape(projection) != np.shape(bands):
            raise AttributeError('Projections not the same shape as the bands')

    #insert here some logic to assign pdos to the orbitals
    pdos_arrays = spin_dependent_pdos_subparser(out_info_dict)
    energy_arrays = [out_info_dict['energy']] * len(orbitals)
    projection_data.set_projectiondata(orbitals,
                                       list_of_projections=projections,
                                       list_of_energy=energy_arrays,
                                       list_of_pdos=pdos_arrays,
                                       bands_check=False)
    # pdos=pdos_arrays
    return bands_data, projection_data
Exemple #21
0
    def parse(self, **kwargs):  # noqa: MC0001  - is mccabe too complex funct -
        """
        Receives in input a dictionary of retrieved nodes. Does all the logic here.
        """
        from aiida.engine import ExitCode

        parser_info = {}
        parser_info['parser_info'] = 'AiiDA Siesta Parser V. {}'.format(
            self._version)

        try:
            output_folder = self.retrieved
        except exceptions.NotExistent:
            raise OutputParsingError("Folder not retrieved")

        output_path, messages_path, xml_path, json_path, bands_path, basis_enthalpy_path = \
            self._fetch_output_files(output_folder)

        if xml_path is None:
            raise OutputParsingError("Xml file not retrieved")
        xmldoc = get_parsed_xml_doc(xml_path)
        result_dict = get_dict_from_xml_doc(xmldoc)

        if output_path is None:
            raise OutputParsingError("output file not retrieved")

        output_dict = dict(
            list(result_dict.items()) + list(parser_info.items()))

        warnings_list = []

        if json_path is not None:
            from .json_time import get_timing_info
            global_time, timing_decomp = get_timing_info(json_path)
            if global_time is None:
                warnings_list.append(["Cannot fully parse the time.json file"])
            else:
                output_dict["global_time"] = global_time
                output_dict["timing_decomposition"] = timing_decomp

        if basis_enthalpy_path is not None:
            the_file = open(basis_enthalpy_path)
            bas_enthalpy = float(the_file.read().split()[0])
            the_file.close()
            output_dict["basis_enthalpy"] = bas_enthalpy
            output_dict["basis_enthalpy_units"] = "eV"
        else:
            warnings_list.append(["BASIS_ENTHALPY file not retrieved"])

        have_errors_to_analyse = False
        if messages_path is None:
            # Perhaps using an old version of Siesta
            warnings_list.append([
                'WARNING: No MESSAGES file, could not check if calculation terminated correctly'
            ])
        else:
            have_errors_to_analyse = True
            #succesful when "INFO: Job completed" is present in message files
            succesful, from_message = self._get_warnings_from_file(
                messages_path)
            warnings_list.append(from_message)
        output_dict["warnings"] = warnings_list

        # An output_parametrs port is always return, even if only parser's info are present
        output_data = Dict(dict=output_dict)
        self.out('output_parameters', output_data)

        #
        # When using floating sites, Siesta associates 'atomic positions' to them, and
        # the structure and forces in the XML file include these fake atoms.
        # In order to return physical structures and forces, we need to remove them.
        # Recall that the input structure is the physical one, and the floating sites
        # are specified in the 'basis' input
        #
        physical_structure = self.node.inputs.structure
        number_of_real_atoms = len(physical_structure.sites)

        # If the structure has changed, save it
        #
        if output_dict['variable_geometry']:
            in_struc = self.node.inputs.structure
            # The next function never fails. If problems arise, the initial structure is
            # returned. The input structure is also necessary because the CML file
            # traditionally contains only the atomic symbols and not the site names.
            # The returned structure does not have any floating atoms, they are removed
            # in the `get_last_structure` call.
            success, out_struc = get_last_structure(xmldoc, in_struc)
            if not success:
                self.logger.warning(
                    "Problem in parsing final structure, returning inp structure in output_structure"
                )

            self.out('output_structure', out_struc)

        # Attempt to parse forces and stresses. In case of failure "None" is returned.
        # Therefore the function never crashes
        forces, stress = get_final_forces_and_stress(xmldoc)
        if forces is not None and stress is not None:
            from aiida.orm import ArrayData
            arraydata = ArrayData()
            arraydata.set_array('forces',
                                np.array(forces[0:number_of_real_atoms]))
            arraydata.set_array('stress', np.array(stress))
            self.out('forces_and_stress', arraydata)

        #Attempt to parse the ion files. Files ".ion.xml" are not produced by siesta if ions file are used
        #in input (`user-basis = T`). This explains the first "if" statement. The SiestaCal input is called
        #`ions__El` (El is the element label) therefore we look for the str "ions" in any of the inputs name.
        if not any(["ions" in inp for inp in self.node.inputs]):  #pylint: disable=too-many-nested-blocks
            from aiida_siesta.data.ion import IonData
            ions = {}
            #Ions from the structure
            in_struc = self.node.inputs.structure
            for kind in in_struc.get_kind_names():
                ion_file_name = kind + ".ion.xml"
                if ion_file_name in output_folder._repository.list_object_names(
                ):
                    ion_file_path = os.path.join(
                        output_folder._repository._get_base_folder().abspath,
                        ion_file_name)
                    ions[kind] = IonData(ion_file_path)
                else:
                    self.logger.warning(f"no ion file retrieved for {kind}")
            #Ions from floating_sites
            if "basis" in self.node.inputs:
                basis_dict = self.node.inputs.basis.get_dict()
                if "floating_sites" in basis_dict:
                    floating_kinds = []
                    for orb in basis_dict["floating_sites"]:
                        if orb["name"] not in floating_kinds:
                            floating_kinds.append(orb["name"])
                            ion_file_name = orb["name"] + ".ion.xml"
                            if ion_file_name in output_folder._repository.list_object_names(
                            ):
                                ion_file_path = os.path.join(
                                    output_folder._repository._get_base_folder(
                                    ).abspath, ion_file_name)
                                ions[orb["name"]] = IonData(ion_file_path)
                            else:
                                self.logger.warning(
                                    f"no ion file retrieved for {orb['name']}")
            #Return the outputs
            if ions:
                self.out('ion_files', ions)

        # Error analysis
        if have_errors_to_analyse:
            # No metter if "INFO: Job completed" is present (succesfull) or not, we check for known
            # errors. They might apprear as WARNING (therefore with succesful True) or FATAL
            # (succesful False)
            for line in from_message:
                if u'split options' in line:
                    min_split = get_min_split(output_path)
                    if min_split:
                        self.logger.error(
                            "Error in split_norm option. Minimum value is {}".
                            format(min_split))
                        return self.exit_codes.SPLIT_NORM
                if u'sys::die' in line:
                    #This is the situation when siesta dies with no specified error
                    #to be reported in "MESSAGES", unfortunately some interesting cases
                    #are treated in this way, we explore the .out file for more insights.
                    if is_polarization_problem(output_path):
                        return self.exit_codes.BASIS_POLARIZ
                if u'SCF_NOT_CONV' in line:
                    return self.exit_codes.SCF_NOT_CONV
                if u'GEOM_NOT_CONV' in line:
                    return self.exit_codes.GEOM_NOT_CONV

        #Because no known error has been found, attempt to parse bands if requested
        if bands_path is None:
            if "bandskpoints" in self.node.inputs:
                return self.exit_codes.BANDS_FILE_NOT_PRODUCED
        else:
            #bands, coords = self._get_bands(bands_path)
            try:
                bands = self._get_bands(bands_path)
            except (ValueError, IndexError):
                return self.exit_codes.BANDS_PARSE_FAIL
            from aiida.orm import BandsData
            arraybands = BandsData()
            #Reset the cell for KpointsData of bands, necessary
            #for bandskpoints without cell and if structure changed
            bkp = self.node.inputs.bandskpoints.clone()
            if output_dict['variable_geometry']:
                bkp.set_cell_from_structure(out_struc)
            else:
                bkp.set_cell_from_structure(self.node.inputs.structure)
            arraybands.set_kpointsdata(bkp)
            arraybands.set_bands(bands, units="eV")
            self.out('bands', arraybands)
            #bandsparameters = Dict(dict={"kp_coordinates": coords})
            #self.out('bands_parameters', bandsparameters)

        #At the very end, return a particular exit code if "INFO: Job completed"
        #was not present in the MESSAGES file, but no known error is detected.
        if have_errors_to_analyse:
            if not succesful:
                self.logger.error(
                    'The calculation finished without "INFO: Job completed", but no '
                    'error could be processed. Might be that the calculation was killed externally'
                )
                return self.exit_codes.UNEXPECTED_TERMINATION

        return ExitCode(0)
Exemple #22
0
    def _get_output_nodes(self, output_path, messages_path, xml_path,
                          json_path, bands_path):
        """
        Extracts output nodes from the standard output and standard error
        files. (And XML and JSON files)
        """
        from aiida.orm import TrajectoryData
        import re

        parser_version = '1.0.1'
        parser_info = {}
        parser_info['parser_info'] = 'AiiDA Siesta Parser V. {}'.format(
            parser_version)
        parser_info['parser_warnings'] = []

        #No need for checks anymore

        xmldoc = get_parsed_xml_doc(xml_path)

        in_struc = self.node.inputs.structure

        try:
            in_settings = self.node.inputs.settings
        except exceptions.NotExistent:
            in_settings = None

        result_dict = get_dict_from_xml_doc(xmldoc)

        # Add timing information
        #if json_path is None:
        ###TODO###
        #Not sure how to implement what once was logger.info
        #Also not sure I understood the purpose of this file
        if json_path is not None:
            from .json_time import get_timing_info
            global_time, timing_decomp = get_timing_info(json_path)
            if global_time is None:
                raise OutputParsingError(
                    "Cannot fully parse the time.json file")
            else:
                result_dict["global_time"] = global_time
                result_dict["timing_decomposition"] = timing_decomp

        # Add warnings
        if messages_path is None:
            # Perhaps using an old version of Siesta
            warnings_list = ['WARNING: No MESSAGES file...']
        else:
            successful, warnings_list = self.get_warnings_from_file(
                messages_path)

        ###TODO### or maybe not
        #How do we process this successfull booelan?

        result_dict["warnings"] = warnings_list

        # Add parser info dictionary
        parsed_dict = dict(
            list(result_dict.items()) + list(parser_info.items()))

        output_data = Dict(dict=parsed_dict)

        self.out('output_parameters', output_data)

        # If the structure has changed, save it
        if is_variable_geometry(xmldoc):
            # Get the input structure to copy its site names,
            # as the CML file traditionally contained only the
            # atomic symbols.
            #
            struc = get_last_structure(xmldoc, in_struc)
            # result_list.append((self.get_linkname_outstructure(),struc))
            self.out(self.get_linkname_outstructure(), struc)

        # Save forces and stress in an ArrayData object
        forces, stress = get_final_forces_and_stress(xmldoc)

        if forces is not None and stress is not None:
            # from aiida.orm.nodes.array import ArrayData
            from aiida.orm import ArrayData
            arraydata = ArrayData()
            arraydata.set_array('forces', np.array(forces))
            arraydata.set_array('stress', np.array(stress))
            # result_list.append((self.get_linkname_fs_and_stress(),arraydata))
            self.out(self.get_linkname_fs_and_stress(), arraydata)

        # Parse band-structure information if available
        if bands_path is not None:
            bands, coords = self.get_bands(bands_path)
            from aiida.orm import BandsData
            arraybands = BandsData()
            f = self.node.inputs.bandskpoints  #Temporary workaround due to a bug
            #f._set_reciprocal_cell()         #in KpointData (issue #2749)
            arraybands.set_kpoints(f.get_kpoints(cartesian=True))
            arraybands.labels = f.labels
            arraybands.set_bands(bands, units="eV")
            self.out(self.get_linkname_bands(), arraybands)
            bandsparameters = Dict(dict={"kp_coordinates": coords})
            self.out(self.get_linkname_bandsparameters(), bandsparameters)

        return result_dict
Exemple #23
0
def band_parser(band_dat, band_kpt, band_labelinfo, structure):  # pylint: disable=too-many-locals
    """
    Parsers the bands output data to construct a BandsData object which is then
    returned. Used for wannier90 >= 3.0

    :param band_dat: list of str with each str stores one line of aiida_band.dat file
    :param band_kpt: list of str with each str stores one line of aiida_band.kpt file
    :param band_labelinfo: list of str with each str stores one line in aiida_band.labelinfo.dat file
    :return: BandsData object constructed from the input params
    """
    import numpy as np

    from aiida.orm import BandsData
    from aiida.orm import KpointsData

    warnings = []

    # imports the data
    out_kpt = np.genfromtxt(band_kpt, skip_header=1, usecols=(0, 1, 2))
    out_dat = np.genfromtxt(band_dat, usecols=1)

    # reshaps the output bands
    out_dat = out_dat.reshape(len(out_kpt), (len(out_dat) // len(out_kpt)),
                              order="F")

    labels_dict = {}
    for line_idx, line in enumerate(band_labelinfo, start=1):
        if not line.strip():
            continue
        try:
            # label, idx, xval, kx, ky, kz = line.split()
            label, idx, _, _, _, _ = line.split()
        except ValueError:
            warnings.append(
                ('Wrong number of items in line {} of the labelinfo file - '
                 'I will not assign that label')).format(line_idx)
            continue
        try:
            idx = int(idx)
        except ValueError:
            warnings.append((
                "Invalid value for the index in line {} of the labelinfo file, "
                "it's not an integer - I will not assign that label"
            )).format(line_idx)
            continue

        # I use a dictionary because there are cases in which there are
        # two lines for the same point (e.g. when I do a zero-length path,
        # from a point to the same point, just to have that value)
        # Note the -1 because in fortran indices are 1-based, in Python are
        # 0-based
        labels_dict[idx - 1] = label
    labels = [(key, labels_dict[key]) for key in sorted(labels_dict)]

    bands = BandsData()
    k = KpointsData()
    k.set_cell_from_structure(structure)
    k.set_kpoints(out_kpt, cartesian=False)
    bands.set_kpointsdata(k)
    bands.set_bands(out_dat, units='eV')
    bands.labels = labels
    return bands, warnings
Exemple #24
0
 def _aiida_bands_data(self, data, cell, kpoints_dict):
     if not data:
         return False
     kpt_idx = sorted(data.keys())  #  list of kpoint indices
     try:
         k_list = [kpoints_dict[i]
                   for i in kpt_idx]  # list of k-point triplet
     except KeyError:
         # kpoint triplets are not present (true  for .qp and so on, can not use BandsData)
         # We use the internal Yambo Format  [ [Eo_1, Eo_2,... ], ...[So_1,So_2,] ]
         #                                  QP_TABLE  [[ib_1,ik_1,isp_1]      ,[ib_n,ik_n,isp_n]]
         # Each entry in DATA has corresponding legend in QP_TABLE that defines its details
         # like   ib= Band index,  ik= kpoint index,  isp= spin polarization index.
         #  Eo_1 =>  at ib_1, ik_1 isp_1.
         pdata = ArrayData()
         QP_TABLE = []
         ORD = []
         Eo = []
         E_minus_Eo = []
         So = []
         Z = []
         for ky in data.keys():  # kp == kpoint index as a string  1,2,..
             for ind in range(len(data[ky]['Band'])):
                 try:
                     Eo.append(data[ky]['Eo'][ind])
                 except KeyError:
                     pass
                 try:
                     E_minus_Eo.append(data[ky]['E-Eo'][ind])
                 except KeyError:
                     pass
                 try:
                     So.append(data[ky]['Sc|Eo'][ind])
                 except KeyError:
                     pass
                 try:
                     Z.append(data[ky]['Z'][ind])
                 except KeyError:
                     pass
                 ik = int(ky)
                 ib = data[ky]['Band'][ind]
                 isp = 0
                 if 'Spin_Pol' in list(data[ky].keys()):
                     isp = data[ky]['Spin_Pol'][ind]
                 QP_TABLE.append([ik, ib, isp])
         pdata.set_array('Eo', numpy.array(Eo))
         pdata.set_array('E_minus_Eo', numpy.array(E_minus_Eo))
         pdata.set_array('So', numpy.array(So))
         pdata.set_array('Z', numpy.array(Z))
         pdata.set_array('qp_table', numpy.array(QP_TABLE))
         return pdata
     quasiparticle_bands = BandsData()
     quasiparticle_bands.set_cell(cell)
     quasiparticle_bands.set_kpoints(k_list, cartesian=True)
     # labels will come from any of the keys in the nested  kp_point data,
     # there is a uniform set of observables for each k-point, ie Band, Eo, ...
     # ***FIXME BUG does not seem to handle spin polarizes at all when constructing bandsdata***
     bands_labels = [
         legend for legend in sorted(data[list(data.keys())[0]].keys())
     ]
     append_list = [[] for i in bands_labels]
     for kp in kpt_idx:
         for i in range(len(bands_labels)):
             append_list[i].append(data[kp][bands_labels[i]])
     generalised_bands = [numpy.array(it) for it in append_list]
     quasiparticle_bands.set_bands(bands=generalised_bands,
                                   units='eV',
                                   labels=bands_labels)
     return quasiparticle_bands