示例#1
0
    def _get_interpolater(self, n_idx, t_idx):
        # interpolater expects energies in eV and structure in angstrom
        energies = {s: e * hartree_to_ev for s, e in self.energies.items()}
        structure = get_angstrom_structure(self.structure)
        bs = BandStructure(
            self.ir_kpoints,
            energies,
            structure.lattice,
            self.efermi * hartree_to_ev,
            structure=structure,
        )
        nelect = sum([idx for idx in self.vb_idx.values()])

        props = defaultdict(dict)
        for spin in self.spins:
            # easier to interpolate the log
            props[spin]["rates"] = np.log10(
                np.sum(self.scattering_rates[spin][:, n_idx, t_idx], axis=0))

        return Interpolater(
            bs,
            nelect,
            interpolation_factor=self.interpolation_factor,
            soc=self.soc,
            other_properties=props,
        )
示例#2
0
文件: PWXML.py 项目: zooks97/pypwio
 def get_BandStructure(self):
     eigenvalues = self.eigenvalues
     eigendict = {Spin.up: eigenvalues}
     return BandStructure(efermi=self.fermi_energy,
                          eigenvals=eigendict,
                          kpoints=self.k_points,
                          structure=self.get_Structure(),
                          lattice=self.get_reciprocal_Lattice())
示例#3
0
def expand_bandstructure(
    bandstructure, symprec=defaults["symprec"], time_reversal=True
):
    kpoints = get_kpoints_from_bandstructure(bandstructure)
    full_kpoints, _, _, _, _, kp_mapping = expand_kpoints(
        bandstructure.structure,
        kpoints,
        symprec=symprec,
        time_reversal=time_reversal,
        return_mapping=True,
    )
    return BandStructure(
        full_kpoints,
        {s: b[:, kp_mapping] for s, b in bandstructure.bands.items()},
        bandstructure.structure.lattice.reciprocal_lattice,
        bandstructure.efermi,
        structure=bandstructure.structure,
    )
示例#4
0
    def _get_interpolater(self, n_idx, t_idx, mode="linear"):
        props = defaultdict(dict)
        for spin in self.spins:
            # calculate total rate
            spin_rates = np.sum(self.scattering_rates[spin][:, n_idx, t_idx], axis=0)

            # easier to interpolate the log
            log_rates = np.log10(spin_rates)

            # # handle rates that close to numerical noise
            log_rates[log_rates > 18] = 15
            log_rates[np.isnan(log_rates)] = 15

            # map to full k-point mesh
            props[spin]["rates"] = log_rates

        if mode == "linear":
            return _LinearBandStructureInterpolator(
                self.kpoints,
                self.ir_to_full_kpoint_mapping,
                self.energies,
                self.structure,
                self.efermi,
                props,
            )
        elif mode == "fourier":
            bs = BandStructure(
                self.ir_kpoints,
                self.energies,
                self.structure.lattice,
                self.efermi,
                structure=self.structure,
            )

            return Interpolator(
                bs,
                self.num_electrons,
                interpolation_factor=self.interpolation_factor,
                soc=self.soc,
                other_properties=props,
            )
        raise ValueError("Unknown interpolation mode; should be 'linear' or 'fourier'.")
示例#5
0
def rotate_bandstructure(bandstructure: BandStructure, frac_symop: SymmOp):
    """Won't rotate projections..."""
    kpoints = get_kpoints_from_bandstructure(bandstructure)
    recip_rot = frac_symop.rotation_matrix.T
    rot_kpoints = np.dot(recip_rot, kpoints.T).T

    # map to first BZ, use VASP zone boundary convention
    rot_kpoints = kpoints_to_first_bz(rot_kpoints, negative_zone_boundary=False)

    # rotate structure
    structure = bandstructure.structure.copy()
    structure.apply_operation(frac_symop, fractional=True)

    return BandStructure(
        rot_kpoints,
        bandstructure.bands,
        structure.lattice.reciprocal_lattice,
        bandstructure.efermi,
        structure=structure,
    )
