def plot_all_hartree_pot(self):
     #plot planar averaged locpots, along with the difference for each axis
     if not self._locpot_bulk:
         print(
             'did not feed path to locpot file! Need this for plotting hartree pot'
         )
         return
     import matplotlib.pyplot as plt
     fig = plt.figure()
     ax = fig.add_subplot(3, 1, 1)
     ax.set_title('Locpot planar averaged potentials and difference')
     bulkloc = Locpot.from_file(self._locpot_bulk)
     defloc = Locpot.from_file(self._locpot_defect)
     for axis in [0, 1, 2]:
         ax = fig.add_subplot(3, 1, axis + 1)
         defect_axis = defloc.get_axis_grid(axis)
         defect_pot = defloc.get_average_along_axis(axis)
         pure_pot = bulkloc.get_average_along_axis(axis)
         ax.plot(defect_axis, pure_pot, 'r', label="Bulk potential")
         ax.plot(defect_axis, defect_pot, 'b', label="Defect potential")
         ax.plot(defect_axis,
                 defect_pot - pure_pot,
                 'k',
                 label='Defect-Bulk difference')
         ax.plot([self._frac_coords[axis]*self._lengths[axis]],\
                 [0], 'or', markersize=4.0, label='Defect site')
         ax.set_ylabel("axis " + str(axis + 1))
         if not axis:
             ax.legend()
     ax.set_xlabel("distance (Angstrom)")
     #plt.savefig('locpotavgdiffplot.png')
     plt.show()
 def plot_hartree_pot(self):
     #plot planar averages of bulk and defect (good for seeing global changes)
     import matplotlib.pyplot as plt
     fig = plt.figure()
     ax = fig.add_subplot(3,1,1)
     ax.set_title('Locpot planar averaged potentials')
     bulkloc=Locpot.from_file(self._locpot_bulk)
     defloc=Locpot.from_file(self._locpot_bulk)
     get_agrid = bulkloc.get_axis_grid
     get_baavg = bulkloc.get_average_along_axis
     get_daavg = defloc.get_average_along_axis
     for axis in [0,1,2]:
         ax = fig.add_subplot(3, 1, axis+1)
         latt_len = self._lengths[axis]
         ax.plot(get_agrid(axis),get_baavg(axis),'r',
                 label="Bulk potential")
         ax.plot(get_agrid(axis), get_daavg(axis),'b',
                 label="Defect potential")
         ax.plot([self._frac_coords[axis]*latt_len], [0], 'or', 
                 markersize=4.0, label="Defect site")
         ax.set_ylabel("axis "+str(axis+1))
         if not axis:
             ax.legend()
     ax.set_xlabel("distance (Angstrom)")
     #plt.savefig('locpotavgplot.png')
     plt.show()
Example #3
0
def get_freysoldt_correction(defect_type, defect_specie, path_to_defect_locpot,path_to_pure_locpot,charge,
                             dielectric_constant,defect_site_coordinates,energy_cutoff=500,get_plot=False):
    
    ''' Function to perform charge corrections according to the method proposed py Freysoldt
        If this correction is used, please reference Freysoldt's original paper.
        doi: 10.1103/PhysRevLett.102.016402
        
        Args:
            defect_type: 'vacancy' or 'interstitial'
            defect_specie: string with element occupying the defect site
            path_to_defect_locpot: path to LOCPOT file of defect structure
            path_to_pure_locpot: path to LOCPOT file of Pure structure
            charge: Charge of the defected system
            dielectric_constant: Dielectric constant
            defect_site_coordinates: numpy array with fractional coordinates of defect site
            energy_cutoff: Cut-off of plane wave expansion
            get_plot: return also Matplotlib object with plot
            
        Returns:
            Freysoldt corrections values as a dictionary 
            '''
    # acquiring data from LOCPOT files    
    locpot_pure = Locpot.from_file(path_to_pure_locpot)
    vol_data_pure = VolumetricData(locpot_pure.structure,locpot_pure.data)
    
    locpot_defect = Locpot.from_file(path_to_defect_locpot)
    vol_data_defect = VolumetricData(locpot_defect.structure,locpot_defect.data)
    
    parameters = {}
    parameters['axis_grid'] = []
    parameters['bulk_planar_averages'] = []
    parameters['defect_planar_averages'] = []
    for i in range(0,3):
        parameters['axis_grid'].append(vol_data_pure.get_axis_grid(i))
        parameters['bulk_planar_averages'].append(vol_data_pure.get_average_along_axis(i))
        parameters['defect_planar_averages'].append(vol_data_defect.get_average_along_axis(i))
    parameters['initial_defect_structure'] = locpot_defect.structure
    parameters['defect_frac_sc_coords'] = defect_site_coordinates
    
    structure_bulk = locpot_pure.structure
    defect_site = PeriodicSite(defect_specie, coords=defect_site_coordinates, lattice = locpot_pure.structure.lattice)
    
    module = importlib.import_module("pymatgen.analysis.defects.core")
    defect_class = getattr(module,defect_type)
    defect = defect_class(structure_bulk, defect_site, charge=charge, multiplicity=None)
    defect_entry = DefectEntry(defect,None,corrections=None,parameters=parameters)
    
    freysoldt_class = FreysoldtCorrection(dielectric_constant,energy_cutoff=energy_cutoff)
    
    freysoldt_corrections = freysoldt_class.get_correction(defect_entry)
  
    if get_plot:
        plt = freysoldt_class.plot(1)
        return freysoldt_corrections , plt
    else:    
        return freysoldt_corrections
Example #4
0
    def correction(self, title=None, partflag='All'):
        """
        Args:
            title: set if you want to plot the planar averaged potential
            partflag: four options
                'pc' for just point charge correction, or
               'potalign' for just potalign correction, or
               'All' for both, or
               'AllSplit' for individual parts split up (form [PC,potterm,full])
        """
        logger = logging.getLogger(__name__)
        logger.info('This is Freysoldt Correction.')
        if not self._q:
            if partflag == 'AllSplit':
                return [0.0, 0.0, 0.0]
            else:
                return 0.0

        if not type(self._purelocpot) is Locpot:
            logger.debug('Load bulk locpot')
            self._purelocpot = Locpot.from_file(self._purelocpot)

        logger.debug('\nRun PC energy')
        if partflag != 'potalign':
            energy_pc = self.pc()
            logger.debug('PC calc done, correction = %f', round(energy_pc, 4))
            logger.debug('Now run potenttial alignment script')

        if partflag != 'pc':
            if not type(self._deflocpot) is Locpot:
                logger.debug('Load defect locpot')
                self._deflocpot = Locpot.from_file(self._deflocpot)
            potalign = self.potalign(title=title)

        logger.info('\n\nFreysoldt Correction details:')
        if partflag != 'potalign':
            logger.info('PCenergy (E_lat) = %f', round(energy_pc, 5))
        if partflag != 'pc':
            logger.info('potential alignment (-q*delta V) = %f',
                        round(potalign, 5))
        if partflag in ['All', 'AllSplit']:
            logger.info('TOTAL Freysoldt correction = %f',
                        round(energy_pc + potalign, 5))

        if partflag == 'pc':
            return round(energy_pc, 5)
        elif partflag == 'potalign':
            return round(potalign, 5)
        elif partflag == 'All':
            return round(energy_pc + potalign, 5)
        else:
            return map(lambda x: round(x, 5),
                       [energy_pc, potalign, energy_pc + potalign])
    def freysoldt_loader(self, bulk_locpot=None):
        """Load metadata required for performing Freysoldt correction
        requires "bulk_path" and "defect_path" to be loaded to DefectEntry parameters dict.
        Can read gunzipped "LOCPOT.gz" files as well.

        Args:
            bulk_locpot (Locpot): Add bulk Locpot object for expedited parsing.
                If None, will load from file path variable bulk_path
        Return:
            bulk_locpot object for reuse by another defect entry (for expedited parsing)
        """
        if not self.defect_entry.charge:
            #dont need to load locpots if charge is zero
            return None

        if not bulk_locpot:
            bulk_locpot_path = os.path.join( self.defect_entry.parameters["bulk_path"],
                                             "LOCPOT")
            if os.path.exists(bulk_locpot_path):
                bulk_locpot = Locpot.from_file(bulk_locpot_path)
            elif os.path.exists(bulk_locpot_path+".gz"):
                bulk_locpot = Locpot.from_file(bulk_locpot_path + ".gz")
            else:
                raise FileNotFoundError(f"""Well I can't f*****g find a LOCPOT(.gz) in {self.defect_entry.parameters['bulk_path']}.
                   You sure there's one there pal? I need it to get the Freysoldt correction""")

        def_locpot_path = os.path.join( self.defect_entry.parameters["defect_path"],
                                             "LOCPOT")

        if os.path.exists(def_locpot_path):
            def_locpot = Locpot.from_file(def_locpot_path)
        elif os.path.exists(def_locpot_path + ".gz"):
            def_locpot = Locpot.from_file(def_locpot_path + ".gz")
        else:
            raise FileNotFoundError(f"""Well I can't f*****g find a LOCPOT(.gz) in {self.defect_entry.parameters['defect_path']}.
               You sure there's one there pal? I need it to get the Freysoldt correction""")

        axis_grid = [def_locpot.get_axis_grid(i) for i in range(3)]
        bulk_planar_averages = [bulk_locpot.get_average_along_axis(i) for i in range(3)]
        defect_planar_averages = [def_locpot.get_average_along_axis(i) for i in range(3)]

        defect_frac_sc_coords = self.defect_entry.site.frac_coords

        self.defect_entry.parameters.update({"axis_grid": axis_grid,
                                             "bulk_planar_averages": bulk_planar_averages,
                                             "defect_planar_averages": defect_planar_averages,
                                             "initial_defect_structure": def_locpot.structure,
                                             "defect_frac_sc_coords": defect_frac_sc_coords
                                             })

        return bulk_locpot
