Beispiel #1
0
def read_dos(bands_file,
             pdos_file=None,
             cell_file=None,
             bin_width=0.01,
             gaussian=None,
             padding=None,
             emin=None,
             emax=None,
             efermi_to_vbm=True,
             lm_orbitals=None,
             elements=None,
             atoms=None,
             total_only=False):
    """Convert DOS data from CASTEP .bands file to Pymatgen/Sumo format

    The data is binned into a regular series using np.histogram

    Args:
        bands_file (:obj:`str`): Path to CASTEP prefix.bands output file. The
            k-point positions, weights and eigenvalues are read from this file.
        pdos_bin (:obj:`str`): Path to CASTEP prefix.pdos_bin output file. The
            weights of projected density of states are read from this file.
        bin_width (:obj:`float`, optional): Spacing for DOS energy axis
        gaussian (:obj:`float` or None, optional): Width of Gaussian broadening
            function
        padding (:obj:`float`, optional): Energy range above and below occupied
            region. (This is not used if xmin and xmax are set.)
        emin (:obj:`float`, optional): Minimum energy value for output DOS)
        emax (:obj:`float`, optional): Maximum energy value for output DOS
        efermi_to_vbm (:obj:`bool`, optional):
            If a bandgap is detected, modify the stored Fermi energy
            so that it lies at the VBM.

    Returns:
        (:obj:`pymatgen.electronic_structure.dos.Dos`, dict)

        where the dict is either empty or contains a PDOS arranged::

          {species: {orbital: Dos}}
    """

    header = _read_bands_header_verbose(bands_file)

    logging.info("Reading band eigenvalues...")
    _, weights, eigenvalues = read_bands_eigenvalues(bands_file, header)

    calc_efermi = header['e_fermi'][0] * _ry_to_ev * 2
    if efermi_to_vbm and not _is_metal(eigenvalues, calc_efermi):
        logging.info("Setting energy zero to VBM")
        efermi = _get_vbm(eigenvalues, calc_efermi)
    else:
        logging.info("Setting energy zero to Fermi energy")
        efermi = calc_efermi

    emin_data = min(eigenvalues[Spin.up].flatten())
    emax_data = max(eigenvalues[Spin.up].flatten())
    if Spin.down in eigenvalues:
        emin_data = min(emin_data, min(eigenvalues[Spin.down].flatten()))
        emax_data = max(emax_data, max(eigenvalues[Spin.down].flatten()))

    if padding is None and gaussian:
        padding = gaussian * 3
    elif padding is None:
        padding = 0.5

    if emin is None:
        emin = emin_data - padding
    if emax is None:
        emax = emax_data + padding

    # Shift sampling window to account for zeroing at VBM/EFermi
    emin += efermi
    emax += efermi

    bins = np.arange(emin, emax + bin_width, bin_width)
    energies = (bins[1:] + bins[:-1]) / 2

    # Add rows to weights for each band so they are aligned with eigenval data
    weights = weights * np.ones([eigenvalues[Spin.up].shape[0], 1])

    dos_data = {
        spin: np.histogram(eigenvalue_set, bins=bins, weights=weights)[0]
        for spin, eigenvalue_set in eigenvalues.items()
    }

    dos = Dos(efermi, energies, dos_data)

    if pdos_file is not None and not total_only:
        if cell_file is None:
            raise OSError(f'Cell file {cell_file} not found: this must be '
                          'provided for PDOS.')
        pdos_raw = compute_pdos(pdos_file, eigenvalues, weights, bins)
        # Also we, need to read the structure, but have it sorted with increasing
        # atomic numbers
        structure = CastepCell.from_file(
            cell_file).structure.get_sorted_structure(
                key=lambda x: x.species.elements[0].Z)
        pdoss = {}
        for isite, site in enumerate(structure.sites):
            pdoss[site] = pdos_raw[isite]
        # Get the pdos dictionary for potting
        pdos = get_pdos(CompleteDos(structure, dos, pdoss),
                        lm_orbitals=lm_orbitals,
                        elements=elements,
                        atoms=atoms)
        # Smear the PDOS
        for orbs in pdos.values():
            for dtmp in orbs.values():
                if gaussian:
                    dtmp.densities = dtmp.get_smeared_densities(gaussian)
    else:
        pdos = {}

    if gaussian:
        dos.densities = dos.get_smeared_densities(gaussian)

    return dos, pdos