示例#6
0
def get_reconstructed_band_structure(list_bs, efermi=None):
    """Combine a list of band structures into a single band structure.

    This is typically very useful when you split non self consistent
    band structure runs in several independent jobs and want to merge back
    the results.

    This method will also ensure that any BandStructure objects will contain
    branches.

    Args:
        list_bs (:obj:`list` of \
        :obj:`~pymatgen.electronic_structure.bandstructure.BandStructure` \
        or :obj:`~pymatgen.electronic_structure.bandstructure.BandStructureSymmLine`):
            The band structures.
        efermi (:obj:`float`, optional): The Fermi energy of the reconstructed
            band structure. If `None`, an average of all the Fermi energies
            across all band structures is used.

    Returns:
        :obj:`pymatgen.electronic_structure.bandstructure.BandStructure` or \
        :obj:`pymatgen.electronic_structure.bandstructureBandStructureSymmLine`:
        A band structure object. The type depends on the type of the band
        structures in ``list_bs``.
    """
    if efermi is None:
        efermi = sum([b.efermi for b in list_bs]) / len(list_bs)

    kpoints = []
    labels_dict = {}
    rec_lattice = list_bs[0].lattice_rec
    nb_bands = min([list_bs[i].nb_bands for i in range(len(list_bs))])

    kpoints = np.concatenate([[k.frac_coords for k in bs.kpoints]
                              for bs in list_bs])

    dicts = [bs.labels_dict for bs in list_bs]
    labels_dict = {k: v.frac_coords for d in dicts for k, v in d.items()}

    # pymatgen band structure objects support branches. These are formed when
    # two kpoints with the same label are next to each other. This bit of code
    # will ensure that the band structure will contain branches, if it doesn't
    # already.
    dup_ids = []
    for i, k in enumerate(kpoints):
        dup_ids.append(i)
        if (tuple(k) in tuple(map(tuple, labels_dict.values())) and i != 0
                and i != len(kpoints) - 1
                and (not np.array_equal(kpoints[i + 1], k)
                     or not np.array_equal(kpoints[i - 1], k))):
            dup_ids.append(i)

    kpoints = kpoints[dup_ids]

    eigenvals = {}
    eigenvals[Spin.up] = np.concatenate(
        [bs.bands[Spin.up][:nb_bands] for bs in list_bs], axis=1)
    eigenvals[Spin.up] = eigenvals[Spin.up][:, dup_ids]

    if list_bs[0].is_spin_polarized:
        eigenvals[Spin.down] = np.concatenate(
            [bs.bands[Spin.down][:nb_bands] for bs in list_bs], axis=1)
        eigenvals[Spin.down] = eigenvals[Spin.up][:, dup_ids]

    projections = {}
    if len(list_bs[0].projections) != 0:
        projs = [bs.projections[Spin.up][:nb_bands][dup_ids] for bs in list_bs]
        projections[Spin.up] = np.concatenate(projs, axis=1)[:, dup_ids]

        if list_bs[0].is_spin_polarized:
            projs = [
                bs.projections[Spin.down][:nb_bands][dup_ids] for bs in list_bs
            ]
            projections[Spin.down] = np.concatenate(projs, axis=1)[:, dup_ids]

    if isinstance(list_bs[0], BandStructureSymmLine):
        return BandStructureSymmLine(kpoints,
                                     eigenvals,
                                     rec_lattice,
                                     efermi,
                                     labels_dict,
                                     structure=list_bs[0].structure,
                                     projections=projections)
    else:
        return BandStructure(kpoints,
                             eigenvals,
                             rec_lattice,
                             efermi,
                             labels_dict,
                             structure=list_bs[0].structure,
                             projections=projections)