Example #6
0
def vacuum(path=None):
    '''
    Gets the energy of the vacuum level. It either parses potential.csv file if 
    available or tries to calculate planar potential from LOCPOT. If neither 
    file is available, function returns np.nan.  

    Args:
        path (`str`, optional): the path to potential.csv or LOCPOT files. 
            Can be the path to a directory in which either file is or you can 
            specify a path that must end in .csv or contain LOCPOT. Defaults to 
            looking for potential.csv or LOCPOT in cwd. 

    Returns:
        Maximum value of planar potential

    '''

    if type(path) == str and path.endswith('.csv'):
        df = pd.read_csv(path)
        max_potential = df['planar'].max()
        max_potential = round(max_potential, 3)

    elif type(path) == str and 'LOCPOT' in path:
        lpt = Locpot.from_file(path)
        planar = lpt.get_average_along_axis(2)
        max_potential = float(f"{np.max(planar): .3f}")

    else:
        if path is None:
            cwd = os.getcwd()
        else:
            cwd = path

        if os.path.isfile('{}/potential.csv'.format(cwd)):
            df = pd.read_csv('{}/potential.csv'.format(cwd))
            max_potential = df['planar'].max()
            max_potential = round(max_potential, 3)

        elif os.path.isfile('{}/LOCPOT'.format(cwd)):
            lpt = Locpot.from_file('{}/LOCPOT'.format(cwd))
            planar = lpt.get_average_along_axis(2)
            max_potential = float(f"{np.max(planar): .3f}")

        else:
            max_potential = np.nan
            warnings.formatwarning = _custom_formatwarning
            warnings.warn(
                'Vacuum electrostatic potential was not parsed from {} '
                'no LOCPOT or potential.csv files were provided.'.format(path))

    return max_potential
Example #7
0
    def freysoldt_loader(self, bulk_locpot=None):
        """Load metadata required for performing Freysoldt correction
        requires "bulk_path" and "defect_path" to be loaded to DefectEntry parameters dict.

        Args:
            bulk_locpot (Locpot): Add bulk Locpot object for expedited parsing.
                If None, will load from file path variable bulk_path
        Return:
            bulk_locpot object for reuse by another defect entry (for expedited parsing)
        """
        if not self.defect_entry.charge:
            #dont need to load locpots if charge is zero
            return None

        if not bulk_locpot:
            bulk_locpot_path = os.path.join(
                self.defect_entry.parameters["bulk_path"], "LOCPOT")
            bulk_locpot = Locpot.from_file(bulk_locpot_path)

        def_locpot_path = os.path.join(
            self.defect_entry.parameters["defect_path"], "LOCPOT")

        def_locpot = Locpot.from_file(def_locpot_path)

        axis_grid = [def_locpot.get_axis_grid(i) for i in range(3)]
        bulk_planar_averages = [
            bulk_locpot.get_average_along_axis(i) for i in range(3)
        ]
        defect_planar_averages = [
            def_locpot.get_average_along_axis(i) for i in range(3)
        ]

        defect_frac_sc_coords = self.defect_entry.site.frac_coords

        self.defect_entry.parameters.update({
            "axis_grid":
            axis_grid,
            "bulk_planar_averages":
            bulk_planar_averages,
            "defect_planar_averages":
            defect_planar_averages,
            "initial_defect_structure":
            def_locpot.structure,
            "defect_frac_sc_coords":
            defect_frac_sc_coords
        })

        return bulk_locpot
Example #8
0
 def test_init(self):
     filepath = os.path.join(test_dir, "LOCPOT")
     locpot = Locpot.from_file(filepath)
     self.assertAlmostEqual(-217.05226954, sum(locpot.get_average_along_axis(0)))
     self.assertAlmostEqual(locpot.get_axis_grid(0)[-1], 2.87629, 2)
     self.assertAlmostEqual(locpot.get_axis_grid(1)[-1], 2.87629, 2)
     self.assertAlmostEqual(locpot.get_axis_grid(2)[-1], 2.87629, 2)
 def __init__(self, locpot_bulk_path, locpot_defect_path, charge, epsilon, 
              site_frac_coords, encut, lengths=None, name=''):
     """
     Args:
         locpot_bulk: 
             Location of LOCPOT of bulk 
         locpot_defect: 
             Location of LOCPOT of defect 
         charge: 
             Charge relative to neutral defect (not relative to bulk)
         epsilon: 
             Dielectric constant obtained from relaxation run
         site_frac_coords: 
             Fractional coordinates of defect site as list
         encut: 
             Planewave basis energy cutoff used in VASP run (in eV)
         name:
             Name of the defect to write files
         lengths:
             Length of lattice vectors. 
     """
     self._locpot_bulk = locpot_bulk_path
     self._locpot_defect = locpot_defect_path
     self._charge = charge 
     self._epsilon = epsilon 
     self._frac_coords = site_frac_coords   
     self._encut = encut
     if not lengths:
         struct=Locpot.from_file(locpot_bulk_path)
         self._lengths=struct.structure.lattice.abc
         print('had to import lengths, if want to speed up set lengths='+str(self._lengths))
     else:
         self._lengths = lengths
     self._name = name
Example #10
0
 def from_files(poscar_filename, locpot_filename, outcar_filename, shift=0):
     p = Poscar.from_file(poscar_filename)
     l = Locpot.from_file(locpot_filename)
     o = Outcar(outcar_filename)
     return WorkFunctionAnalyzer(p.structure,
                                 l.get_average_along_axis(int(sys.argv[1])),
                                 o.efermi, shift=shift)
Example #11
0
def get_band_edges():
    """
    Calculate the band edge locations relative to the vacuum level
    for a semiconductor. If spin-polarized, returns all 4 band edges.
    """

    # Vacuum level energy from LOCPOT.
    locpot = Locpot.from_file("LOCPOT")
    evac = max(locpot.get_average_along_axis(2))

    vasprun = Vasprun("vasprun.xml")
    efermi = vasprun.efermi - evac

    if vasprun.get_band_structure().is_spin_polarized:
        eigenvals = {Spin.up: [], Spin.down: []}
        for band in vasprun.eigenvalues:
            for eigenvalue in vasprun.eigenvalues[band]:
                eigenvals[band[0]].append(eigenvalue)

        up_cbm = min([e[0] for e in eigenvals[Spin.up] if not e[1]]) - evac
        up_vbm = max([e[0] for e in eigenvals[Spin.up] if e[1]]) - evac
        dn_cbm = min([e[0] for e in eigenvals[Spin.down] if not e[1]]) - evac
        dn_vbm = max([e[0] for e in eigenvals[Spin.down] if e[1]]) - evac
        edges = {"up_cbm": up_cbm, "up_vbm": up_vbm, "dn_cbm": dn_cbm, "dn_vbm": dn_vbm, "efermi": efermi}

    else:
        bs = vasprun.get_band_structure()
        cbm = bs.get_cbm()["energy"] - evac
        vbm = bs.get_vbm()["energy"] - evac
        edges = {"cbm": cbm, "vbm": vbm, "efermi": efermi}

    return edges