Beispiel #2
0
def read_tdos(bands_file,
              bin_width=0.01,
              gaussian=None,
              padding=None,
              emin=None,
              emax=None,
              efermi_to_vbm=True):
    """Convert DOS data from CASTEP .bands file to Pymatgen/Sumo format

    The data is binned into a regular series using np.histogram

    Args:
        bands_file (:obj:`str`): Path to CASTEP prefix.bands output file. The
            k-point positions, weights and eigenvalues are read from this file.
        bin_width (:obj:`float`, optional): Spacing for DOS energy axis
        gaussian (:obj:`float` or None, optional): Width of Gaussian broadening
            function
        padding (:obj:`float`, optional): Energy range above and below occupied
            region. (This is not used if xmin and xmax are set.)
        emin (:obj:`float`, optional): Minimum energy value for output DOS)
        emax (:obj:`float`, optional): Maximum energy value for output DOS
        efermi_to_vbm (:obj:`bool`, optional):
            If a bandgap is detected, modify the stored Fermi energy
            so that it lies at the VBM.

    Returns:
        :obj:`pymatgen.electronic_structure.dos.Dos`
    """

    header = _read_bands_header_verbose(bands_file)

    logging.info("Reading band eigenvalues...")
    _, weights, eigenvalues = read_bands_eigenvalues(bands_file, header)

    calc_efermi = header['e_fermi'][0] * _ry_to_ev * 2
    if efermi_to_vbm and not _is_metal(eigenvalues, calc_efermi):
        logging.info("Setting energy zero to VBM")
        efermi = _get_vbm(eigenvalues, calc_efermi)
    else:
        logging.info("Setting energy zero to Fermi energy")
        efermi = calc_efermi

    emin_data = min(eigenvalues[Spin.up].flatten())
    emax_data = max(eigenvalues[Spin.up].flatten())
    if Spin.down in eigenvalues:
        emin_data = min(emin_data, min(eigenvalues[Spin.down].flatten()))
        emax_data = max(emax_data, max(eigenvalues[Spin.down].flatten()))

    if padding is None and gaussian:
        padding = gaussian * 3
    elif padding is None:
        padding = 0.5

    if emin is None:
        emin = emin_data - padding
    if emax is None:
        emax = emax_data + padding

    # Shift sampling window to account for zeroing at VBM/EFermi
    emin += efermi
    emax += efermi

    bins = np.arange(emin, emax + bin_width, bin_width)
    energies = (bins[1:] + bins[:-1]) / 2

    # Add rows to weights for each band so they are aligned with eigenval data
    weights = weights * np.ones([eigenvalues[Spin.up].shape[0], 1])

    dos_data = {
        spin: np.histogram(eigenvalue_set, bins=bins, weights=weights)[0]
        for spin, eigenvalue_set in eigenvalues.items()
    }

    dos = Dos(efermi, energies, dos_data)
    if gaussian:
        dos.densities = dos.get_smeared_densities(gaussian)

    return dos