示例#7
0
def get_band_structure(
    vasprun: Vasprun, zero_weighted: str = defaults["zero_weighted_kpoints"]
) -> BandStructure:
    """
    Get a band structure from a Vasprun object.

    This can ensure that if the calculation contains zero-weighted k-points then the
    weighted k-points will be discarded (helps with hybrid calculations).

    Also ensures that the Fermi level is set correctly.

    Args:
        vasprun: A vasprun object.
        zero_weighted: How to handle zero-weighted k-points if they are present in the
            calculation. Options are:
            - "keep": Keep zero-weighted k-points in the band structure.
            - "drop": Drop zero-weighted k-points, keeping only the weighted k-points.
            - "prefer": Drop weighted-kpoints if zero-weighted k-points are present
              in the calculation (useful for cheap hybrid calculations).

    Returns:
        A band structure.
    """
    # first check if Fermi level crosses a band
    k_idx = get_zero_weighted_kpoint_indices(vasprun, mode=zero_weighted)
    kpoints = np.array(vasprun.actual_kpoints)[k_idx]

    projections = {}
    eigenvalues = {}
    for spin, spin_eigenvalues in vasprun.eigenvalues.items():
        # discard weight and set shape nbands, nkpoints
        eigenvalues[spin] = spin_eigenvalues[k_idx, :, 0].transpose(1, 0)

        if vasprun.projected_eigenvalues:
            # is nkpoints, nbands, nion, norb; we need nbands, nkpoints, norb, nion
            spin_projections = vasprun.projected_eigenvalues[spin]
            projections[spin] = spin_projections[k_idx].transpose(1, 0, 3, 2)

    # finding the Fermi level is quite painful, as VASP can sometimes put it slightly
    # inside a band
    fermi_crosses_band = False
    for spin_eigenvalues in eigenvalues.values():
        eigs_below = np.any(spin_eigenvalues < vasprun.efermi, axis=1)
        eigs_above = np.any(spin_eigenvalues > vasprun.efermi, axis=1)
        if np.any(eigs_above & eigs_below):
            fermi_crosses_band = True

    # if the Fermi level crosses a band, the eigenvalue band properties is a more
    # reliable way to check whether this is a real effect
    bandgap, cbm, vbm, _ = vasprun.eigenvalue_band_properties

    if not fermi_crosses_band:
        # safe to use VASP fermi level
        efermi = vasprun.efermi
    elif fermi_crosses_band and bandgap == 0:
        # it is actually a metal
        efermi = vasprun.efermi
    else:
        # Set Fermi level half way between valence and conduction bands
        efermi = (cbm + vbm) / 2

    return BandStructure(
        kpoints,
        eigenvalues,
        vasprun.final_structure.lattice.reciprocal_lattice,
        efermi,
        structure=vasprun.final_structure,
        projections=projections,
    )
示例#8
0
    def interpolate_bands(self,
                          interpolation_factor: float = 5,
                          energy_cutoff: Optional[float] = None,
                          nworkers: int = -1):
        """Gets a pymatgen band structure.
        Note, the interpolation mesh is determined using by
        ``interpolate_factor`` option in the ``Inteprolater`` constructor.
        The degree of parallelization is controlled by the ``nworkers`` option.
        Args:
            energy_cutoff: The energy cut-off to determine which bands are
                included in the interpolation. If the energy of a band falls
                within the cut-off at any k-point it will be included. For
                metals the range is defined as the Fermi level ± energy_cutoff.
                For gapped materials, the energy range is from the VBM -
                energy_cutoff to the CBM + energy_cutoff.
            nworkers: The number of processors used to perform the
                interpolation. If set to ``-1``, the number of workers will
                be set to the number of CPU cores.
        Returns:
            The interpolated electronic structure.
        """

        coefficients = {}

        equivalences = sphere.get_equivalences(atoms=self._atoms,
                                               nkpt=self._kpoints.shape[0] *
                                               interpolation_factor,
                                               magmom=self._magmom)

        # get the interpolation mesh used by BoltzTraP2
        interpolation_mesh = 2 * np.max(np.abs(np.vstack(equivalences)),
                                        axis=0) + 1

        for spin in self._spins:
            energies = self._band_structure.bands[spin] * units.eV
            data = DFTData(self._kpoints,
                           energies,
                           self._lattice_matrix,
                           mommat=self._mommat)
            coefficients[spin] = fite.fitde3D(data, equivalences)
        is_metal = self._band_structure.is_metal()

        nworkers = multiprocessing.cpu_count() if nworkers == -1 else nworkers

        # determine energy cutoffs
        if energy_cutoff and is_metal:
            min_e = self._band_structure.efermi - energy_cutoff
            max_e = self._band_structure.efermi + energy_cutoff

        elif energy_cutoff:
            min_e = self._band_structure.get_vbm()['energy'] - energy_cutoff
            max_e = self._band_structure.get_cbm()['energy'] + energy_cutoff

        else:
            min_e = min([
                self._band_structure.bands[spin].min() for spin in self._spins
            ])
            max_e = max([
                self._band_structure.bands[spin].max() for spin in self._spins
            ])

        energies = {}
        new_vb_idx = {}
        for spin in self._spins:
            ibands = np.any((self._band_structure.bands[spin] > min_e) &
                            (self._band_structure.bands[spin] < max_e),
                            axis=1)

            energies[spin] = fite.getBTPbands(equivalences,
                                              coefficients[spin][ibands],
                                              self._lattice_matrix,
                                              nworkers=nworkers)[0]

            # boltztrap2 gives energies in Rydberg, convert to eV
            energies[spin] /= units.eV

            if not is_metal:
                vb_idx = max(
                    self._band_structure.get_vbm()["band_index"][spin])
                # need to know the index of the valence band after discounting
                # bands during the interpolation. As ibands is just a list of
                # True/False, we can count the number of Trues up to
                # and including the VBM to get the new number of valence bands
                new_vb_idx[spin] = sum(ibands[:vb_idx + 1]) - 1

        if is_metal:
            efermi = self._band_structure.efermi
        else:
            # if material is semiconducting, set Fermi level to middle of gap
            e_vbm = max(
                [np.max(energies[s][:new_vb_idx[s] + 1]) for s in self._spins])
            e_cbm = min(
                [np.min(energies[s][new_vb_idx[s] + 1:]) for s in self._spins])
            efermi = (e_vbm + e_cbm) / 2

        atoms = AseAtomsAdaptor().get_atoms(self._band_structure.structure)
        mapping, grid = spglib.get_ir_reciprocal_mesh(interpolation_mesh,
                                                      atoms,
                                                      symprec=0.1)
        full_kpoints = grid / interpolation_mesh

        sort_idx = np.lexsort(
            (full_kpoints[:, 2], full_kpoints[:, 2] < 0, full_kpoints[:, 1],
             full_kpoints[:, 1] < 0, full_kpoints[:,
                                                  0], full_kpoints[:, 0] < 0))

        reordered_kpoints = full_kpoints[sort_idx]

        return BandStructure(reordered_kpoints,
                             energies,
                             self._band_structure.structure.lattice,
                             efermi,
                             structure=self._structure), np.max(np.abs(
                                 np.vstack(equivalences)),
                                                                axis=0)
