Пример #1
0
    def __init__(self, bandstructure_path, info_path, results_path):
        r"""
        Initialises an instance of the :class:`~effmass.inputs.Data` class and checks data using :meth:`check_data`.

        Args:
            bandStructure_path (str): The path to the bandstructure file.
            info_path (str): The path to the info file.
            results_path (str): The path to the results.out file.

        Returns:
            None.
        """

        super().__init__()

        assert (type(bandstructure_path) == str), "The bandStructure path must be a string"
        assert (type(info_path) == str), "The info path must be a string"
        assert (type(results_path) == str), "The results path must be a string"

        # load the Octopus data 
        band_struct = bandstructure.Bandstructure(bandstructure_path)
        self.number_of_kpoints = band_struct.num_kpoints
        info_data = info.Info(info_path)
        results_data = results.Results(results_path, self.number_of_kpoints)

        # unpack the reciprocal lattice vector
        lattice_vectors = info_data.get_lattice_vectors()
        lattice_vector, reciprocal_lattice = zip(*lattice_vectors)
        reciprocal_lattice_split = [lattice.split() for lattice in reciprocal_lattice]
        self.reciprocal_lattice = [[float(v) for v in vector] for vector in reciprocal_lattice_split]

        self.spin_channels = 1
        self.number_of_bands = band_struct.num_bands

        # load energies and occupancies from bandstructure file
        octo_data_energies, octo_data_occupancies = band_struct.get_eigenvalues()

        # change to shape (num kpoints, num bands)
        self.energies = octo_data_energies.T
        self.occupancy = octo_data_occupancies.T

        # handle negative occupancy values
        if np.any(self.occupancy < 0):
            warnings.warn("One or more occupancies in your data are negative. All negative occupancies will be set to zero.")
            self.occupancy[ self.occupancy < 0 ] = 0.0

        # load kpoints from bandstructure file and into an numpy array
        kpoints = band_struct.kpoints
        kx, ky, kz = zip(*kpoints)
        kpoints = np.array([kx, ky, kz])

        # change to shape (number_of_kpoints, 3)
        self.kpoints = kpoints.T

        self.CBM = extrema._calc_CBM(self.occupancy, self.energies)
        self.VBM = extrema._calc_VBM(self.occupancy, self.energies)
        self.fermi_energy = (self.CBM + self.VBM) / 2
        self.check_data(self.spin_channels, self.number_of_kpoints, self.number_of_bands,
                   self.CBM, self.VBM, self.fermi_energy, self.occupancy)
Пример #2
0
def test_bandedge_energy(toy_segments, toy_data_object):
    assert math.isclose(
        toy_segments[0]._bandedge_energy(toy_data_object),
        extrema._calc_VBM(toy_data_object.occupancy, toy_data_object.energies))
    assert math.isclose(
        toy_segments[1]._bandedge_energy(toy_data_object),
        extrema._calc_CBM(toy_data_object.occupancy, toy_data_object.energies))
    assert math.isclose(
        toy_segments[2]._bandedge_energy(toy_data_object),
        extrema._calc_CBM(toy_data_object.occupancy, toy_data_object.energies))