Example #12
0
def plot_local_potential(axis=2, ylim=(-20, 0), fmt='pdf'):
    """
    Plot data from the LOCPOT file along any of the 3 primary axes.
    Useful for determining surface dipole moments and electric
    potentials on the interior of the material.

    Args:
        axis (int): 0 = x, 1 = y, 2 = z
        ylim (tuple): minimum and maximum potentials for the plot's y-axis.
        fmt (str): matplotlib format style. Check the matplotlib docs
            for options.
    """

    ax = plt.figure(figsize=(16, 10)).gca()

    locpot = Locpot.from_file('LOCPOT')
    structure = Structure.from_file('CONTCAR')
    vd = VolumetricData(structure, locpot.data)
    abs_potentials = vd.get_average_along_axis(axis)
    vacuum_level = max(abs_potentials)

    vasprun = Vasprun('vasprun.xml')
    bs = vasprun.get_band_structure()
    if not bs.is_metal():
        cbm = bs.get_cbm()['energy'] - vacuum_level
        vbm = bs.get_vbm()['energy'] - vacuum_level

    potentials = [potential - vacuum_level for potential in abs_potentials]
    axis_length = structure.lattice.lengths[axis]
    positions = np.arange(0, axis_length, axis_length / len(potentials))

    ax.plot(positions, potentials, linewidth=2, color='k')

    ax.set_xlim(0, axis_length)
    ax.set_ylim(ylim[0], ylim[1])

    ax.set_xticklabels(
        [r'$\mathrm{%s}$' % tick for tick in ax.get_xticks()], size=20)
    ax.set_yticklabels(
        [r'$\mathrm{%s}$' % tick for tick in ax.get_yticks()], size=20)
    ax.set_xlabel(r'$\mathrm{\AA}$', size=24)
    ax.set_ylabel(r'$\mathrm{V\/(eV)}$', size=24)

    if not bs.is_metal():
        ax.text(ax.get_xlim()[1], cbm, r'$\mathrm{CBM}$',
                horizontalalignment='right', verticalalignment='bottom',
                size=20)
        ax.text(ax.get_xlim()[1], vbm, r'$\mathrm{VBM}$',
                horizontalalignment='right', verticalalignment='top', size=20)
        ax.fill_between(ax.get_xlim(), cbm, ax.get_ylim()[1],
                        facecolor=plt.cm.jet(0.3), zorder=0, linewidth=0)
        ax.fill_between(ax.get_xlim(), ax.get_ylim()[0], vbm,
                        facecolor=plt.cm.jet(0.7), zorder=0, linewidth=0)

    if fmt == "None":
        return ax
    else:
        plt.savefig('locpot.{}'.format(fmt))
    plt.close()
Example #13
0
def plot_local_potential(axis=2, ylim=(-20, 0), fmt='pdf'):
    """
    Plot data from the LOCPOT file along any of the 3 primary axes.
    Useful for determining surface dipole moments and electric
    potentials on the interior of the material.

    Args:
        axis (int): 0 = x, 1 = y, 2 = z
        ylim (tuple): minimum and maximum potentials for the plot's y-axis.
        fmt (str): matplotlib format style. Check the matplotlib docs
            for options.
    """

    ax = plt.figure(figsize=(16, 10)).gca()

    locpot = Locpot.from_file('LOCPOT')
    structure = Structure.from_file('CONTCAR')
    vd = VolumetricData(structure, locpot.data)
    abs_potentials = vd.get_average_along_axis(axis)
    vacuum_level = max(abs_potentials)

    vasprun = Vasprun('vasprun.xml')
    bs = vasprun.get_band_structure()
    if not bs.is_metal():
        cbm = bs.get_cbm()['energy'] - vacuum_level
        vbm = bs.get_vbm()['energy'] - vacuum_level

    potentials = [potential - vacuum_level for potential in abs_potentials]
    axis_length = structure.lattice._lengths[axis]
    positions = np.arange(0, axis_length, axis_length / len(potentials))

    ax.plot(positions, potentials, linewidth=2, color='k')

    ax.set_xlim(0, axis_length)
    ax.set_ylim(ylim[0], ylim[1])

    ax.set_xticklabels(
        [r'$\mathrm{%s}$' % tick for tick in ax.get_xticks()], size=20)
    ax.set_yticklabels(
        [r'$\mathrm{%s}$' % tick for tick in ax.get_yticks()], size=20)
    ax.set_xlabel(r'$\mathrm{\AA}$', size=24)
    ax.set_ylabel(r'$\mathrm{V\/(eV)}$', size=24)

    if not bs.is_metal():
        ax.text(ax.get_xlim()[1], cbm, r'$\mathrm{CBM}$',
                horizontalalignment='right', verticalalignment='bottom',
                size=20)
        ax.text(ax.get_xlim()[1], vbm, r'$\mathrm{VBM}$',
                horizontalalignment='right', verticalalignment='top', size=20)
        ax.fill_between(ax.get_xlim(), cbm, ax.get_ylim()[1],
                        facecolor=plt.cm.jet(0.3), zorder=0, linewidth=0)
        ax.fill_between(ax.get_xlim(), ax.get_ylim()[0], vbm,
                        facecolor=plt.cm.jet(0.7), zorder=0, linewidth=0)

    if fmt == "None":
        return ax
    else:
        plt.savefig('locpot.{}'.format(fmt))
    plt.close()
Example #14
0
 def test_init(self):
     filepath = os.path.join(test_dir, 'LOCPOT')
     locpot = Locpot.from_file(filepath)
     self.assertAlmostEqual(-217.05226954,
                            sum(locpot.get_average_along_axis(0)))
     self.assertAlmostEqual(locpot.get_axis_grid(0)[-1], 2.87629, 2)
     self.assertAlmostEqual(locpot.get_axis_grid(1)[-1], 2.87629, 2)
     self.assertAlmostEqual(locpot.get_axis_grid(2)[-1], 2.87629, 2)
 def setUp(self):
     self.bl = Locpot.from_file(bl_path)
     self.dl = Locpot.from_file(dl_path)
     self.bs = self.bl.structure
     self.ds = self.dl.structure
     self.kbi = KumagaiBulkInit(self.bs,
                                self.bl.dim,
                                15,
                                optgamma=3.49423226983)
     self.kc = KumagaiCorrection(15,
                                 -3,
                                 3.49423226983,
                                 self.kbi.g_sum,
                                 self.bs,
                                 self.ds,
                                 bulk_locpot=self.bl,
                                 defect_locpot=self.dl)
Example #16
0
def get_band_edges():
    """
    Calculate the band edge locations relative to the vacuum level
    for a semiconductor.

    Returns:
        edges (dict): {'up_cbm': , 'up_vbm': , 'dn_cbm': , 'dn_vbm': , 'efermi'}
    """
    # Vacuum level energy from LOCPOT.
    locpot = Locpot.from_file('LOCPOT')
    evac = max(locpot.get_average_along_axis(2))

    vasprun = Vasprun('vasprun.xml')
    bs = vasprun.get_band_structure()
    eigenvals = vasprun.eigenvalues
    efermi = vasprun.efermi - evac

    if bs.is_spin_polarized:
        print(eigenvals[Spin.up])
        print([e[0] - evac for e in eigenvals[Spin.up][0]])
        up_cbm = min([
            min([e[0] for e in eigenvals[Spin.up][i] if not e[1]])
            for i in range(len(eigenvals[Spin.up]))
        ]) - evac
        up_vbm = max([
            max([e[0] for e in eigenvals[Spin.up][i] if e[1]])
            for i in range(len(eigenvals[Spin.up]))
        ]) - evac
        dn_cbm = min([
            min([e[0] for e in eigenvals[Spin.down][i] if not e[1]])
            for i in range(len(eigenvals[Spin.down]))
        ]) - evac
        dn_vbm = max([
            max([e[0] for e in eigenvals[Spin.down][i] if e[1]])
            for i in range(len(eigenvals[Spin.down]))
        ]) - evac
        edges = {
            'up_cbm': up_cbm,
            'up_vbm': up_vbm,
            'dn_cbm': dn_cbm,
            'dn_vbm': dn_vbm,
            'efermi': efermi
        }

    else:
        cbm = bs.get_cbm()['energy'] - evac
        vbm = bs.get_vbm()['energy'] - evac
        edges = {
            'up_cbm': cbm,
            'up_vbm': vbm,
            'dn_cbm': cbm,
            'dn_vbm': vbm,
            'efermi': efermi
        }

    return edges
 def plot_hartree_pot_diff(self):
     #only plot the difference in planar averaged potentials
     import matplotlib.pyplot as plt
     fig = plt.figure()
     ax = fig.add_subplot(3,1,1)
     ax.set_title('Locpot planar averaged potential difference')
     bulkloc=Locpot.from_file(self._locpot_bulk)
     defloc=Locpot.from_file(self._locpot_defect)
     for axis in [0,1,2]:
         ax = fig.add_subplot(3, 1, axis+1)
         defect_axis = defloc.get_axis_grid(axis)
         defect_pot = defloc.get_average_along_axis(axis)
         pure_pot = bulkloc.get_average_along_axis(axis)
         latt_len = self._lengths[axis]
         ax.plot(defect_axis,defect_pot-pure_pot,'b',
                 label='Defect-Bulk difference')
         ax.plot([self._frac_coords[axis] * latt_len], [0], 'or',
                 markersize=4.0, label='Defect site')
         ax.set_ylabel("axis "+str(axis+1))
         if not axis:
             ax.legend()
     ax.set_xlabel("distance (Angstrom)")
     #plt.savefig('locpotdiffplot.png')
     plt.show()
 def process_LOCPOT(self, locpot_path=None, dir_to_save=None):
     """
     This function process LOCPOT file obtained by VASP
     :param file_path: path to LOCPOT file
     :param dir_to_save: path to directory to save vacuum_lvl
     :return: nothing
     """
     if locpot_path == None:
         locpot_path = self.locpot_path
     if dir_to_save == None:
         dir_to_save = self.dir_to_save
     locpot = Locpot.from_file(locpot_path)
     avr = locpot.get_average_along_axis(2)
     self.vacuum_lvl = np.max(avr)
     self.save('vacuum_lvl', dir_to_save)