示例#9
0
)  #,ionic_step_skip=1,ionic_step_offset=1,parse_dos=True,parse_eigen=True,occu_tol=1e-8)
efermi = vasp.efermi
eigenvals = vasp.eigenvalues
bs = vasp.get_band_structure(kpoints_filename='KPOINTS',
                             efermi=efermi,
                             line_mode=True)
#kpoints_modes = Kpoints_support_modes(3) #Line mode
kpoints = Kpoints.from_file('KPOINTS')
poscar = Poscar.from_file('POSCAR')
incar = Incar.from_file(('INCAR'))
struc = IStructure.from_file('POSCAR')
lattice = struc.lattice
labels = kpoints.labels
space_group = struc.get_space_group_info()
#coords = struc.frac_coords()
BS = BandStructure(kpoints.kpts, eigenvals, lattice, efermi,
                   structure=struc)  #kpoints.kpts
labels_dict = BS.labels_dict
BSSL = BandStructureSymmLine(kpoints.kpts,
                             eigenvals,
                             lattice,
                             efermi,
                             labels_dict,
                             structure=struc)
b = vasp.eigenvalue_band_properties
vbm = BS.get_vbm()
cbm = BS.get_cbm()
print(b, efermi, 'cbm:', bs.get_cbm(), 'vbm:',
      bs.get_vbm())  #,cbm,vbm#BSSL.get_branch(90)#,kpoints.kpts
#print a.eigenvalue_band_properties,fermi
#vbm = a.eigenvalue_band_properties
示例#10
0
    def get_band_structure(
        self,
        kpoint_mesh: Union[float, int, List[int]],
        energy_cutoff: Optional[float] = None,
        scissor: Optional[float] = None,
        bandgap: Optional[float] = None,
        symprec: float = 0.01,
    ) -> BandStructure:
        """Calculates the density of states using the interpolated bands.

        Args:
            kpoint_mesh: The k-point mesh as a 1x3 array. E.g.,``[6, 6, 6]``.
                Alternatively, if a single value is provided this will be
                treated as a reciprocal density and the k-point mesh dimensions
                generated automatically.
            energy_cutoff: The energy cut-off to determine which bands are
                included in the interpolation. If the energy of a band falls
                within the cut-off at any k-point it will be included. For
                metals the range is defined as the Fermi level ± energy_cutoff.
                For gapped materials, the energy range is from the VBM -
                energy_cutoff to the CBM + energy_cutoff.
            scissor: The amount by which the band gap is scissored. Cannot
                be used in conjunction with the ``bandgap`` option. Has no
                effect for metallic systems.
            bandgap: Automatically adjust the band gap to this value. Cannot
                be used in conjunction with the ``scissor`` option. Has no
                effect for metallic systems.
            symprec: The symmetry tolerance used when determining the symmetry
                inequivalent k-points on which to interpolate.

        Returns:
            The density of states.
        """
        ir_kpoints, weights, full_kpoints, ir_kpoints_idx, ir_to_full_idx = get_kpoints(
            kpoint_mesh,
            self._band_structure.structure,
            symprec=symprec,
            return_full_kpoints=True,
        )

        energies = self.get_energies(
            ir_kpoints,
            scissor=scissor,
            bandgap=bandgap,
            energy_cutoff=energy_cutoff,
            atomic_units=True,
        )

        energies = {
            s: bands[:, ir_to_full_idx] / units.eV
            for s, bands in energies.items()
        }

        return BandStructure(
            full_kpoints,
            energies,
            self._band_structure.structure.lattice,
            self._band_structure.efermi,
            coords_are_cartesian=True,
            structure=self._band_structure.structure,
        )