Beispiel #3
0
def read_dos(
    bands_file,
    pdos_file=None,
    cell_file=None,
    bin_width=0.01,
    gaussian=None,
    padding=None,
    emin=None,
    emax=None,
    efermi_to_vbm=True,
    lm_orbitals=None,
    elements=None,
    atoms=None,
    total_only=False,
):
    """Convert DOS data from CASTEP .bands file to Pymatgen/Sumo format

    The data is binned into a regular series using np.histogram

    Args:
        bands_file (:obj:`str`): Path to CASTEP prefix.bands output file. The
            k-point positions, weights and eigenvalues are read from this file.
        bin_width (:obj:`float`, optional): Spacing for DOS energy axis
        gaussian (:obj:`float` or None, optional): Width of Gaussian broadening
            function
        padding (:obj:`float`, optional): Energy range above and below occupied
            region. (This is not used if xmin and xmax are set.)
        emin (:obj:`float`, optional): Minimum energy value for output DOS)
        emax (:obj:`float`, optional): Maximum energy value for output DOS
        efermi_to_vbm (:obj:`bool`, optional):
            If a bandgap is detected, modify the stored Fermi energy
            so that it lies at the VBM.
        elements (:obj:`dict`, optional): The elements and orbitals to extract
            from the projected density of states. Should be provided as a
            :obj:`dict` with the keys as the element names and corresponding
            values as a :obj:`tuple` of orbitals. For example, the following
            would extract the Bi s, px, py and d orbitals::

                {'Bi': ('s', 'px', 'py', 'd')}

            If an element is included with an empty :obj:`tuple`, all orbitals
            for that species will be extracted. If ``elements`` is not set or
            set to ``None``, all elements for all species will be extracted.
        lm_orbitals (:obj:`dict`, optional): The orbitals to decompose into
            their lm contributions (e.g. p -> px, py, pz). Should be provided
            as a :obj:`dict`, with the elements names as keys and a
            :obj:`tuple` of orbitals as the corresponding values. For example,
            the following would be used to decompose the oxygen p and d
            orbitals::

                {'O': ('p', 'd')}

        atoms (:obj:`dict`, optional): Which atomic sites to use when
            calculating the projected density of states. Should be provided as
            a :obj:`dict`, with the element names as keys and a :obj:`tuple` of
            :obj:`int` specifying the atomic indices as the corresponding
            values. The elemental projected density of states will be summed
            only over the atom indices specified. If an element is included
            with an empty :obj:`tuple`, then all sites for that element will
            be included. The indices are 0 based for each element specified in
            the POSCAR. For example, the following will calculate the density
            of states for the first 4 Sn atoms and all O atoms in the
            structure::

                {'Sn': (1, 2, 3, 4), 'O': (, )}

            If ``atoms`` is not set or set to ``None`` then all atomic sites
            for all elements will be considered.

    Returns:
        (:obj:`pymatgen.electronic_structure.dos.Dos`, dict)

        where the dict is either empty or contains a PDOS arranged::

          {species: {orbital: Dos}}
    """

    header = _read_bands_header_verbose(bands_file)

    logging.info("Reading band eigenvalues...")
    _, weights, eigenvalues = read_bands_eigenvalues(bands_file, header)

    calc_efermi = header["e_fermi"][0] * _ry_to_ev * 2
    if efermi_to_vbm and not _is_metal(eigenvalues, calc_efermi):
        logging.info("Setting energy zero to VBM")
        efermi = _get_vbm(eigenvalues, calc_efermi)
    else:
        logging.info("Setting energy zero to Fermi energy")
        efermi = calc_efermi

    emin_data = min(eigenvalues[Spin.up].flatten())
    emax_data = max(eigenvalues[Spin.up].flatten())
    if Spin.down in eigenvalues:
        emin_data = min(emin_data, min(eigenvalues[Spin.down].flatten()))
        emax_data = max(emax_data, max(eigenvalues[Spin.down].flatten()))

    if padding is None and gaussian:
        padding = gaussian * 3
    elif padding is None:
        padding = 0.5

    if emin is None:
        emin = emin_data - padding
    if emax is None:
        emax = emax_data + padding

    # Shift sampling window to account for zeroing at VBM/EFermi
    emin += efermi
    emax += efermi

    bins = np.arange(emin, emax + bin_width, bin_width)
    energies = (bins[1:] + bins[:-1]) / 2

    # Add rows to weights for each band so they are aligned with eigenval data
    weights = weights * np.ones([eigenvalues[Spin.up].shape[0], 1])

    dos_data = {
        spin: np.histogram(eigenvalue_set, bins=bins, weights=weights)[0]
        for spin, eigenvalue_set in eigenvalues.items()
    }

    dos = Dos(efermi, energies, dos_data)

    if pdos_file is not None and not total_only:
        if cell_file is None:
            raise OSError(f"Cell file {cell_file} not found: this must be "
                          "provided for PDOS.")
        pdos_raw = compute_pdos(pdos_file, eigenvalues, weights, bins)
        # Also we, need to read the structure, but have it sorted with increasing
        # atomic numbers
        structure = CastepCell.from_file(
            cell_file).structure.get_sorted_structure(
                key=lambda x: x.species.elements[0].Z)
        pdoss = {}
        for isite, site in enumerate(structure.sites):
            pdoss[site] = pdos_raw[isite]
        # Get the pdos dictionary for potting
        pdos = get_pdos(
            CompleteDos(structure, dos, pdoss),
            lm_orbitals=lm_orbitals,
            elements=elements,
            atoms=atoms,
        )
        # Smear the PDOS
        for orbs in pdos.values():
            for dtmp in orbs.values():
                if gaussian:
                    dtmp.densities = dtmp.get_smeared_densities(gaussian)
    else:
        pdos = {}

    if gaussian:
        dos.densities = dos.get_smeared_densities(gaussian)

    return dos, pdos