Пример #3
0
    def __init__(self, outcar_path, procar_path, ignore=0):
        r"""
        Initialises an instance of the :class:`~effmass.inputs.Data` class and checks data using :meth:`check_data`.

        Args:
            outcar_path (str): The path to the OUTCAR file
            procar_path (str): The path to the PROCAR file
            ignore (int): The number of kpoints to ignore at the beginning of the bandstructure slice through kspace (useful for hybrid calculations where zero weightings are appended to a previous self-consistent calculation).
        
        Returns: 
            None.
        """
        assert (type(outcar_path) == str), "The OUTCAR path must be a string"
        assert (type(procar_path) == str), "The PROCAR path must be a string"
        assert (type(ignore) == int and ignore >= 0
                ), "The number of kpoints to ignore must be a positive integer"

        reciprocal_lattice = outcar.reciprocal_lattice_from_outcar(outcar_path)
        vasp_data = procar.Procar()
        vasp_data.read_from_file(procar_path)

        if vasp_data.calculation[
                'spin_polarised']:  # to account for the change in PROCAR format for calculations with 2 spin channels (1 k-point block ---> 2 k-point blocks)
            blocks = 2
        else:
            blocks = 1

        self.spin_channels = vasp_data.spin_channels
        self.number_of_kpoints = vasp_data.number_of_k_points - ignore
        self.number_of_bands = vasp_data.number_of_bands
        self.number_of_ions = vasp_data.number_of_ions
        self.kpoints = vasp_data.k_points[ignore:]
        self.energies = vasp_data.bands[self.number_of_bands * ignore:,
                                        1:].reshape(
                                            self.number_of_kpoints,
                                            self.number_of_bands * blocks).T
        self.occupancy = vasp_data.occupancy[self.number_of_bands * ignore:,
                                             1:].reshape(
                                                 self.number_of_kpoints,
                                                 self.number_of_bands *
                                                 blocks).T
        self.reciprocal_lattice = reciprocal_lattice * 2 * math.pi
        self.CBM = extrema._calc_CBM(self.occupancy, self.energies)
        self.VBM = extrema._calc_VBM(self.occupancy, self.energies)
        self.fermi_energy = (self.CBM + self.VBM) / 2
        self.dos = []
        self.integrated_dos = []
        self.check_data()