示例#11
0
def bs_graph(rawdatadir, savedir, e_fermi, soc=False):
    run = BSVasprun("{}/vasprun.xml".format(rawdatadir),
                    parse_projected_eigen=True)
    bs = run.get_band_structure(efermi=e_fermi,
                                line_mode=True,
                                force_hybrid_mode=True)
    bsplot = BSPlotter(bs)

    # Get the plot
    bsplot.get_plot(vbm_cbm_marker=True, ylim=(-1.5, 1.5), zero_to_efermi=True)
    bs_graph.e_fermi = float(bs.efermi)
    bs_graph.band_gap = float(bs.get_band_gap()["energy"])
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()
    ax.hlines(0, xlim[0], xlim[1], linestyle="--", color="black")
    ax.tick_params(labelsize=20)
    if not soc:
        ax.plot((), (), "r-", label="spin up")
        ax.plot((), (), "b-", label="spin down")
        ax.legend(fontsize=16, loc="upper left")
    plt.savefig("{}/BSGraph".format(savedir))
    plt.close()

    if not soc:
        # Print quick info about band gap (source: vasprun.xml)
        #print(bs_graph.e_fermi)
        #print(bs_graph.band_gap)

        # Get quick info about band gap (source: EIGENVAL)
        eigenval = Eigenval("{}/EIGENVAL".format(rawdatadir))
        bs_graph.band_properties = eigenval.eigenvalue_band_properties

        # Get detailed info about band gap and CB/VB in each spin channel
        # (source: EIGENVAL)
        bs_graph.eigenvalues = eigenval.eigenvalues
        bs_graph.kpoints = eigenval.kpoints
        poscar = Poscar.from_file("{}/POSCAR".format(rawdatadir))
        bs_graph.lattice = poscar.structure.lattice.reciprocal_lattice

        bs_graph.eigenvalues[Spin.up] = bs_graph.eigenvalues[
            Spin.up][:, :, :-1]
        bs_graph.eigenvalues[Spin.down] = bs_graph.eigenvalues[
            Spin.down][:, :, :-1]
        bs_graph.eigenvalues[Spin.up] = bs_graph.eigenvalues[Spin.up][:, :, 0]
        bs_graph.eigenvalues[Spin.down] = bs_graph.eigenvalues[Spin.down][:, :,
                                                                          0]

        bs_graph.eigenvalues[Spin.up] = \
            np.transpose(bs_graph.eigenvalues[Spin.up])
        bs_graph.eigenvalues[Spin.down] = \
            np.transpose(bs_graph.eigenvalues[Spin.down])

        bs = BandStructure(bs_graph.kpoints, bs_graph.eigenvalues,
                           bs_graph.lattice, bs_graph.e_fermi)
        bs_graph.vbm = bs.get_vbm()["energy"]
        bs_graph.cbm = bs.get_cbm()["energy"]
        bs_graph.electronic_gap = bs.get_band_gap()["energy"]
        bs_graph.direct = bs.get_band_gap()["direct"]
        if bs_graph.vbm and bs_graph.cbm and bs_graph.electronic_gap:
            bs_graph.gap_by_spin = bs.get_direct_band_gap_dict()
    return