Example #19
0
def get_band_edges():
    """
    Calculate the band edge locations relative to the vacuum level
    for a semiconductor. For a metal, returns the fermi level.

    Returns:
        edges (dict): {'up_cbm': , 'up_vbm': , 'dn_cbm': , 'dn_vbm': , 'efermi'}
    """
    # Vacuum level energy from LOCPOT.
    locpot = Locpot.from_file('LOCPOT')
    evac = max(locpot.get_average_along_axis(2))

    vasprun = Vasprun('vasprun.xml')
    bs = vasprun.get_band_structure()
    eigenvals = vasprun.eigenvalues
    efermi = vasprun.efermi - evac

    if bs.is_metal():
        edges = {'up_cbm': None, 'up_vbm': None, 'dn_cbm': None, 'dn_vbm': None,
                 'efermi': efermi}

    elif bs.is_spin_polarized:
        up_cbm = min(
            [min([e[0] for e in eigenvals[Spin.up][i] if not e[1]])
             for i in range(len(eigenvals[Spin.up]))]) - evac
        up_vbm = max(
            [max([e[0] for e in eigenvals[Spin.up][i] if e[1]])
             for i in range(len(eigenvals[Spin.up]))]) - evac
        dn_cbm = min(
            [min([e[0] for e in eigenvals[Spin.down][i] if not e[1]])
             for i in range(len(eigenvals[Spin.down]))]) - evac
        dn_vbm = max(
            [max([e[0] for e in eigenvals[Spin.down][i] if e[1]])
             for i in range(len(eigenvals[Spin.down]))]) - evac
        edges = {'up_cbm': up_cbm, 'up_vbm': up_vbm, 'dn_cbm': dn_cbm,
                 'dn_vbm': dn_vbm, 'efermi': efermi}

    else:
        cbm = bs.get_cbm()['energy'] - evac
        vbm = bs.get_vbm()['energy'] - evac
        edges = {'up_cbm': cbm, 'up_vbm': vbm, 'dn_cbm': cbm, 'dn_vbm': vbm,
                 'efermi': efermi}

    return edges
Example #20
0
def get_band_edges():
    """
    Calculate the band edge locations relative to the vacuum level
    for a semiconductor. If spin-polarized, returns all 4 band edges.
    """

    # Vacuum level energy from LOCPOT.
    locpot = Locpot.from_file('LOCPOT')
    evac = max(locpot.get_average_along_axis(2))

    vasprun = Vasprun('vasprun.xml')
    efermi = vasprun.efermi - evac

    if vasprun.get_band_structure().is_spin_polarized:
        eigenvals = {Spin.up: [], Spin.down: []}
        for band in vasprun.eigenvalues:
            for eigenvalue in vasprun.eigenvalues[band]:
                eigenvals[band[0]].append(eigenvalue)

        up_cbm = min([e[0] for e in eigenvals[Spin.up] if not e[1]]) - evac
        up_vbm = max([e[0] for e in eigenvals[Spin.up] if e[1]]) - evac
        dn_cbm = min([e[0] for e in eigenvals[Spin.down] if not e[1]]) - evac
        dn_vbm = max([e[0] for e in eigenvals[Spin.down] if e[1]]) - evac
        edges = {
            'up_cbm': up_cbm,
            'up_vbm': up_vbm,
            'dn_cbm': dn_cbm,
            'dn_vbm': dn_vbm,
            'efermi': efermi
        }

    else:
        bs = vasprun.get_band_structure()
        cbm = bs.get_cbm()['energy'] - evac
        vbm = bs.get_vbm()['energy'] - evac
        edges = {'cbm': cbm, 'vbm': vbm, 'efermi': efermi}

    return edges
Example #21
0
 def load_data_for_ecstm(self):
     """
     This inner function load necessary data for ECSTM calculations
     :param outcar_path: str
     path to OUTCAR vasp file
     :param locpot_path: str
     path to LOCPOT vasp file
     :return:
     """
     try:
         self.E = np.load(self.path_to_data + '/E.npy')
         self.DOS = np.load(self.path_to_data + '/DOS.npy')
     except:
         import preprocessing_v2 as preprocessing
         p = preprocessing.Preprocessing(working_folder=self.working_folder)
         p.process_OUTCAR()
         self.E, self.DOS, dE_new = p.get_DOS(self.energy_range, self.dE)
         if dE_new != self.dE:
             print(
                 "WARNING! Something wrong with dE during DOS calculations")
         np.save(self.path_to_data + '/DOS.npy', self.DOS)
         np.save(self.path_to_data + '/E.npy', self.E)
     try:
         self.efermi = np.load(self.path_to_data + '/efermi.npy')
     except:
         print(
             f"ERROR! {self.path_to_data}/efermi.npy does not exist. Try to preprocess data"
         )
     try:
         self.vacuum_lvl = np.load(self.path_to_data + 'vacuum_lvl.npy')
     except:
         from pymatgen.io.vasp.outputs import Locpot
         locpot = Locpot.from_file(self.working_folder + '/LOCPOT')
         avr = locpot.get_average_along_axis(2)
         self.vacuum_lvl = np.max(avr)
         np.save(self.path_to_data + '/vacuum_lvl.npy', self.vacuum_lvl)
 def setUp(self):
     self.bl = Locpot.from_file(bl_path)
     self.dl = Locpot.from_file(dl_path)
     self.bs = self.bl.structure
     self.ds = self.dl.structure
Example #23
0
corrections = {}
include_freysoldt_corrections = True

###############################################################################
###############################################################################

# Paths have to end with backslash. I would update everything with os.path.join but I'm lazy
print('Path of pure calculation: "%s"\n' % pure_path)
print('Path of pure LOCPOT calculation: "%s"\n' % path_to_pure_locpot)
print('Path of pure DOS calculation: "%s"\n' % dos_path)
print('Path of defects calculation: "%s"\n' % input_path)

system_name = os.path.basename(os.path.dirname(input_path))

path_to_pure_locpot = os.path.join(path_to_pure_locpot, 'LOCPOT')
structure_pure = Locpot.from_file(path_to_pure_locpot).structure
natoms_sup = len(structure_pure.sites)

vasprun_pure = Vasprun(pure_path + 'vasprun.xml')
vasprun_dos = Vasprun(dos_path + 'vasprun.xml')
(band_gap, cbm, vbm,
 is_band_gap_direct) = vasprun_dos.eigenvalue_band_properties

natoms_pure = len(Locpot.from_file(path_to_pure_locpot).structure.sites)
Epure = ((vasprun_pure.final_energy) / natoms_pure) * natoms_sup