Пример #4
0
    def __init__(self, directory_path, output_name='calculation.out'):
        r"""
        Initialises an instance of the :class:`~effmass.inputs.DataAims` class and checks data using :meth:`check_data`.

        Args:
            directory_path (str): The path to the directory containing output, geometry.in, control.in and bandstructure files
	    output_name (str): Name of the output file - contrary to the rest of the files, this is chosen by the user during an Aims run. Defaults to 'aims.out'.

        Returns:
            None.
        """
        super().__init__()

        assert (type(directory_path) == str), "The file path must be a string"

        "Finding reciprocal lattice vectors"

        latvec = []

        for line in open("{}/geometry.in".format(directory_path)):
            line = line.split("\t")[0]
            words = line.split()
            if len(words) == 0:
                continue
            if words[0] == "lattice_vector":
                if len(words) != 4:
                    raise Exception("geometry.in: Syntax error in line '"+line+"'")
                latvec.append(np.array(words[1:4]))

        if len(latvec) != 3:
            raise Exception("geometry.in: Must contain exactly 3 lattice vectors")

        latvec = np.asarray(latvec)
        latvec = latvec.astype(np.float)

        #Calculate reciprocal lattice vectors
        rlatvec = []
        volume = (np.dot(latvec[0,:],np.cross(latvec[1,:],latvec[2,:])))
        rlatvec.append(np.array(2*math.pi*np.cross(latvec[1,:],latvec[2,:])/volume))
        rlatvec.append(np.array(2*math.pi*np.cross(latvec[2,:],latvec[0,:])/volume))
        rlatvec.append(np.array(2*math.pi*np.cross(latvec[0,:],latvec[1,:])/volume))

        reciprocal_lattice = np.asarray(rlatvec)
        self.reciprocal_lattice = reciprocal_lattice


        "Finding spin channels"

        spin_channels = 0

        for line in open("{}/{}".format(directory_path, output_name)):
            line = line.split("\t")[0]
            if "include_spin_orbit" in line:
               spin_channels = 4
               break
            elif "Number of spin channels" in line:
               words = line.split()
               spin_channels = int(words[-1])
               break

        self.spin_channels = spin_channels

        "Finding number of bands"

        number_of_bands = 0

        for line in open("{}/{}".format(directory_path, output_name)):
            line = line.split("\t")[0]
            if "Number of Kohn-Sham" in line:
                words = line.split()
                number_of_bands = int(words[-1])

                break

        if spin_channels == 2 or spin_channels == 4: #Doubling for spin-polarised calculation
            number_of_bands = 2*number_of_bands

        self.number_of_bands = number_of_bands

        "Finding number of kpoints and determining number of BZ paths"

        number_of_kpoints = 0
        number_of_BZ_paths = 0
        path_list = []

        for line in open("{}/{}".format(directory_path, output_name)):
            line = line.split("\n")[0]
            if not line.startswith("#") and "output" in line:
                if "band" in line:
                   words = line.split()
                   if words[0]=="output" and words[1]=="band":
                     path_list.append(int(words[8]))
                     number_of_BZ_paths += 1

        number_of_kpoints = sum(path_list)

        "Reading out bandstructure files to determine kpoint, energy and occupation matrices"

        kpoints = np.zeros([number_of_kpoints,3])
        energies = np.zeros([number_of_bands,number_of_kpoints])
        occupancy = np.zeros([number_of_bands,number_of_kpoints])
        path_counter = 0

        if spin_channels == 1 or spin_channels == 4:
            kpoint_counter = 0
            while path_counter<number_of_BZ_paths:
                kpoint_counter = sum(path_list[:path_counter])
                for line in open("{}/band1{:03d}.out".format(directory_path, path_counter+1)):
                    line = line.split("\t")[0]
                    words = line.split()
                    kpoints[int(kpoint_counter),0] = float(words[1])
                    kpoints[int(kpoint_counter),1] = float(words[2])
                    kpoints[int(kpoint_counter),2] = float(words[3])
                    for i in range(number_of_bands):
                        energies[i,int(kpoint_counter)] = float(words[5+2*i])
                        occupancy[i,int(kpoint_counter)] = float(words[4+2*i])
                    kpoint_counter += 1
                path_counter +=1

        if spin_channels == 2:
            while path_counter<number_of_BZ_paths:
                kpoint_counter = int(sum(path_list[:path_counter]))
                for line in open("{}/band1{:03d}.out".format(directory_path, path_counter+1)):
                    line = line.split("\t")[0]
                    words = line.split()
                    kpoints[int(kpoint_counter),0] = float(words[1])
                    kpoints[int(kpoint_counter),1] = float(words[2])
                    kpoints[int(kpoint_counter),2] = float(words[3])
                    for i in range(number_of_bands//2):
                        energies[i,int(kpoint_counter)] = float(words[5+2*i])
                        occupancy[i,int(kpoint_counter)] = float(words[4+2*i])
                    kpoint_counter += 1
                kpoint_counter = int(sum(path_list[:path_counter]))
                for line in open("{}/band2{:03d}.out".format(directory_path, path_counter+1)):
                    line = line.split("\t")[0]
                    words = line.split()
                    for i in range(number_of_bands//2):
                        energies[number_of_bands//2+i,kpoint_counter] = float(words[5+2*i])
                        occupancy[number_of_bands//2+i,kpoint_counter] = float(words[4+2*i])
                    kpoint_counter += 1
                path_counter += 1

        "Delete double kpoints at path edges"

        index_count = len(kpoints)
        index = 0
        while index < index_count-1:
            if np.array_equal(kpoints[index],kpoints[index+1]):
                kpoints = np.delete(kpoints,index+1,axis=0)
                energies = np.delete(energies,index+1,axis=1)
                occupancy = np.delete(occupancy,index+1,axis=1)
                index_count = len(kpoints)
            index += 1

        self.number_of_kpoints = len(kpoints)


        self.CBM = extrema._calc_CBM(occupancy, energies)
        self.VBM = extrema._calc_VBM(occupancy, energies)
        self.fermi_energy = (self.CBM + self.VBM) / 2

        "Cutting energy values in a range of 30 eV above and below the Fermi level. FHI AIMS is all electron, but not all states are needed for a meaningful effmass calculation"

        index_count = len(occupancy)
        index = 0
        while index < index_count-1:
            if all(item < self.fermi_energy - 30 for item in energies[index]):
                energies = np.delete(energies, index, axis = 0)
                occupancy = np.delete(occupancy, index, axis = 0)
                index_count = len(occupancy)
            elif all(item > self.fermi_energy + 30 for item in energies[index]):
                energies = np.delete(energies, index, axis = 0)
                occupancy = np.delete(occupancy, index, axis = 0)
                index_count = len(occupancy)
            else:
                index += 1

        self.energies = energies
        self.occupancy = occupancy
        self.kpoints = kpoints

        self.check_data(self.spin_channels, self.number_of_kpoints, self.number_of_bands, 
                   self.CBM, self.VBM, self.fermi_energy, self.occupancy)
Пример #5
0
    def __init__(self, outcar_path, procar_path, ignore=0, **kwargs):
        r"""
        Initialises an instance of the :class:`~effmass.inputs.Data` class and checks data using :meth:`check_data`.

        Args:
            outcar_path (str): The path to the OUTCAR file
            procar_path (:obj:`str` or :obj:`list`): The path(s) to one or more PROCAR files.
            
            ignore (int): The number of kpoints to ignore at the beginning of the bandstructure slice through kspace (useful for hybrid calculations where zero weightings are appended to a previous self-consistent calculation).
            **kwargs: Additional keyword arguments for reading the PROCAR file(s). 
 
        Returns: 
            None.
        """

        super().__init__()

        assert (type(outcar_path) == str), "The OUTCAR path must be a string"
        assert (type(ignore) == int and ignore >= 0
                ), "The number of kpoints to ignore must be a positive integer"

        reciprocal_lattice = outcar.reciprocal_lattice_from_outcar(outcar_path)
        if isinstance(procar_path, list):
            vasp_data = procar.Procar.from_files(procar_path, **kwargs)
        elif isinstance(procar_path, str):
            vasp_data = procar.Procar.from_file(procar_path, **kwargs)
        else:
            raise TypeError('procar_path must be a string or list of strings')

        self.spin_channels = vasp_data.spin_channels
        self.number_of_bands = vasp_data.number_of_bands

        number_of_kpoints = vasp_data.number_of_k_points
        vasp_data_energies = np.array( [ band.energy for band in np.ravel( vasp_data.bands ) ] )
        vasp_data_occupancies = np.array( [ band.occupancy for band in np.ravel( vasp_data.bands ) ] )
        if vasp_data.calculation['spin_polarised']: # to account for the change in PROCAR format for calculations with 2 spin channels (1 k-point block ---> 2 k-point blocks)
            energies = np.zeros([self.number_of_bands*2,number_of_kpoints]) # This is a very ugly way to slice 'n' dice. Should avoid creating new array and use array methods instead. But it does the job so will keep for now.
            for i in range(self.number_of_bands):
                energies[i] = vasp_data_energies.reshape(
                                            number_of_kpoints*2,  # factor of 2 for each kpoint block
                                            self.number_of_bands).T[i][:number_of_kpoints]
                energies[self.number_of_bands+i] = vasp_data_energies.reshape(
                                            number_of_kpoints*2,
                                            self.number_of_bands).T[i][number_of_kpoints:]
            occupancy = np.zeros([self.number_of_bands*2,number_of_kpoints])
            for i in range(self.number_of_bands):
                occupancy[i] = vasp_data_occupancies.reshape(
                                                 number_of_kpoints*2,
                                                 self.number_of_bands).T[i][:number_of_kpoints]
                occupancy[self.number_of_bands+i] = vasp_data_occupancies.reshape(
                                                 number_of_kpoints*2,
                                                 self.number_of_bands).T[i][number_of_kpoints:]
        else:
            energies = vasp_data_energies.reshape(
                                            number_of_kpoints,
                                            self.number_of_bands).T
            occupancy = vasp_data_occupancies.reshape(
                                                 number_of_kpoints,
                                                 self.number_of_bands).T

        # remove values which are from the self-consistent calculation prior to the bandstructure calculation (workflow for hybrid functionals)
        self.energies = np.delete(energies,list(range(ignore)),1)
        self.occupancy = np.delete(occupancy,list(range(ignore)),1)
        self.number_of_kpoints = number_of_kpoints - ignore

        # handle negative occupancy values
        if np.any(self.occupancy < 0):
            warnings.warn("One or more occupancies in your PROCAR file are negative. All negative occupancies will be set to zero.")
            self.occupancy[ self.occupancy < 0 ] = 0.0

        self.kpoints = np.array( [ kp.frac_coords 
            for kp in vasp_data.k_points[ignore:vasp_data.number_of_k_points] ] )
        self.reciprocal_lattice = reciprocal_lattice * 2 * math.pi
        self.CBM = extrema._calc_CBM(self.occupancy, self.energies)
        self.VBM = extrema._calc_VBM(self.occupancy, self.energies)
        self.fermi_energy = (self.CBM + self.VBM) / 2
        self.dos = []
        self.integrated_dos = []
        self.check_data(self.spin_channels, self.number_of_kpoints, self.number_of_bands, 
                   self.CBM, self.VBM, self.fermi_energy, self.occupancy)
Пример #6
0
def test_calc_VBM(toy_data_object):

    assert extrema._calc_VBM(toy_data_object.occupancy,toy_data_object.energies) == 0.5
Пример #7
0
    def __init__(self, outcar_path, procar_path, ignore=0):
        r"""
        Initialises an instance of the :class:`~effmass.inputs.Data` class and checks data using :meth:`check_data`.

        Args:
            outcar_path (str): The path to the OUTCAR file
            procar_path (str): The path to the PROCAR file
            ignore (int): The number of kpoints to ignore at the beginning of the bandstructure slice through kspace (useful for hybrid calculations where zero weightings are appended to a previous self-consistent calculation).
        
        Returns: 
            None.
        """
        assert (type(outcar_path) == str), "The OUTCAR path must be a string"
        assert (type(procar_path) == str), "The PROCAR path must be a string"
        assert (type(ignore) == int and ignore >= 0
                ), "The number of kpoints to ignore must be a positive integer"

        reciprocal_lattice = outcar.reciprocal_lattice_from_outcar(outcar_path)
        vasp_data = procar.Procar()
        vasp_data.read_from_file(procar_path)

        self.spin_channels = vasp_data.spin_channels
        self.number_of_bands = vasp_data.number_of_bands
        self.number_of_ions = vasp_data.number_of_ions

        number_of_kpoints = vasp_data.number_of_k_points
        if vasp_data.calculation[
                'spin_polarised']:  # to account for the change in PROCAR format for calculations with 2 spin channels (1 k-point block ---> 2 k-point blocks)
            energies = np.zeros(
                [self.number_of_bands * 2, number_of_kpoints]
            )  # This is a very ugly way to slice 'n' dice. Should avoid creating new array and use array methods instead. But it does the job so will keep for now.
            for i in range(self.number_of_bands):
                energies[i] = vasp_data.bands[:, 1:].reshape(
                    number_of_kpoints * 2,  # factor or 2 for each kpoint block
                    self.number_of_bands).T[i][:number_of_kpoints]
                energies[self.number_of_bands +
                         i] = vasp_data.bands[:, 1:].reshape(
                             number_of_kpoints * 2,
                             self.number_of_bands).T[i][number_of_kpoints:]
            occupancy = np.zeros([self.number_of_bands * 2, number_of_kpoints])
            for i in range(self.number_of_bands):
                occupancy[i] = vasp_data.occupancy[:, 1:].reshape(
                    number_of_kpoints * 2,
                    self.number_of_bands).T[i][:number_of_kpoints]
                occupancy[self.number_of_bands +
                          i] = vasp_data.occupancy[:, 1:].reshape(
                              number_of_kpoints * 2,
                              self.number_of_bands).T[i][number_of_kpoints:]
        else:
            energies = vasp_data.bands[:, 1:].reshape(number_of_kpoints,
                                                      self.number_of_bands).T
            occupancy = vasp_data.occupancy[:, 1:].reshape(
                number_of_kpoints, self.number_of_bands).T

        # remove values which are from the self-consistent calculation prior to the bandstructure calculation (workflow for hybrid functionals)
        self.energies = np.delete(energies, list(range(ignore)), 1)
        self.occupancy = np.delete(occupancy, list(range(ignore)), 1)
        self.number_of_kpoints = number_of_kpoints - ignore

        self.kpoints = vasp_data.k_points[ignore:vasp_data.number_of_k_points]
        self.reciprocal_lattice = reciprocal_lattice * 2 * math.pi
        self.CBM = extrema._calc_CBM(self.occupancy, self.energies)
        self.VBM = extrema._calc_VBM(self.occupancy, self.energies)
        self.fermi_energy = (self.CBM + self.VBM) / 2
        self.dos = []
        self.integrated_dos = []
        self.check_data()