pyprocar.io.lobster — PyProcar 6.2.1 documentation

Skip to main content
`Ctrl`+`K`

# Source code for pyprocar.io.lobster

    import re
    import os

    import numpy as np
    import xml.etree.ElementTree as ET

    from pyprocar.core import DensityOfStates, Structure, ElectronicBandStructure, KPath
    from . import qe, vasp

    # Physics Contstnats
    HARTREE_TO_EV = 27.211386245988  #eV/Hartree

    def str2bool(v):
      return v.lower() in ("true")

    class LobsterParser():
        """The class helps parse the information from a lobster calculation

        Parameters
        ----------
        dirname : str, optional
            The directory name for the calculation, by default ""
        code : str, optional
            The code name, by default 'qe'
        lobsterin : str, optional
            The lobster in put filename, by default 'lobsterin'
        lobsterout : str, optional
            The lobster output filename, by default 'lobsterout'
        scfIn_filename : str, optional
            The scf.in file for QE calculations, by default "scf.in"
        outcar : str, optional
            The outcar for vasp calculations, by default 'OUTCAR'
        poscar : str, optional
            The poscar for vasp calculations, by default 'POSCAR'
        procar : str, optional
            The procar for vasp calculations, by default 'PROCAR'
        dos_interpolation_factor : _type_, optional
            The interpolation factor on the density of states, by default None

        """

        def __init__(self,
            dirname = "",
            code = 'qe',
            lobsterin = 'lobsterin',
            lobsterout = 'lobsterout',
            scfIn_filename = "scf.in",
            outcar = 'OUTCAR',
            poscar = 'POSCAR',
            procar = 'PROCAR',
            dos_interpolation_factor = None ):

            if dirname != "":
                dirname = dirname + os.sep
            else:
                dirname = ""

            self.dirname = dirname
            self.code = code

            rf = open(f"{self.dirname}{lobsterin}", "r")
            self.lobsterin = rf.read()
            rf.close()

            rf = open(f"{self.dirname}{lobsterout}", "r")
            self.lobsterout = rf.read()
            rf.close()

            # self.orbitals = [
            #             {"l": 0, "m": 1},
            #             {"l": 1, "m": 3},
            #             {"l": 1, "m": 1},
            #             {"l": 1, "m": 2},
            #             {"l": 2, "m": 5},
            #             {"l": 2, "m": 3},
            #             {"l": 2, "m": 1},
            #             {"l": 2, "m": 2},
            #             {"l": 2, "m": 4},
            #         ]

            self.orbitals = [
                "s",
                "p_y",
                "p_z",
                "p_x",
                "d_xy",
                "d_yz",
                "d_z^2",
                "d_xz",
                "d_x^2-y^2",
                # "f_y^3-x^2",
                # "f_xyz",
                # "f_yz^2",
                # "f_z^3",
                # "f_xz^2",
                # "f_zx^2",
                # "f_x^3",
                # "_tot",
            ]

            self.dos_interpolation_factor = dos_interpolation_factor

            self.scfIn_filename  = scfIn_filename

            self.parse_structure(
                            outcar=outcar,
                            poscar=poscar,
                            procar=procar,
                            interpolation_factor=1,
                            fermi=None)

            self._getSpecialKpoints()

            # For band structures

            if len(self.kticks) != 0:
                self._readFileNames()
                self._readFatBands()
                self._createKPath()
                self.ebs = ElectronicBandStructure(
                                            kpoints=self.kpoints,
                                            bands=self.bands + self.efermi,
                                            projected=self._spd2projected(self.spd),
                                            efermi=self.efermi,
                                            kpath=self.kpath,
                                            projected_phase=None,
                                            labels=self.orbitals[:-1],
                                            reciprocal_lattice=self.reciprocal_lattice,
                                            interpolation_factor=dos_interpolation_factor,
                                            # shifted_to_efermi=True,
                                            # shifted_to_efermi=False,
                                        )

            if os.path.exists(f"{self.dirname}DOSCAR.lobster"):
                self.data = self._parse_doscar(f"{self.dirname}DOSCAR.lobster")

        @property
        def species(self):
            """Returns the species of the calculation

            Returns
            -------
            List
                Returns a list of string or atomic numbers[int]
            """
            return self.initial_structure.species

        @property
        def structures(self):
            """Returns a list of pyprocar.core.Structure

            Returns
            -------
            List
                Returns a list of pyprocar.core.Structure
            """
            # symbols = [x.strip() for x in self.data['ions']]
            symbols = [x.strip() for x in self.ions]
            structures = []

            st = Structure(atoms=symbols, lattice = self.direct_lattice, fractional_coordinates =self.atomic_positions )

            structures.append(st)
            return structures

        @property
        def structure(self):
            """Returns a the last element of a list of pyprocar.core.Structure

            Returns
            -------
            pyprocar.core.Structure
                Returns a the last element of a list of pyprocar.core.Structure
            """
            return self.structures[-1]

        @property
        def initial_structure(self):
            """Returns a the first element of a list of pyprocar.core.Structure

            Returns
            -------
            pyprocar.core.Structure
                Returns a the first element of a list of pyprocar.core.Structure
            """
            return self.structures[0]

        @property
        def final_structure(self):
            """Returns a the last element of a list of pyprocar.core.Structure

            Returns
            -------
            pyprocar.core.Structure
                Returns a the last element of a list of pyprocar.core.Structure
            """

            return self.structures[-1]

        @property
        def dos(self):
            """Initializes the pyprocar.core.DensityOfStates

            Returns
            -------
            pyprocar.core.DensityOfStates
                Retruns the pyprocar.core.DensityOfStates object
            """
            energies = self.dos_total['energies']
            total = []
            for ispin in self.dos_total:
                if ispin == 'energies':
                    continue
                total.append(self.dos_total[ispin])
            # total = np.array(total).T
            return DensityOfStates(
                energies=energies,
                total=total,
                projected=self.dos_projected,
                interpolation_factor=self.dos_interpolation_factor)
        """
            Returns the complete density (total,projected) of states as a python dictionary
            """
        @property
        def dos_to_dict(self):
            """Returns the complete density (total,projected) of states as a python dictionary

            Returns
            -------
            dict
                Returns a dictionary with the projected and total denisity of states
            """

            return {
                'total': self._get_dos_total(),
                'projected': self._get_dos_projected()
            }

        @property
        def dos_total(self):
            """Returns the density of states total as a dict.
            The keys are energies,Spin-up, Spin-down

            Returns
            -------
            dict
                Returns the density of states total as a dict
            """

            dos_total, labels = self._get_dos_total()
            #dos_total['energies'] -= self.fermi

            return dos_total

        @property
        def dos_projected(self):
            """Returns the projected DOS as a multi-dimentional array,
            to be used in the pyprocar.core.DensityOfStates object

            Returns
            -------
            list
                A list of projections per atom
            """

            ret = []
            dos_projected, info = self._get_dos_projected()
            if dos_projected is None:
                return None
            norbitals = len(info) - 1
            info[0] = info[0].capitalize()
            labels = []
            labels.append(info[0])
            ret = []
            for iatom in dos_projected:
                temp_atom = []
                for iorbital in range(norbitals):
                    temp_spin = []
                    for key in dos_projected[iatom]:
                        if key == 'energies':
                            continue
                        temp_spin.append(dos_projected[iatom][key][:, iorbital])
                    temp_atom.append(temp_spin)
                ret.append([temp_atom])
            return ret

        def _get_dos_total(self):
            """Helper method to get the total density of state

            Returns
            -------
            dict, list
                Returns the total_dos dict and label names
            """

            energies = self.data['total'][:, 0]
            dos_total = {'energies': energies}

            if self.nspin != 1:
                dos_total['Spin-up'] = self.data['total'][:, 1]
                dos_total['Spin-down'] = self.data['total'][:, 2]
                #dos_total['integrated_dos_up'] = self.data['total'][:, 3]
                #dos_total['integrated_dos_down'] = self.data['total'][:, 4]
            else:
                dos_total['Spin-up'] = self.data['total'][:, 1]
                #dos_total['integrated_dos'] = self.data['total'][:, 2]

            return dos_total,list(dos_total.keys())

        def _get_dos_projected(self, atoms=[]):
            """
            Helper method to get the total density of state

            Parameters
            -------
            atoms: list, optional
                Atoms to to iterate through
            Returns
            -------
            list, list
                Returns the porjected dos istt and label names
            """
            if len(atoms) == 0:
                atoms = np.arange(self.initial_structure.natoms)

            if 'projected' in list(self.data.keys()):
                dos_projected = {}
                ion_list = ["ion %s" % str(x + 1) for x in atoms
                            ]  # using this name as vasrun.xml uses ion #
                for i in range(len(ion_list)):
                    iatom = ion_list[i]
                    name = self.initial_structure.atoms[atoms[i]]

                    energies = self.data['projected'][i][:,0,0]

                    dos_projected[name] = {'energies': energies}
                    if self.nspin != 1:
                        dos_projected[name]['Spin-up'] = self.data['projected'][i][:, 1:,0]
                        dos_projected[name]['Spin-down'] = self.data['projected'][i][:, 1:,1]
                    else:
                        dos_projected[name]['Spin-up'] = self.data['projected'][i][:, 1:,0]

                return dos_projected, self.data['projected_labels_info']
            else:
                print(
                    "This calculation does not include partial density of states")
                return None, None

        def dos_parametric(self,atoms=None,orbitals=None,spin=None,title=None):
            """This function sums over the list of atoms and orbitals given
            for example dos_paramateric(atoms=[0,1,2],orbitals=[1,2,3],spin=[0,1])
            will sum all the projections of atoms 0,1,2 and all the orbitals of 1,2,3 (px,py,pz)
            and return separatly for the 2 spins as a DensityOfStates object from pychemia.visual.DensityofStates

            Parameters
            ----------
            atoms : list[int], optional
                list of atom index needed to be sumed over., by default None
            orbitals : list[int], optional
                list of orbitals needed to be sumed over
            |  s  ||  py ||  pz ||  px || dxy || dyz || dz2 || dxz ||x2-y2||
            |  0  ||  1  ||  2  ||  3  ||  4  ||  5  ||  6  ||  7  ||  8  ||, by default None
            spin : list[int], optional
                which spins to be included, by default None
            title : str, optional
                Title, by default None

            Returns
            -------
            pyprocar.core.DensityOfStates
                Returns the dos object
            """
            projected = self.dos_projected
            dos_projected,labelsInfo = self._get_dos_projected()
            self.availiableOrbitals = list(labelsInfo.keys())
            self.availiableOrbitals.pop(0)
            if atoms == None :
                atoms = np.arange(self.nions,dtype=int)
            if spin == None :
                spin = [0,1]
            if orbitals == None :
                orbitals = np.arange((len(projected[0].labels)-1)//2,dtype=int)
            if title == None:
                title = 'Sum'
            orbitals = np.array(orbitals)

            if len(spin) == 2:
                labels = ['Energy','Spin-Up','Spin-Down']
                new_orbitals = []
                for ispin in spin :
                    new_orbitals.append(list(orbitals+ispin*(len(projected[0].labels)-1)//2))

                orbitals = new_orbitals

            else :

                for x in orbitals:

                    if (x+1 > (len(projected[0].labels)-1)//2 ):
                        print('listed wrong amount of orbitals')
                        print('Only use one or more of the following ' + str(np.arange((len(projected[0].labels)-1)//2,dtype=int)))
                        print('Only use one or more of the following ' + str(np.arange((len(projected[0].labels)-1)//2,dtype=int)))
                        print('They correspond to the following orbitals : ' + str(self.availiableOrbitals) )
                        print('Again do not trust the plot that was just produced' )
                if spin[0] == 0:
                    labels = ['Energy','Spin-Up']
                elif spin[0] == 1:
                    labels = ['Energy','Spin-Down']

            ret = np.zeros(shape=(len(projected[0].energies),len(spin)+1))
            ret[:,0] = projected[0].energies

            for iatom in atoms :
                if len(spin) == 2 :
                    ret[:,1:]+=self.dos_projected[iatom].values[:,orbitals].sum(axis=2)
                elif len(spin) == 1 :
                    ret[:,1]+=self.dos_projected[iatom].values[:,orbitals].sum(axis=1)

            return DensityOfStates(table=ret,title=title,labels=labels)

        def _parse_doscar(self, filename):
            """Helper method to parse the DOSCAR

            Parameters
            ----------
            filename : str
                The filename of the DOSCAR

            Returns
            -------
            dict
                Returns a dict with the total, projected, and labels

            Raises
            ------
            ValueError
                DOSCAR seems truncated
            """
            rf = open(filename)
            data = rf.readlines()
            rf.close()

            if len(data) < 5:
                raise ValueError('DOSCAR seems truncated')

            # Skipping the first lines of header
            iline = 5

            header = [float(x) for x in data[iline].strip().split()]
            ndos = int(header[2])
            iline += 1

            total_dos = [[float(x) for x in y.split()] for y in data[iline:iline + ndos]]
            total_dos = np.array(total_dos)

            iline += ndos
            ndos, total_ncols = total_dos.shape
            is_spin_polarized = False
            if total_ncols == 5:
                is_spin_polarized = True
                spins = ['dos_up','dos_down']
            # In case there are more lines of data, they are the projected DOS
            if len(data) > iline:

                projected_dos = []
                proj_orbitals = []
                ion_index =0

                while iline < len(data):

                    header = [float(x) for x in data[iline].split(";")[0].split()]

                    #print(header)
                    ionsList = re.findall("calculating FatBand for Element: (.*) Orbital.*", self.lobsterout)
                    proj_ions = re.findall("calculating FatBand for Element: (.*) Orbital\(s\):\s*.*", self.lobsterout)

                    proj_orbitals.append((proj_ions[ion_index],data[iline].split(";")[2]))
                    #print(proj_orbitals)
                    ion_index += 1

                    ndos = int(header[2])
                    iline += 1
                    tmp_dos = [[float(x) for x in y.split()] for y in data[iline:iline + ndos]]
                    tmp_dos = np.array(tmp_dos)

                    projected_dos.append(tmp_dos)

                    iline += ndos

                final_projected = []

                for i_ion in range(len(projected_dos)):
                    tmp_dos = np.zeros(shape = [len(projected_dos[i_ion][:,0]),10,2])
                    for ilabel, label in enumerate(proj_orbitals[i_ion][1].split(),1):
                        if is_spin_polarized == False :
                            tmp_dos[:,0,0] = projected_dos[i_ion][:,0]
                            if (label.find('s') == True):
                                tmp_dos[:,1,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('p_y') == True):
                                tmp_dos[:,2,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('p_z') == True):
                                tmp_dos[:,3,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('p_x') == True):
                                tmp_dos[:,4,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('d_xy') == True):
                                tmp_dos[:,5,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('d_yz') == True):
                                tmp_dos[:,6,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('d_z^2') == True):
                                tmp_dos[:,7,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('d_xz') == True):
                                tmp_dos[:,8,0] += projected_dos[i_ion][:,ilabel]
                            elif(label.find('d_x^2-y^2') == True):
                                tmp_dos[:,9,0] += projected_dos[i_ion][:,ilabel]
                        else:
                            tmp_dos[:,0,0] = projected_dos[i_ion][:,0]
                            tmp_dos[:,0,1] = projected_dos[i_ion][:,0]
                            if (label.find('s') == True):
                                tmp_dos[:,1,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,1,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('p_y') == True):
                                tmp_dos[:,2,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,2,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('p_z') == True):
                                tmp_dos[:,3,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,3,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('p_x') == True):
                                tmp_dos[:,4,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,4,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('d_xy') == True):
                                tmp_dos[:,5,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,5,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('d_yz') == True):
                                tmp_dos[:,6,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,6,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('d_z^2') == True):
                                tmp_dos[:,7,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,7,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('d_xz') == True):
                                tmp_dos[:,8,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,8,1] += projected_dos[i_ion][:,2*ilabel]
                            elif(label.find('d_x^2-y^2') == True):
                                tmp_dos[:,9,0] += projected_dos[i_ion][:,2*ilabel-1]
                                tmp_dos[:,9,1] += projected_dos[i_ion][:,2*ilabel]
                    final_projected.append(tmp_dos)
                    final_labels_index = {'energies':None,'s':0,'p_y':1,'p_z':2, 'p_x':3 , 'd_xy': 4,  'd_yz': 5, 'd_z^2': 6, 'd_xz':7,'d_x^2-y^2':8}
                    final_labels = list(final_labels_index.keys())
                return {'total': total_dos, 'projected': final_projected, 'projected_labels_info':final_labels , 'ions':ionsList}

            else:

                return {'total': total_dos}

        def _readFileNames(self):
            """Helper method to parse filenames form the lobster outfile

            Returns
            -------
            None
                None
            """
            self.file_names = []
            self.ionsList = re.findall(
                "calculating FatBand for Element: (.*) Orbital.*", self.lobsterout
            )
            proj_orbitals = re.findall(
                "calculating FatBand for Element: (.*) Orbital\(s\):\s*(.*)",
                self.lobsterout,
            )
            for proj in proj_orbitals:
                orbitals = proj[1].split()
                for orbital in orbitals:
                    fileName = f"{self.dirname}{os.sep}FATBAND_" + proj[0] + "_" + orbital + ".lobster"
                    self.file_names.append(fileName)
            return None

        def _readFatBands(self):
            """Helpermethod to parse the fatbands

            Returns
            -------
            None
                None
            """
            rf = open(self.file_names[0], "r")
            projFile = rf.read()
            rf.close()

            ##########################################################################################
            # kpoints
            ##########################################################################################
            raw_kpoints = re.findall("# K-Point \d+ :\s*([-\.\\d]*)\s*([-\.\\d]*)\s*([-\.\\d]*)", projFile)
            self.kpointsCount = len(raw_kpoints)
            self.kpoints = np.zeros(shape=(self.kpointsCount, 3))
            for ik in range(len(raw_kpoints)):
                for coord in range(3):
                    self.kpoints[ik][coord] = raw_kpoints[ik][coord]

            self.bandsCount = int(re.findall('NBANDS (\d*)', projFile)[0])
            self.orbitalCount = 10
            self.spd = np.zeros(
                shape=(
                    self.kpointsCount,
                    self.bandsCount,
                    self.nspin,
                    self.ionsCount + 1,
                    len(self.orbitals) + 2,
                )
            )

            self.bands = np.zeros(shape=(self.kpointsCount, self.bandsCount,self.nspin))
            for file in range(len(self.file_names)):
                rf = open(self.file_names[file], "r")
                projFile = rf.read()
                rf.close()

                fatbands_info =  re.findall("#\sFATBAND\sfor(.*)",projFile)[0].split()
                current_ion = fatbands_info[0]
                current_orbital = fatbands_info[1][1:]

                iion = 0
                iorbital = 0
                for i in range(len(self.ionsList)):
                    if self.ionsList[i] == current_ion:
                        iion = i
                for i in range(len(self.orbitals)):
                    if self.orbitals[i] == current_orbital:
                        iorbital = i + 1

                fatbands = re.split("# K-Point",projFile)[1:]
                for ik,fatband in enumerate(fatbands[:]):

                    for iband ,band in enumerate(fatband.split('\n')[1:-1]):
                        if self.nspin == 2:
                            if iband < self.bandsCount:
                                self.bands[ik, iband, 0] = float(band.split()[1])
                                self.bands[ik, iband, 1] = float(fatband.split('\n')[1:][iband + self.bandsCount].split()[1])

                                self.spd[ik, iband, 0, iion, iorbital] = float(band.split()[2])
                                self.spd[ik, iband, 1, iion, iorbital] = float(fatband.split('\n')[1:-1][iband + self.bandsCount].split()[2])

                        else:
                            self.bands[ik, iband, 0] =  float(band.split()[1])
                            self.spd[ik, iband, 0, iion, iorbital] = float(band.split()[2])
                self.spd[:, :, :, :, -1] = np.sum(self.spd[:, :, :, :, 1:-1], axis=4)
                self.spd[:, :, :, -1, :] = np.sum(self.spd[:, :, :, 0:-1, :], axis=3)
                self.spd[:, :, :, -1, 0] = 0

            return None

        def _spd2projected(self, spd, nprinciples=1):
            """A helper method to conver spd format to projected format
            to be inputed in to pyprocar.core.ElectronicBandStructure

            Parameters
            ----------
            spd : np.ndarray
                The spd array
            nprinciples : int, optional
                The principle quantum number, by default 1

            Returns
            -------
            np.ndarray
                The projected array
            """
            # This function is for VASP
            # non-pol and colinear
            # spd is formed as (nkpoints,nbands, nspin, natom+1, norbital+2)
            # natom+1 > last column is total
            # norbital+2 > 1st column is the number of atom last is total
            # non-colinear
            # spd is formed as (nkpoints,nbands, nspin +1 , natom+1, norbital+2)
            # natom+1 > last column is total
            # norbital+2 > 1st column is the number of atom last is total
            # nspin +1 > last column is total
            if spd is None:
                return None
            natoms = spd.shape[3] - 1
            nkpoints = spd.shape[0]

            nbands = spd.shape[1]
            nspins = spd.shape[2]

            norbitals = spd.shape[4] - 2
            # if spd.shape[2] == 4:
            #     nspins = 3
            # else:
            #     nspins = spd.shape[2]
            # if nspins == 2:
            #     nbands = int(spd.shape[1] / 2)
            # else:
            #     nbands = spd.shape[1]
            projected = np.zeros(
                shape=(nkpoints, nbands, natoms, nprinciples, norbitals, nspins),
                dtype=spd.dtype,
            )
            temp_spd = spd.copy()
            # (nkpoints,nbands, nspin, natom, norbital)
            temp_spd = np.swapaxes(temp_spd, 2, 4)
            # (nkpoints,nbands, norbital , natom , nspin)
            temp_spd = np.swapaxes(temp_spd, 2, 3)
            # (nkpoints,nbands, natom, norbital, nspin)
            # projected[ikpoint][iband][iatom][iprincipal][iorbital][ispin]
            # if nspins == 3:
            #     projected[:, :, :, 0, :, :] = temp_spd[:, :, :-1, 1:-1, :-1]
            # elif nspins == 2:
            #     projected[:, :, :, 0, :, 0] = temp_spd[:, :nbands, :-1, 1:-1, 0]
            #     projected[:, :, :, 0, :, 1] = temp_spd[:, nbands:, :-1, 1:-1, 0]
            # else:
            projected[:, :, :, 0, :, :] = temp_spd[:, :, :-1, 1:-1, :]
            return projected

        def _createKPath(self):
            """Helper method to format thwe kpath information into
             the pyprocar.core.KPath

            Returns
            -------
            None
                None
            """
            self.special_kpoints = np.zeros(shape = (len(self.kticks) -1 ,2,3) )
            self.modified_knames = []
            for itick in range(len(self.kticks)):
                if itick != len(self.kticks) - 1:
                    self.special_kpoints[itick,0,:] = self.kpoints[self.kticks[itick]]
                    self.special_kpoints[itick,1,:] = self.kpoints[self.kticks[itick+1]]
                    self.modified_knames.append([self.knames[itick], self.knames[itick+1] ])

            has_time_reversal = True

            self.kpath = KPath(
                            knames=self.modified_knames,
                            special_kpoints=self.special_kpoints,
                            kticks = self.kticks,
                            ngrids=self.ngrids,
                            has_time_reversal=has_time_reversal,
                        )

            return None

        def _getSpecialKpoints(self):
            """Helper method to get the special kpoints for a given code

            Returns
            -------
            None
                None
            """

            if self.code == 'qe':
                numK = int(re.findall("K_POINTS.*\n([0-9]*)", self.scfIn)[0])

                raw_kpoints = re.findall(
                    "K_POINTS.*\n\s*[0-9]*.*\n" + numK * "(.*)\n", self.scfIn,
                )[0]

                raw_high_symmetry = []
                self.knames = []
                self.kticks = []
                tickCountIndex = 0
                for x in raw_kpoints:
                    if len(x.split()) == 5:

                        raw_high_symmetry.append(
                            (float(x.split()[0]), float(x.split()[1]), float(x.split()[2]))
                        )
                        self.knames.append("%s" % x.split()[4].replace("!", ""))
                        self.kticks.append(tickCountIndex)
                    if float(x.split()[3]) == 0:
                        tickCountIndex += 1
                self.high_symmetry_points = np.array(raw_high_symmetry)
                self.nhigh_sym = len(self.knames)

                self.ngrids = []
                tick_Count = 1
                for ihs in range(self.nhigh_sym):
                    # self.kticks.append(tick_Count - 1)
                    self.ngrids.append(int(float(raw_kpoints[ihs].split()[3])))
                    tick_Count += int(float(raw_kpoints[ihs].split()[3]))
            else:
                self.knames = []
                self.kticks = []
                self.high_symmetry_points =[]
                self.nhigh_sym = []
                self.ngrids = []

            return None

        def parse_structure(self,
                outcar:str=None,
                poscar:str=None,
                procar:str=None,
                reciprocal_lattice:np.ndarray=None,
                kpoints:str=None,
                interpolation_factor:int=1,
                fermi:float=None):
            """A method to parse the electronic sturcutue information

            Parameters
            ----------
            outcar : str, optional
                The OUTCAR filename, by default None
            poscar : str, optional
                The POSCAR filename, by default None
            procar : str, optional
                The POSCAR filename, by default None
            reciprocal_lattice : np.ndarray, optional
                The reciprocal lattice matrix, by default None
            kpoints : str, optional
                The KPOINTS file name, by default None
            interpolation_factor : int, optional
                The interpolation factor, by default 1
            fermi : float, optional
                The fermi energy, by default None

            Returns
            -------
            None
                None
            """

            if self.code == "vasp":
                if outcar is not None:
                    outcar = vasp.Outcar(outcar)
                    if fermi is None:
                        fermi = outcar.efermi
                    reciprocal_lattice = outcar.reciprocal_lattice
                if poscar is not None:
                    poscar = vasp.Poscar(poscar)
                    structure = poscar.structure
                    if reciprocal_lattice is None:
                        reciprocal_lattice = poscar.structure.reciprocal_lattice

                if kpoints is not None:
                    kpoints = vasp.Kpoints(kpoints)
                    kpath = kpoints.kpath

                procar = vasp.Procar(procar,
                                    structure,
                                    reciprocal_lattice,
                                    kpath,
                                    fermi,
                                    interpolation_factor=interpolation_factor)
                ebs = procar.ebs

            elif self.code == "qe":
                rf = open(f"{self.dirname}{self.scfIn_filename}", "r")
                self.scfIn = rf.read()
                rf.close()

                if self.dirname is None:
                    self.dirname = "bands"

                parser = qe.QEParser(scfIn_filename = "scf.in", dirname = self.dirname, dos_interpolation_factor = None)
                self.prefix = parser.prefix
                self.non_colinear = parser.non_colinear
                self.spinCalc = parser.spinCalc
                self.spin_orbit = parser.spin_orbit
                self.nspin = parser.nspin
                self.bandsCount = parser.bandsCount

                self.atomic_positions = parser.atomic_positions
                self.direct_lattice = parser.direct_lattice
                self.species_list = parser.nspecies
                self.species_list = parser.species_list
                self.ionsCount = parser.ionsCount
                self.ions = parser.ions
                self.alat = parser.alat
                self.composition = parser.composition
                self.efermi = parser.efermi
                self.reciprocal_lattice = parser.reciprocal_lattice

            return None