list_dir_vacancies = glob(input_path + '*/')
# saving initial corrections dictionary
corrections_init = corrections.copy()
# list with defects
defect_list = []
Example #24
0
def electrostatic_potential(lattice_vector,
                            filename='./LOCPOT',
                            axis=2,
                            make_csv=True,
                            csv_fname='potential.csv',
                            plt_fname='potential.png',
                            dpi=300,
                            **kwargs):
    """
    Reads LOCPOT to get the planar and macroscopic potential in specified direction

    Args:
        lattice_vector (float): the periodicity of the slab
        filename (str): path to your locpot file, default='./LOCPOT'
        axis (int): direction in which the potential is investigated; a=0, b=1,
        c=2; default=2
        make_csv (bool): makes a csv file with planar and macroscopic potential,
        default=True
        csv_fname (str): filename of the csv file, default='potential.csv'
        plt_fname (str): filename of the plot of potentials, controls the format,
        default='potential.png'
        dpi (int): dots per inch; default=300

    Returns:
        csv file and plot of planar and macroscopic potential
    """
    # Read potential and structure data
    lpt = Locpot.from_file(filename)
    struc = Structure.from_file(filename)

    # Planar potential
    planar = lpt.get_average_along_axis(axis)

    # Divide lattice parameter by no. of grid points in the direction
    resolution = struc.lattice.abc[axis] / lpt.dim[axis]

    # Get number of points over which the rolling average is evaluated
    points = int(lattice_vector / resolution)

    # Need extra points at the start and end of planar potential to evaluate the
    # macroscopic potential this makes use of the PBC where the end of one unit
    # cell coincides with start of the next one
    add_to_start = planar[(len(planar) - points):]
    add_to_end = planar[0:points]
    pfm_data = np.concatenate((add_to_start, planar, add_to_end))
    pfm = pd.DataFrame(data=pfm_data, columns=['y'])

    # Macroscopic potential
    m_data = pfm.y.rolling(window=points, center=True).mean()
    macroscopic = m_data.iloc[points:(len(planar) + points)]
    macroscopic.reset_index(drop=True, inplace=True)

    # Make csv
    if make_csv:
        data = pd.DataFrame(data=planar, columns=['planar'])
        data['macroscopic'] = macroscopic
        data.to_csv(csv_fname, header=True, index=False)

    # Plot both planar and macroscopic, save figure
    fig, ax = plt.subplots()
    ax.plot(planar, label='planar')
    ax.plot(macroscopic, label='macroscopic')
    ax.legend()
    plt.ylabel('Potential / eV')
    plt.savefig(plt_fname, dpi=dpi, **kwargs)
Example #25
0
    def __init__(self,
                 dielectric_tensor,
                 q,
                 gamma,
                 g_sum,
                 bulk_structure,
                 defect_structure,
                 energy_cutoff=520,
                 madetol=0.0001,
                 lengths=None,
                 **kw):
        """
        Args:
            dielectric_tensor: 
                Macroscopic dielectric tensor
                Include ionic also if defect is relaxed, othewise ion clamped.
                Can be a matrix array or scalar.
            q: 
                Charge associated with the defect. Typically integer
            gamma:  
                Convergence parameter. Obtained from KumagaiBulkPart
            g_sum: 
                value that is dependent on the Bulk only. 
                Obtained from KumagaiBulkPart
            bulk_structure: 
                bulk Pymatgen structure object. Need to specify this if 
                using Outcar method for atomic site avg.
                (If you specify outcar files for bulk_file_path but dont 
                specify structure then code will break)
                (TO DO: resolve this dumb dependency by being smarter 
                about where structure comes from?)
            defect_structure: 
                defect structure. Needed if using Outcar method
            energy_cutoff: 
                Energy for plane wave cutoff (in eV).
                If not given, Materials Project default 520 eV is used.
            madetol: 
                Tolerance for convergence of energy terms in eV 
            lengths:
                Lengths of axes, for speeding up plotting slightly
            keywords:
                1) bulk_locpot: Bulk Locpot file path OR Bulk Locpot 
                   defect_locpot: Defect Locpot file path or defect Locpot 
                2) (Or) bulk_outcar:   Bulk Outcar file path 
                   defect_outcar: Defect outcar file path
                3) defect_position: Defect position as a pymatgen Site object in the bulk supercell structure
                    NOTE: this is optional but recommended, if not provided then analysis is done to find
                    the defect position; this analysis has been rigorously tested, but has broken in an example with
                    severe long range relaxation
                    (at which point you probably should not be including the defect in your analysis...)
        """
        if isinstance(dielectric_tensor, int) or \
                isinstance(dielectric_tensor, float):
            self.dieltens = np.identity(3) * dielectric_tensor
        else:
            self.dieltens = np.array(dielectric_tensor)

        if 'bulk_locpot' in kw:
            if isinstance(kw['bulk_locpot'], Locpot):
                self.locpot_blk = kw['bulk_locpot']
            else:
                self.locpot_blk = Locpot.from_file(kw['bulk_locpot'])
            if isinstance(kw['defect_locpot'], Locpot):
                self.locpot_def = kw['defect_locpot']
            else:
                self.locpot_def = Locpot.from_file(kw['defect_locpot'])
            self.dim = self.locpot_blk.dim

            self.outcar_blk = None
            self.outcar_def = None
            self.do_outcar_method = False

        if 'bulk_outcar' in kw:
            self.outcar_blk = Outcar(str(kw['bulk_outcar']))
            self.outcar_def = Outcar(str(kw['defect_outcar']))
            self.do_outcar_method = True
            self.locpot_blk = None
            self.locpot_def = None
            self.dim = self.outcar_blk.ngf

        if 'defect_position' in kw:
            self._defpos = kw['defect_position']
        else:
            self._defpos = None

        self.madetol = madetol
        self.q = q
        self.encut = energy_cutoff
        self.structure = bulk_structure
        self.defstructure = defect_structure
        self.gamma = gamma
        self.g_sum = g_sum

        self.lengths = lengths
Example #26
0
def electrostatic_potential(locpot='./LOCPOT',
                            lattice_vector=None,
                            save_csv=True,
                            csv_fname='potential.csv',
                            save_plt=True,
                            plt_fname='potential.png',
                            **kwargs):
    """
    Reads LOCPOT to get the planar and optionally macroscopic potential in 
    c direction. 

    Args:
        locpot (`str`, optional): The path to the LOCPOT file. Defaults to 
            ``'./LOCPOT'``
        lattice_vector (`float`, optional): The periodicity of the slab, 
            calculates macroscopic potential with that periodicity 
        save_csv (`bool`, optional): Saves to csv. Defaults to ``True``.
        csv_fname (`str`, optional): Filename of the csv file. Defaults 
            to ``'potential.csv'``.
        save_plt (`bool`, optional): Make and save the plot of electrostatic 
            potential. Defaults to ``True``. 
        plt_fname (`str`, optional): Filename of the plot. Defaults to 
            ``'potential.png'``.

    Returns:
        DataFrame
    """
    # Read potential and structure data
    lpt = Locpot.from_file(locpot)
    struc = Structure.from_file(locpot)

    # Planar potential
    planar = lpt.get_average_along_axis(2)
    df = pd.DataFrame(data=planar, columns=['planar'])

    # Calculate macroscopic potential
    if lattice_vector is not None:
        # Divide lattice parameter by no. of grid points in the direction
        resolution = struc.lattice.abc[2] / lpt.dim[2]

        # Get number of points over which the rolling average is evaluated
        points = int(lattice_vector / resolution)

        # Need extra points at the start and end of planar potential to evaluate the
        # macroscopic potential this makes use of the PBC where the end of one unit
        # cell coincides with start of the next one
        add_to_start = planar[(len(planar) - points):]
        add_to_end = planar[0:points]
        pfm_data = np.concatenate((add_to_start, planar, add_to_end))
        pfm = pd.DataFrame(data=pfm_data, columns=['y'])

        # Macroscopic potential
        m_data = pfm.y.rolling(window=points, center=True).mean()
        macroscopic = m_data.iloc[points:(len(planar) + points)]
        macroscopic.reset_index(drop=True, inplace=True)
        df['macroscopic'] = macroscopic

    # Get gradient of the plot - this is used for convergence testing, to make
    # sure the potential is actually flat
    df['gradient'] = np.gradient(df['planar'])

    # Plot and save the graph, save the csv or return the dataframe
    if save_plt:
        plot_electrostatic_potential(df=df, plt_fname=plt_fname, **kwargs)
    if save_csv:
        if not csv_fname.endswith('.csv'):
            csv_fname += '.csv'
        df.to_csv(csv_fname, header=True, index=False)
    else:
        return df