示例#12
0
    def interpolate_bands(
        self,
        interpolation_factor: float = 5,
        energy_cutoff: Optional[float] = None,
        nworkers: int = -1,
    ):
        """Gets a pymatgen band structure.
        Note, the interpolation mesh is determined using by
        ``interpolate_factor`` option in the ``Inteprolater`` constructor.
        The degree of parallelization is controlled by the ``nworkers`` option.
        Args:
            interpolation_factor: The factor by which the band structure will
                be interpolated.
            energy_cutoff: The energy cut-off to determine which bands are
                included in the interpolation. If the energy of a band falls
                within the cut-off at any k-point it will be included. For
                metals the range is defined as the Fermi level ± energy_cutoff.
                For gapped materials, the energy range is from the VBM -
                energy_cutoff to the CBM + energy_cutoff.
            nworkers: The number of processors used to perform the
                interpolation. If set to ``-1``, the number of workers will
                be set to the number of CPU cores.

        Returns:
            The interpolated electronic structure.
        """

        coefficients = {}

        equivalences = sphere.get_equivalences(
            atoms=self._atoms,
            nkpt=self._kpoints.shape[0] * interpolation_factor,
            magmom=self._magmom,
        )

        # get the interpolation mesh used by BoltzTraP2
        interpolation_mesh = 2 * np.max(np.abs(np.vstack(equivalences)),
                                        axis=0) + 1

        for spin in self._spins:
            energies = self._band_structure.bands[spin] * eV
            data = DFTData(self._kpoints,
                           energies,
                           self._lattice_matrix,
                           mommat=self._mommat)
            coefficients[spin] = fite.fitde3D(data, equivalences)
        is_metal = self._band_structure.is_metal()

        nworkers = multiprocessing.cpu_count() if nworkers == -1 else nworkers

        # determine energy cutoffs
        if energy_cutoff and is_metal:
            min_e = self._band_structure.efermi - energy_cutoff
            max_e = self._band_structure.efermi + energy_cutoff

        elif energy_cutoff:
            min_e = self._band_structure.get_vbm()["energy"] - energy_cutoff
            max_e = self._band_structure.get_cbm()["energy"] + energy_cutoff

        else:
            min_e = min([
                self._band_structure.bands[spin].min() for spin in self._spins
            ])
            max_e = max([
                self._band_structure.bands[spin].max() for spin in self._spins
            ])

        energies = {}
        new_vb_idx = {}
        for spin in self._spins:
            ibands = np.any(
                (self._band_structure.bands[spin] > min_e)
                & (self._band_structure.bands[spin] < max_e),
                axis=1,
            )

            energies[spin] = fite.getBTPbands(
                equivalences,
                coefficients[spin][ibands],
                self._lattice_matrix,
                nworkers=nworkers,
            )[0]

            # boltztrap2 gives energies in Rydberg, convert to eV
            energies[spin] /= eV

            if not is_metal:
                vb_energy = self._band_structure.get_vbm()["energy"]
                spin_bands = self._band_structure.bands[spin]
                below_vbm = np.any(spin_bands < vb_energy, axis=1)
                spin_vb_idx = np.max(np.where(below_vbm)[0])

                # need to know the index of the valence band after discounting
                # bands during the interpolation. As ibands is just a list of
                # True/False, we can count the number of Trues up to
                # and including the VBM to get the new number of valence bands
                new_vb_idx[spin] = sum(ibands[:spin_vb_idx + 1]) - 1

        if is_metal:
            efermi = self._band_structure.efermi
        else:
            # if material is semiconducting, set Fermi level to middle of gap
            warnings.warn(
                "The Fermi energy may be different to that in the vasprun.xml file,"
                " due to the material being a semiconductor. The Fermi level has been "
                "set to midway between the top of the valence band and the bottom of "
                "the conduction band.",
                category=None,
                stacklevel=1,
                source=None,
            )
            e_vbm = max(
                [np.max(energies[s][:new_vb_idx[s] + 1]) for s in self._spins])
            e_cbm = min(
                [np.min(energies[s][new_vb_idx[s] + 1:]) for s in self._spins])
            efermi = (e_vbm + e_cbm) / 2

        atoms = AseAtomsAdaptor().get_atoms(self._band_structure.structure)
        mapping, grid = spglib.get_ir_reciprocal_mesh(interpolation_mesh,
                                                      atoms,
                                                      symprec=0.1)
        kpoints = grid / interpolation_mesh

        # sort energies so they have the same order as the k-points generated by spglib
        sort_idx = sort_boltztrap_to_spglib(kpoints)
        energies = {s: ener[:, sort_idx] for s, ener in energies.items()}

        rlat = self._band_structure.structure.lattice.reciprocal_lattice
        interp_band_structure = BandStructure(kpoints,
                                              energies,
                                              rlat,
                                              efermi,
                                              structure=self._structure)

        return interp_band_structure, interpolation_mesh