Example #27
0
    def pc(self, struct=None):
        """
        Peform Electrostatic Correction
        note this ony needs structural info
        so struct input object speeds this calculation up
        equivalently fast if input Locpot is a locpot object
        """
        logger = logging.getLogger(__name__)
        if type(struct) is Structure:
            s1 = struct
        else:
            if not type(self._purelocpot) is Locpot:
                logging.info('load Pure locpot')
                self._purelocpot = Locpot.from_file(self._purelocpot)
            s1 = self._purelocpot.structure

        ap = s1.lattice.get_cartesian_coords(1)
        logger.info('Running Freysoldt 2011 PC calculation (should be '\
                     'equivalent to sxdefectalign)')
        logger.debug('defect lattice constants are (in angstroms)' \
                      + str(cleanlat(ap)))
        [a1, a2, a3] = ang_to_bohr * ap
        logging.debug( 'In atomic units, lat consts are (in bohr):' \
                      + str(cleanlat([a1, a2, a3])))
        vol = np.dot(a1, np.cross(a2, a3))  #vol in bohr^3

        #compute isolated energy
        step = 1e-4
        encut1 = 20  #converge to some smaller encut first [eV]
        flag = 0
        converge = []
        while (flag != 1):
            eiso = 1.
            gcut = eV_to_k(encut1)  #gcut is in units of 1/A
            g = step  #initalize
            while g < (gcut + step):
                #simpson integration
                eiso += 4 * (self._q_model.rho_rec(g * g)**2)
                eiso += 2 * (self._q_model.rho_rec((g + step)**2)**2)
                g += 2 * step
            eiso -= self._q_model.rho_rec(gcut**2)**2
            eiso *= (self._q**2) * step / (3 * round(np.pi, 6))
            converge.append(eiso)
            if len(converge) > 2:
                if abs(converge[-1] - converge[-2]) < self._madetol:
                    flag = 1
                elif encut1 > self._encut:
                    logger.error('Eiso did not converge before ' \
                                  + str(self._encut) + ' eV')
                    raise
            encut1 += 20
        eiso = converge[-1]
        logger.debug('Eisolated : %f, converged at encut: %d', round(eiso, 5),
                     encut1 - 20)

        #compute periodic energy;
        encut1 = 20  #converge to some smaller encut
        flag = 0
        converge = []
        while flag != 1:
            eper = 0.0
            for g2 in generate_reciprocal_vectors_squared(a1, a2, a3, encut1):
                eper += (self._q_model.rho_rec(g2)**2) / g2
            eper *= (self._q**2) * 2 * round(np.pi, 6) / vol
            eper += (self._q**2) *4* round(np.pi, 6) \
                    * self._q_model.rho_rec_limit0() / vol
            converge.append(eper)
            if len(converge) > 2:
                if abs(converge[-1] - converge[-2]) < self._madetol:
                    flag = 1
                elif encut1 > self._encut:
                    logger.error('Eper did not converge before %d eV',
                                 self._encut)
                    return
            encut1 += 20
        eper = converge[-1]

        logger.info('Eperiodic : %f hartree, converged at encut %d eV',
                    round(eper, 5), encut1 - 20)
        logger.info('difference (periodic-iso) is %f hartree',
                    round(eper - eiso, 6))
        logger.info('difference in (eV) is %f',
                    round((eper - eiso) * hart_to_ev, 4))

        PCfreycorr = round((eiso - eper) / self._dielectricconst * hart_to_ev,
                           6)
        logger.info('Defect Correction without alignment %f (eV): ',
                    PCfreycorr)

        return PCfreycorr
Example #28
0
                  CONTACR and LOCPOT files.")

(options, args) = parser.parse_args()

v = Vasprun('vasprun.xml')
cdos = v.complete_dos
element_dos = cdos.get_element_dos()
plotter = DosPlotter()
efermi = v.efermi

if options.verbose:
    from pymatgen.core import Element
    from pymatgen.io.vasp.outputs import Locpot, Poscar
    from pymatgen.analysis.surface_analysis import WorkFunctionAnalyzer

    l = Locpot.from_file('LOCPOT')
    s = Poscar.from_file('CONTCAR')

    wf = WorkFunctionAnalyzer(s.structure,
                              l.get_average_along_axis(1),
                              efermi,
                              shift=0)
    loc_vac = wf.vacuum_locpot

    for i in element_dos:
        element_dos[i].efermi = loc_vac

    plotter.add_dos_dict(element_dos)
    plt = plotter.get_plot(xlim=[-9, 1])
    plt.plot([efermi - loc_vac, efermi - loc_vac],
             plt.ylim(),
Example #29
0
    def potalign(self,
                 title=None,
                 widthsample=1.0,
                 axis=None,
                 output_sr=False):
        """
        For performing planar averaging potential alignment

        Accounts for defects in arbitrary positions
        title is for name of plot, if you dont want a plot then leave it as None
        widthsample is the width of the region in between defects where the potential alignment correction is averaged
        axis allows you to override the axis setting of class
                (good for quickly plotting multiple axes without having to reload Locpot)
        output_sr allows for output of the short range potential in the middle (sampled) region.
                (Good for delocalization analysis)
        """
        logger = logging.getLogger(__name__)
        if axis is None:
            axis = self._axis
        else:
            axis = axis

        if not type(self._purelocpot) is Locpot:
            logger.debug('load pure locpot object')
            self._purelocpot = Locpot.from_file(self._purelocpot)
        if not type(self._deflocpot) is Locpot:
            logger.debug('load defect locpot object')
            self._deflocpot = Locpot.from_file(self._deflocpot)

        #determine location of defects
        blksite, defsite = find_defect_pos(self._purelocpot.structure,
                                           self._deflocpot.structure,
                                           defpos=self._defpos)
        if blksite is None and defsite is None:
            logger.error('Not able to determine defect site')
            return

        if blksite is None:
            logger.debug('Found defect to be Interstitial type at %s',
                         repr(defsite))
        elif defsite is None:
            logger.debug('Found defect to be Vacancy type at %s',
                         repr(blksite))
        else:
            logger.debug(
                'Found defect to be antisite/substitution type at '
                '%s in bulk, and %s in defect cell', repr(blksite),
                repr(defsite))

        #It is important to do planar averaging at same position, otherwise
        #you can get rigid shifts due to atomic changes at far away from defect
        #note these are cartesian co-ordinate sites...
        if defsite is None:  #vacancies
            self._pos = blksite
        else:  #all else, do w.r.t defect site
            self._pos = defsite

        x = np.array(self._purelocpot.get_axis_grid(axis))  #angstrom
        nx = len(x)
        logging.debug('run Freysoldt potential alignment method')

        #perform potential alignment part
        pureavg = self._purelocpot.get_average_along_axis(axis)  #eV
        defavg = self._deflocpot.get_average_along_axis(axis)  #eV

        #now shift these planar averages to have defect at origin
        blklat = self._purelocpot.structure.lattice
        axfracval = blklat.get_fractional_coords(self._pos)[axis]
        axbulkval = axfracval * blklat.abc[axis]
        if axbulkval < 0:
            axbulkval += blklat.abc[axis]
        elif axbulkval > blklat.abc[axis]:
            axbulkval -= blklat.abc[axis]

        if axbulkval:
            for i in range(len(x)):
                if axbulkval < x[i]:
                    break
            rollind = len(x) - i
            pureavg = np.roll(pureavg, rollind)
            defavg = np.roll(defavg, rollind)

        #if not self._silence:
        logger.debug('calculating lr part along planar avg axis')
        latt = self._purelocpot.structure.lattice
        reci_latt = latt.reciprocal_lattice
        dg = reci_latt.abc[axis]
        dg /= ang_to_bohr  #convert to bohr to do calculation in atomic units

        v_G = np.empty(len(x), np.dtype('c16'))
        epsilon = self._dielectricconst
        # q needs to be that of the back ground
        v_G[0] = 4 * np.pi * -self._q / epsilon * self._q_model.rho_rec_limit0(
        )
        for i in range(1, nx):
            if (2 * i < nx):
                g = i * dg
            else:
                g = (i - nx) * dg
            g2 = g * g
            v_G[i] = 4 * np.pi / (epsilon *
                                  g2) * -self._q * self._q_model.rho_rec(g2)
        if not (nx % 2):
            v_G[nx // 2] = 0
        v_R = np.fft.fft(v_G)
        v_R_imag = np.imag(v_R)
        v_R /= (latt.volume * ang_to_bohr**3)
        v_R = np.real(v_R) * hart_to_ev

        max_imag_vr = v_R_imag.max()
        if abs(max_imag_vr) > self._madetol:
            logging.error('imaginary part found to be %s', repr(max_imag_vr))
            sys.exit()

        #now get correction and do plots
        short = (defavg - pureavg - v_R)
        checkdis = int((widthsample / 2) / (x[1] - x[0]))
        mid = int(len(short) / 2)

        tmppot = [short[i] for i in range(mid - checkdis, mid + checkdis)]
        logger.debug('shifted defect position on axis (%s) to origin',
                     repr(axbulkval))
        logger.debug('means sampling region is (%f,%f)', x[mid - checkdis],
                     x[mid + checkdis])

        C = -np.mean(tmppot)
        logger.debug('C = %f', C)
        final_shift = [short[j] + C for j in range(len(v_R))]
        v_R = [elmnt - C for elmnt in v_R]

        logger.info('C value is averaged to be %f eV ', C)
        logger.info('Potentital alignment (-q*delta V):  %f (eV)',
                    -self._q * C)

        if title:  # TODO: Make title  optional and use a flag for plotting
            plotter = FreysoldtCorrPlotter(
                x, v_R, defavg - pureavg, final_shift,
                np.array([mid - checkdis, mid + checkdis]))

            if title != 'written':
                plotter.plot(title=title)
            else:
                # TODO: Make this default fname more defect specific so it doesnt
                # over write previous defect data written
                fname = 'FreyAxisData'  # Extension is npz
                plotter.to_datafile(fname)

        if output_sr:
            return ((-self._q * C), tmppot)  #pot align energy correction (eV)
        else:
            return (-self._q * C)  #pot align energy correction (eV)
Example #30
0
def plot_band_alignments(directories, run_type="PBE", fmt="pdf"):
    """
    Plot CBM's and VBM's of all compounds together, relative to the band
    edges of H2O.

    Args:
        directories (list): list of the directory paths for materials
            to include in the plot.
        run_type (str): 'PBE' or 'HSE', so that the function knows which
            subdirectory to go into (pbe_bands or hse_bands).
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    if run_type == "HSE":
        subdirectory = "hse_bands"
    else:
        subdirectory = "pbe_bands"

    band_gaps = {}

    for directory in directories:
        if is_converged("{}/{}".format(directory, subdirectory)):
            os.chdir("{}/{}".format(directory, subdirectory))
            band_structure = Vasprun("vasprun.xml").get_band_structure()
            band_gap = band_structure.get_band_gap()

            # Vacuum level energy from LOCPOT.
            locpot = Locpot.from_file("LOCPOT")
            evac = max(locpot.get_average_along_axis(2))

            try:
                is_metal = False
                is_direct = band_gap["direct"]
                cbm = band_structure.get_cbm()
                vbm = band_structure.get_vbm()

            except AttributeError:
                cbm = None
                vbm = None
                is_metal = True
                is_direct = False

            band_gaps[directory] = {"CBM": cbm, "VBM": vbm, "Direct": is_direct, "Metal": is_metal, "E_vac": evac}

            os.chdir("../../")

    ax = plt.figure(figsize=(16, 10)).gca()

    x_max = len(band_gaps) * 1.315
    ax.set_xlim(0, x_max)

    # Rectangle representing band edges of water.
    ax.add_patch(plt.Rectangle((0, -5.67), height=1.23, width=len(band_gaps), facecolor="#00cc99", linewidth=0))
    ax.text(len(band_gaps) * 1.01, -4.44, r"$\mathrm{H+/H_2}$", size=20, verticalalignment="center")
    ax.text(len(band_gaps) * 1.01, -5.67, r"$\mathrm{O_2/H_2O}$", size=20, verticalalignment="center")

    x_ticklabels = []

    y_min = -8

    i = 0

    # Nothing but lies.
    are_directs, are_indirects, are_metals = False, False, False

    for compound in [cpd for cpd in directories if cpd in band_gaps]:
        x_ticklabels.append(compound)

        # Plot all energies relative to their vacuum level.
        evac = band_gaps[compound]["E_vac"]
        if band_gaps[compound]["Metal"]:
            cbm = -8
            vbm = -2
        else:
            cbm = band_gaps[compound]["CBM"]["energy"] - evac
            vbm = band_gaps[compound]["VBM"]["energy"] - evac

        # Add a box around direct gap compounds to distinguish them.
        if band_gaps[compound]["Direct"]:
            are_directs = True
            linewidth = 5
        elif not band_gaps[compound]["Metal"]:
            are_indirects = True
            linewidth = 0

        # Metals are grey.
        if band_gaps[compound]["Metal"]:
            are_metals = True
            linewidth = 0
            color_code = "#404040"
        else:
            color_code = "#002b80"

        # CBM
        ax.add_patch(
            plt.Rectangle(
                (i, cbm), height=-cbm, width=0.8, facecolor=color_code, linewidth=linewidth, edgecolor="#e68a00"
            )
        )
        # VBM
        ax.add_patch(
            plt.Rectangle(
                (i, y_min),
                height=(vbm - y_min),
                width=0.8,
                facecolor=color_code,
                linewidth=linewidth,
                edgecolor="#e68a00",
            )
        )

        i += 1

    ax.set_ylim(y_min, -2)

    # Set tick labels
    ax.set_xticks([n + 0.4 for n in range(i)])
    ax.set_xticklabels(x_ticklabels, family="serif", size=20, rotation=60)
    ax.set_yticklabels(ax.get_yticks(), family="serif", size=20)

    # Add a legend
    height = y_min
    if are_directs:
        ax.add_patch(
            plt.Rectangle(
                (i * 1.165, height),
                width=i * 0.15,
                height=(-y_min * 0.1),
                facecolor="#002b80",
                edgecolor="#e68a00",
                linewidth=5,
            )
        )
        ax.text(
            i * 1.24,
            height - y_min * 0.05,
            "Direct",
            family="serif",
            color="w",
            size=20,
            horizontalalignment="center",
            verticalalignment="center",
        )
        height -= y_min * 0.15

    if are_indirects:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height), width=i * 0.15, height=(-y_min * 0.1), facecolor="#002b80", linewidth=0)
        )
        ax.text(
            i * 1.24,
            height - y_min * 0.05,
            "Indirect",
            family="serif",
            size=20,
            color="w",
            horizontalalignment="center",
            verticalalignment="center",
        )
        height -= y_min * 0.15

    if are_metals:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height), width=i * 0.15, height=(-y_min * 0.1), facecolor="#404040", linewidth=0)
        )
        ax.text(
            i * 1.24,
            height - y_min * 0.05,
            "Metal",
            family="serif",
            size=20,
            color="w",
            horizontalalignment="center",
            verticalalignment="center",
        )

    # Who needs axes?
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.spines["bottom"].set_visible(False)
    ax.spines["left"].set_visible(False)
    ax.yaxis.set_ticks_position("left")
    ax.xaxis.set_ticks_position("bottom")

    ax.set_ylabel("eV", family="serif", size=24)

    plt.savefig("band_alignments.{}".format(fmt), transparent=True)
    plt.close()
Example #31
0
def plot_band_alignments(directories, run_type='PBE', fmt='pdf'):
    """
    Plot CBM's and VBM's of all compounds together, relative to the band
    edges of H2O.

    Args:
        directories (list): list of the directory paths for materials
            to include in the plot.
        run_type (str): 'PBE' or 'HSE', so that the function knows which
            subdirectory to go into (pbe_bands or hse_bands).
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    if run_type == 'HSE':
        subdirectory = 'hse_bands'
    else:
        subdirectory = 'pbe_bands'

    band_gaps = {}

    for directory in directories:
        sub_dir = os.path.join(directory, subdirectory)
        if is_converged(sub_dir):
            os.chdir(sub_dir)
            band_structure = Vasprun('vasprun.xml').get_band_structure()
            band_gap = band_structure.get_band_gap()

            # Vacuum level energy from LOCPOT.
            locpot = Locpot.from_file('LOCPOT')
            evac = max(locpot.get_average_along_axis(2))

            if not band_structure.is_metal():
                is_direct = band_gap['direct']
                cbm = band_structure.get_cbm()
                vbm = band_structure.get_vbm()

            else:
                cbm = None
                vbm = None
                is_direct = False

            band_gaps[directory] = {'CBM': cbm, 'VBM': vbm,
                                    'Direct': is_direct,
                                    'Metal': band_structure.is_metal(),
                                    'E_vac': evac}

            os.chdir('../../')

    ax = plt.figure(figsize=(16, 10)).gca()

    x_max = len(band_gaps) * 1.315
    ax.set_xlim(0, x_max)

    # Rectangle representing band edges of water.
    ax.add_patch(plt.Rectangle((0, -5.67), height=1.23, width=len(band_gaps),
                 facecolor='#00cc99', linewidth=0))

    ax.text(len(band_gaps) * 1.01, -4.44, r'$\mathrm{H+/H_2}$', size=20,
            verticalalignment='center')
    ax.text(len(band_gaps) * 1.01, -5.67, r'$\mathrm{O_2/H_2O}$', size=20,
            verticalalignment='center')

    x_ticklabels = []

    y_min = -8

    i = 0

    # Nothing but lies.
    are_directs, are_indirects, are_metals = False, False, False

    for compound in [cpd for cpd in directories if cpd in band_gaps]:
        x_ticklabels.append(compound)

        # Plot all energies relative to their vacuum level.
        evac = band_gaps[compound]['E_vac']
        if band_gaps[compound]['Metal']:
            cbm = -8
            vbm = -2
        else:
            cbm = band_gaps[compound]['CBM']['energy'] - evac
            vbm = band_gaps[compound]['VBM']['energy'] - evac

        # Add a box around direct gap compounds to distinguish them.
        if band_gaps[compound]['Direct']:
            are_directs = True
            linewidth = 5
        elif not band_gaps[compound]['Metal']:
            are_indirects = True
            linewidth = 0

        # Metals are grey.
        if band_gaps[compound]['Metal']:
            are_metals = True
            linewidth = 0
            color_code = '#404040'
        else:
            color_code = '#002b80'

        # CBM
        ax.add_patch(plt.Rectangle((i, cbm), height=-cbm, width=0.8,
                                   facecolor=color_code, linewidth=linewidth,
                                   edgecolor="#e68a00"))
        # VBM
        ax.add_patch(plt.Rectangle((i, y_min),
                                   height=(vbm - y_min), width=0.8,
                                   facecolor=color_code, linewidth=linewidth,
                                   edgecolor="#e68a00"))

        i += 1

    ax.set_ylim(y_min, 0)

    # Set tick labels
    ax.set_xticks([n + 0.4 for n in range(i)])
    ax.set_xticklabels(x_ticklabels, family='serif', size=20, rotation=60)
    ax.set_yticklabels(ax.get_yticks(), family='serif', size=20)

    # Add a legend
    height = y_min
    if are_directs:
        ax.add_patch(plt.Rectangle((i*1.165, height), width=i*0.15,
                                   height=(-y_min*0.1), facecolor='#002b80',
                                   edgecolor='#e68a00', linewidth=5))
        ax.text(i*1.24, height - y_min * 0.05, 'Direct', family='serif',
                color='w', size=20, horizontalalignment='center',
                verticalalignment='center')
        height -= y_min * 0.15

    if are_indirects:
        ax.add_patch(plt.Rectangle((i*1.165, height), width=i*0.15,
                                   height=(-y_min*0.1), facecolor='#002b80',
                                   linewidth=0))
        ax.text(i*1.24, height - y_min * 0.05, 'Indirect', family='serif',
                size=20, color='w', horizontalalignment='center',
                verticalalignment='center')
        height -= y_min * 0.15

    if are_metals:
        ax.add_patch(plt.Rectangle((i*1.165, height), width=i*0.15,
                                   height=(-y_min*0.1), facecolor='#404040',
                                   linewidth=0))
        ax.text(i*1.24, height - y_min * 0.05, 'Metal', family='serif',
                size=20, color='w', horizontalalignment='center',
                verticalalignment='center')

    # Who needs axes?
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.yaxis.set_ticks_position('left')
    ax.xaxis.set_ticks_position('bottom')

    ax.set_ylabel('eV', family='serif', size=24)

    if fmt == "None":
        return ax
    else:
        plt.savefig('band_alignments.{}'.format(fmt), transparent=True)
    plt.close()
Example #32
0
def plot_band_alignments(directories, run_type='PBE', fmt='pdf'):
    """
    Plot CBM's and VBM's of all compounds together, relative to the band
    edges of H2O.

    Args:
        directories (list): list of the directory paths for materials
            to include in the plot.
        run_type (str): 'PBE' or 'HSE', so that the function knows which
            subdirectory to go into (pbe_bands or hse_bands).
        fmt (str): matplotlib format style. Check the matplotlib
            docs for options.
    """

    if run_type == 'HSE':
        subdirectory = 'hse_bands'
    else:
        subdirectory = 'pbe_bands'

    band_gaps = {}

    for directory in directories:
        sub_dir = os.path.join(directory, subdirectory)
        if is_converged(sub_dir):
            os.chdir(sub_dir)
            band_structure = Vasprun('vasprun.xml').get_band_structure()
            band_gap = band_structure.get_band_gap()

            # Vacuum level energy from LOCPOT.
            locpot = Locpot.from_file('LOCPOT')
            evac = max(locpot.get_average_along_axis(2))

            if not band_structure.is_metal():
                is_direct = band_gap['direct']
                cbm = band_structure.get_cbm()
                vbm = band_structure.get_vbm()

            else:
                cbm = None
                vbm = None
                is_direct = False

            band_gaps[directory] = {
                'CBM': cbm,
                'VBM': vbm,
                'Direct': is_direct,
                'Metal': band_structure.is_metal(),
                'E_vac': evac
            }

            os.chdir('../../')

    ax = plt.figure(figsize=(16, 10)).gca()

    x_max = len(band_gaps) * 1.315
    ax.set_xlim(0, x_max)

    # Rectangle representing band edges of water.
    ax.add_patch(
        plt.Rectangle((0, -5.67),
                      height=1.23,
                      width=len(band_gaps),
                      facecolor='#00cc99',
                      linewidth=0))

    ax.text(len(band_gaps) * 1.01,
            -4.44,
            r'$\mathrm{H+/H_2}$',
            size=20,
            verticalalignment='center')
    ax.text(len(band_gaps) * 1.01,
            -5.67,
            r'$\mathrm{O_2/H_2O}$',
            size=20,
            verticalalignment='center')

    x_ticklabels = []

    y_min = -8

    i = 0

    # Nothing but lies.
    are_directs, are_indirects, are_metals = False, False, False

    for compound in [cpd for cpd in directories if cpd in band_gaps]:
        x_ticklabels.append(compound)

        # Plot all energies relative to their vacuum level.
        evac = band_gaps[compound]['E_vac']
        if band_gaps[compound]['Metal']:
            cbm = -8
            vbm = -2
        else:
            cbm = band_gaps[compound]['CBM']['energy'] - evac
            vbm = band_gaps[compound]['VBM']['energy'] - evac

        # Add a box around direct gap compounds to distinguish them.
        if band_gaps[compound]['Direct']:
            are_directs = True
            linewidth = 5
        elif not band_gaps[compound]['Metal']:
            are_indirects = True
            linewidth = 0

        # Metals are grey.
        if band_gaps[compound]['Metal']:
            are_metals = True
            linewidth = 0
            color_code = '#404040'
        else:
            color_code = '#002b80'

        # CBM
        ax.add_patch(
            plt.Rectangle((i, cbm),
                          height=-cbm,
                          width=0.8,
                          facecolor=color_code,
                          linewidth=linewidth,
                          edgecolor="#e68a00"))
        # VBM
        ax.add_patch(
            plt.Rectangle((i, y_min),
                          height=(vbm - y_min),
                          width=0.8,
                          facecolor=color_code,
                          linewidth=linewidth,
                          edgecolor="#e68a00"))

        i += 1

    ax.set_ylim(y_min, 0)

    # Set tick labels
    ax.set_xticks([n + 0.4 for n in range(i)])
    ax.set_xticklabels(x_ticklabels, family='serif', size=20, rotation=60)
    ax.set_yticklabels(ax.get_yticks(), family='serif', size=20)

    # Add a legend
    height = y_min
    if are_directs:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height),
                          width=i * 0.15,
                          height=(-y_min * 0.1),
                          facecolor='#002b80',
                          edgecolor='#e68a00',
                          linewidth=5))
        ax.text(i * 1.24,
                height - y_min * 0.05,
                'Direct',
                family='serif',
                color='w',
                size=20,
                horizontalalignment='center',
                verticalalignment='center')
        height -= y_min * 0.15

    if are_indirects:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height),
                          width=i * 0.15,
                          height=(-y_min * 0.1),
                          facecolor='#002b80',
                          linewidth=0))
        ax.text(i * 1.24,
                height - y_min * 0.05,
                'Indirect',
                family='serif',
                size=20,
                color='w',
                horizontalalignment='center',
                verticalalignment='center')
        height -= y_min * 0.15

    if are_metals:
        ax.add_patch(
            plt.Rectangle((i * 1.165, height),
                          width=i * 0.15,
                          height=(-y_min * 0.1),
                          facecolor='#404040',
                          linewidth=0))
        ax.text(i * 1.24,
                height - y_min * 0.05,
                'Metal',
                family='serif',
                size=20,
                color='w',
                horizontalalignment='center',
                verticalalignment='center')

    # Who needs axes?
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.yaxis.set_ticks_position('left')
    ax.xaxis.set_ticks_position('bottom')

    ax.set_ylabel('eV', family='serif', size=24)

    if fmt == "None":
        return ax
    else:
        plt.savefig('band_alignments.{}'.format(fmt), transparent=True)
    plt.close()