Esempio n. 1
0
    def test_process_entry(self):

        # basic process with no corrections
        dentry = DefectEntry(
            self.vac,
            0.0,
            corrections={},
            parameters={"vbm": 0.0, "cbm": 0.0},
            entry_id=None,
        )
        dc = DefectCompatibility()
        dentry = dc.process_entry(dentry)
        self.assertIsNotNone(dentry)

        # process with corrections from parameters used in other unit tests
        params = self.frey_params.copy()
        params.update(self.bandfill_params)
        params.update(
            {
                "hybrid_cbm": params["cbm"] + 0.2,
                "hybrid_vbm": params["vbm"] - 0.4,
            }
        )
        dentry = DefectEntry(self.vac, 0.0, corrections={}, parameters=params, entry_id=None)
        dc = DefectCompatibility()
        dentry = dc.process_entry(dentry)
        self.assertAlmostEqual(dentry.corrections["bandedgeshifting_correction"], 1.2)
        self.assertAlmostEqual(dentry.corrections["bandfilling_correction"], 0.0)
        self.assertAlmostEqual(dentry.corrections["charge_correction"], 5.44595036)

        # test over delocalized free carriers which forces skipping charge correction
        # modify the eigenvalue list to have free holes
        hole_eigenvalues = {}
        for spinkey, spinset in params["eigenvalues"].items():
            hole_eigenvalues[spinkey] = []
            for kptset in spinset:
                hole_eigenvalues[spinkey].append([])
                for eig in kptset:
                    if (eig[0] < params["vbm"]) and (eig[0] > params["vbm"] - 0.8):
                        hole_eigenvalues[spinkey][-1].append([eig[0], 0.5])
                    else:
                        hole_eigenvalues[spinkey][-1].append(eig)

        params.update({"eigenvalues": hole_eigenvalues})
        dentry = DefectEntry(self.vac, 0.0, corrections={}, parameters=params, entry_id=None)
        dc = DefectCompatibility(free_chg_cutoff=0.8)
        dentry = dc.process_entry(dentry)
        self.assertAlmostEqual(dentry.corrections["bandedgeshifting_correction"], 1.19999999)
        self.assertAlmostEqual(dentry.corrections["bandfilling_correction"], -1.62202400)
        self.assertAlmostEqual(dentry.corrections["charge_correction"], 0.0)

        # turn off band filling and band edge shifting
        dc = DefectCompatibility(free_chg_cutoff=0.8, use_bandfilling=False, use_bandedgeshift=False)
        dentry = dc.process_entry(dentry)
        self.assertAlmostEqual(dentry.corrections["bandedgeshifting_correction"], 0.0)
        self.assertAlmostEqual(dentry.corrections["bandfilling_correction"], 0.0)
        self.assertAlmostEqual(dentry.corrections["charge_correction"], 0.0)
    def test_check_kumagai_delocalized(self):
        de = DefectEntry(self.kumagai_vac, 0.0, parameters=self.kumagai_params)
        de.parameters.update(
            {"is_compatible":
             True})  # needs to be initialized with this here for unittest
        dc = DefectCompatibility(atomic_site_var_tol=13.3,
                                 atomic_site_minmax_tol=20.95)
        dentry = dc.perform_kumagai(de)

        # check case which fits under compatibility constraints
        dentry = dc.check_kumagai_delocalized(dentry)
        kumagai_delocal = dentry.parameters["delocalization_meta"][
            "atomic_site"]
        self.assertTrue(kumagai_delocal["is_compatible"])
        kumagai_md = kumagai_delocal["metadata"]
        true_variance = 13.262304401193997
        true_minmax = 20.9435
        self.assertTrue(kumagai_md["kumagai_variance_compatible"])
        self.assertAlmostEqual(kumagai_md["kumagai_variance"], true_variance)
        self.assertTrue(kumagai_md["kumagai_minmax_compatible"])
        self.assertAlmostEqual(kumagai_md["kumagai_minmax_window"],
                               true_minmax)

        self.assertTrue(dentry.parameters["is_compatible"])

        # break variable compatibility
        dc = DefectCompatibility(atomic_site_var_tol=0.1,
                                 atomic_site_minmax_tol=20.95)
        de.parameters.update({"is_compatible": True})
        dentry = dc.perform_kumagai(de)
        dentry = dc.check_kumagai_delocalized(dentry)
        kumagai_delocal = dentry.parameters["delocalization_meta"][
            "atomic_site"]
        self.assertFalse(kumagai_delocal["is_compatible"])
        kumagai_md = kumagai_delocal["metadata"]
        self.assertFalse(kumagai_md["kumagai_variance_compatible"])
        self.assertAlmostEqual(kumagai_md["kumagai_variance"], true_variance)
        self.assertTrue(kumagai_md["kumagai_minmax_compatible"])
        self.assertAlmostEqual(kumagai_md["kumagai_minmax_window"],
                               true_minmax)

        self.assertFalse(dentry.parameters["is_compatible"])

        # break maxmin compatibility
        dc = DefectCompatibility(atomic_site_var_tol=13.3,
                                 atomic_site_minmax_tol=0.5)
        de.parameters.update({"is_compatible": True})
        dentry = dc.perform_kumagai(de)
        dentry = dc.check_kumagai_delocalized(dentry)
        kumagai_delocal = dentry.parameters["delocalization_meta"][
            "atomic_site"]
        self.assertFalse(kumagai_delocal["is_compatible"])
        kumagai_md = kumagai_delocal["metadata"]
        self.assertTrue(kumagai_md["kumagai_variance_compatible"])
        self.assertAlmostEqual(kumagai_md["kumagai_variance"], true_variance)
        self.assertFalse(kumagai_md["kumagai_minmax_compatible"])
        self.assertAlmostEqual(kumagai_md["kumagai_minmax_window"],
                               true_minmax)

        self.assertFalse(dentry.parameters["is_compatible"])
    def test_check_freysoldt_delocalized(self):
        de = DefectEntry(self.vac,
                         0.0,
                         corrections={},
                         parameters=self.frey_params,
                         entry_id=None)
        de.parameters.update(
            {"is_compatible":
             True})  # needs to be initialized with this here for unittest
        dc = DefectCompatibility(plnr_avg_var_tol=0.1, plnr_avg_minmax_tol=0.5)
        dentry = dc.perform_freysoldt(de)

        # check case which fits under compatibility constraints
        dentry = dc.check_freysoldt_delocalized(dentry)
        frey_delocal = dentry.parameters["delocalization_meta"]["plnr_avg"]
        self.assertTrue(frey_delocal["is_compatible"])
        ans_var = [0.00038993, 0.02119532, 0.02119532]
        ans_window = [0.048331509, 0.36797169, 0.36797169]
        for ax in range(3):
            ax_metadata = frey_delocal["metadata"][ax]
            self.assertTrue(ax_metadata["frey_variance_compatible"])
            self.assertAlmostEqual(ax_metadata["frey_variance"], ans_var[ax])
            self.assertTrue(ax_metadata["frey_minmax_compatible"])
            self.assertAlmostEqual(ax_metadata["frey_minmax_window"],
                                   ans_window[ax])

        self.assertTrue(dentry.parameters["is_compatible"])

        # check planar delocalization on 2nd and 3rd axes
        dc = DefectCompatibility(plnr_avg_var_tol=0.1, plnr_avg_minmax_tol=0.2)
        dentry.parameters.update({"is_compatible": True})
        dentry = dc.check_freysoldt_delocalized(dentry)
        frey_delocal = dentry.parameters["delocalization_meta"]["plnr_avg"]
        self.assertFalse(frey_delocal["is_compatible"])
        ax_metadata = frey_delocal["metadata"][0]
        self.assertTrue(ax_metadata["frey_variance_compatible"])
        self.assertTrue(ax_metadata["frey_minmax_compatible"])
        for ax in [1, 2]:
            ax_metadata = frey_delocal["metadata"][ax]
            self.assertTrue(ax_metadata["frey_variance_compatible"])
            self.assertFalse(ax_metadata["frey_minmax_compatible"])

        self.assertFalse(dentry.parameters["is_compatible"])

        # check variance based delocalization on 2nd and 3rd axes
        dc = DefectCompatibility(plnr_avg_var_tol=0.01,
                                 plnr_avg_minmax_tol=0.5)
        dentry.parameters.update({"is_compatible": True})
        dentry = dc.check_freysoldt_delocalized(dentry)
        frey_delocal = dentry.parameters["delocalization_meta"]["plnr_avg"]
        self.assertFalse(frey_delocal["is_compatible"])
        ax_metadata = frey_delocal["metadata"][0]
        self.assertTrue(ax_metadata["frey_variance_compatible"])
        self.assertTrue(ax_metadata["frey_minmax_compatible"])
        for ax in [1, 2]:
            ax_metadata = frey_delocal["metadata"][ax]
            self.assertFalse(ax_metadata["frey_variance_compatible"])
            self.assertTrue(ax_metadata["frey_minmax_compatible"])

        self.assertFalse(dentry.parameters["is_compatible"])
    def test_bandfilling_SOC_calc(self):
        v = Vasprun(os.path.join(PymatgenTest.TEST_FILES_DIR, "vasprun.xml.int_Te_SOC.gz"))
        struc = v.structures[0]
        interstitial = Interstitial(struc, struc.sites[-1], charge=-2)
        eigenvalues = v.eigenvalues.copy()
        kptweights = v.actual_kpoints_weights
        potalign = -0.1
        defect_incar = v.incar

        bandfill_params = {
            "eigenvalues": eigenvalues,
            "kpoint_weights": kptweights,
            "potalign": potalign,
            "vbm": 1.6465,  # bulk VBM
            "cbm": 3.1451,  # bulk CBM
            "run_metadata": {"defect_incar": defect_incar},
        }

        soc_dentry = DefectEntry(
            interstitial,
            0.0,
            corrections={},
            parameters=bandfill_params,
            entry_id=None,
        )
        dc = DefectCompatibility()
        soc_dentry = dc.process_entry(soc_dentry)

        self.assertAlmostEqual(soc_dentry.corrections["bandfilling_correction"], -1.9628402187500003)
Esempio n. 5
0
    def setUpClass(cls):
        cls.vbm_val = 2.6682
        cls.gap = 1.5
        cls.entries = list(loadfn(os.path.join(os.path.dirname(__file__), "GaAs_test_defentries.json")).values())
        for entry in cls.entries:
            entry.parameters.update({"vbm": cls.vbm_val})
        cls.pd = DefectPhaseDiagram(cls.entries, cls.vbm_val, cls.gap)
        cls.mu_elts = {Element("As"): -4.658070555, Element("Ga"): -3.7317319750000006}

        # make Vac_As (q= -2) only defect test single-stable-charge exceptions
        cls.extra_entry = DefectEntry(cls.entries[5].defect.copy(), 100.0)
        sep_entries = [
            ent for ent in cls.entries if not (ent.name == "Vac_As_mult4" and ent.charge in [-2, -1, 0, 1, 2])
        ]
        sep_entries.append(cls.extra_entry.copy())
        cls.sep_pd = DefectPhaseDiagram(sep_entries, cls.vbm_val, cls.gap)

        # make Vac_As (q= -2) is incompatible for larger supercell
        ls_entries = cls.entries[:]
        for entry in ls_entries:
            if entry.name == "Vac_As_mult4" and entry.charge == -2.0:
                entry.parameters["is_compatible"] = False
        cls.pd_ls_fcTrue = DefectPhaseDiagram(ls_entries, cls.vbm_val, cls.gap, filter_compatible=True)
        cls.pd_ls_fcFalse = DefectPhaseDiagram(ls_entries, cls.vbm_val, cls.gap, filter_compatible=False)

        # load complete dos for fermi energy solving
        with open(os.path.join(PymatgenTest.TEST_FILES_DIR, "complete_dos.json")) as f:
            dos_dict = json.load(f)
        cls.dos = CompleteDos.from_dict(dos_dict)
Esempio n. 6
0
    def test_corrections(self):
        entry = DefectEntry(self.substitution, 2.5)

        self.assertAlmostEqual(entry.energy, 2.5)

        entry.corrections["pot_corr"] = -0.3
        self.assertAlmostEqual(entry.energy, 2.2)
    def test_perform_all_corrections(self):

        # return entry even if insufficent values are provided
        # for freysoldt, kumagai, bandfilling, or band edge shifting
        de = DefectEntry(self.vac, 0.0, corrections={}, parameters={}, entry_id=None)
        dc = DefectCompatibility()
        dentry = dc.perform_all_corrections(de)
        self.assertIsNotNone(dentry)
 def test_delocalization_analysis(self):
     # return entry even if insufficent values are provided
     # for delocalization analysis with freysoldt, kumagai,
     # bandfilling, or band edge shifting
     de = DefectEntry(self.vac, 0.0, corrections={}, parameters={}, entry_id=None)
     dc = DefectCompatibility()
     dentry = dc.delocalization_analysis(de)
     self.assertIsNotNone(dentry)
def convert_cd_to_de( cd, b_cse):
    """
    As of pymatgen v2.0, ComputedDefect objects were deprecated in favor
    of DefectEntry objects in pymatgen.analysis.defects.core
    This function takes a ComputedDefect (either as a dict or object) and
    converts it into a DefectEntry object in order to handle legacy
    PyCDT creation within the current paradigm of PyCDT.

    :param cd (dict or ComputedDefect object): ComputedDefect as an object or as a dictionary
    :params b_cse (dict or ComputedStructureEntry object): ComputedStructureEntry of bulk entry
        associated with the ComputedDefect.
    :return: de (DefectEntry): Resulting DefectEntry object
    """
    if type(cd) != dict:
        cd = cd.as_dict()
    if type(b_cse) != dict:
        b_cse = b_cse.as_dict()

    bulk_sc_structure = Structure.from_dict( b_cse["structure"])

    #modify defect_site as required for Defect object, confirming site exists in bulk structure
    site_cls = cd["site"]
    defect_site = PeriodicSite.from_dict( site_cls)
    def_nom = cd["name"].lower()
    if "sub_" in def_nom or "as_" in def_nom:
        #modify site object for substitution site of Defect object
        site_cls["species"][0]["element"] = cd["name"].split("_")[2]
        defect_site = PeriodicSite.from_dict( site_cls)

    poss_deflist = sorted(
        bulk_sc_structure.get_sites_in_sphere(defect_site.coords, 0.1, include_index=True), key=lambda x: x[1])
    if len(poss_deflist) != 1:
        raise ValueError("ComputedDefect to DefectEntry conversion failed. "
                         "Could not determine periodic site position in bulk supercell.")

    # create defect object
    if "vac_" in def_nom:
        defect_obj = Vacancy(bulk_sc_structure, defect_site, charge=cd["charge"])
    elif "as_" in def_nom or "sub_" in def_nom:
        defect_obj = Substitution(bulk_sc_structure, defect_site, charge=cd["charge"])
    elif "int_" in def_nom:
        defect_obj = Interstitial(bulk_sc_structure, defect_site, charge=cd["charge"])
    else:
        raise ValueError("Could not recognize defect type for {}".format( cd["name"]))


    # assign proper energy and parameter metadata
    uncorrected_energy = cd["entry"]["energy"] - b_cse["energy"]
    def_path = os.path.split( cd["entry"]["data"]["locpot_path"])[0]
    bulk_path = os.path.split( b_cse["data"]["locpot_path"])[0]
    p = {"defect_path": def_path,
         "bulk_path": bulk_path,
         "encut": cd["entry"]["data"]["encut"]}

    de = DefectEntry( defect_obj, uncorrected_energy, parameters = p)

    return de
Esempio n. 10
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
    def test_perform_freysoldt(self):
        de = DefectEntry(self.vac, 0.0, corrections={}, parameters=self.frey_params, entry_id=None)
        dc = DefectCompatibility()
        dentry = dc.perform_freysoldt(de)

        val = dentry.parameters["freysoldt_meta"]
        self.assertAlmostEqual(val["freysoldt_electrostatic"], 0.975893)
        self.assertAlmostEqual(val["freysoldt_potential_alignment_correction"], 4.4700574)
        self.assertAlmostEqual(val["freysoldt_potalign"], 1.4900191)
        self.assertTrue("pot_corr_uncertainty_md" in val.keys())
        self.assertTrue("pot_plot_data" in val.keys())
    def test_perform_kumagai(self):
        de = DefectEntry(self.kumagai_vac, 0.0, parameters=self.kumagai_params)
        dc = DefectCompatibility()
        dentry = dc.perform_kumagai(de)

        val = dentry.parameters["kumagai_meta"]
        self.assertAlmostEqual(val["kumagai_electrostatic"], 0.88236299)
        self.assertAlmostEqual(val["kumagai_potential_alignment_correction"], 2.09704862)
        self.assertAlmostEqual(val["kumagai_potalign"], 0.69901620)
        self.assertTrue("pot_corr_uncertainty_md" in val.keys())
        self.assertTrue("pot_plot_data" in val.keys())
Esempio n. 13
0
    def test_defect_concentration(self):
        entry = DefectEntry(self.substitution, .5, corrections={})
        entry.defect._charge = -1

        chem_pots = {"Sr": 0., "V": 0., "O": 0.}
        self.assertAlmostEqual(entry.defect_concentration(chem_pots), 1.2878309944593931e14)

        # #test temperature dependence
        self.assertAlmostEqual(entry.defect_concentration(chem_pots, temperature=600), 2.040208007417593e+18)

        # test fermi level dependence
        self.assertAlmostEqual(entry.defect_concentration(chem_pots, fermi_level=.3), 1.4113592133771723e+19)
Esempio n. 14
0
    def test_bandedgeshifting(self):
        struc = PymatgenTest.get_structure("VO2")
        struc.make_supercell(3)
        struc = struc
        vac = Vacancy(struc, struc.sites[0], charge=-3)

        besc = BandEdgeShiftingCorrection()
        params = {"hybrid_cbm": 1.0, "hybrid_vbm": -1.0, "vbm": -0.5, "cbm": 0.6}
        de = DefectEntry(vac, 0.0, corrections={}, parameters=params, entry_id=None)

        corr = besc.get_correction(de)
        self.assertEqual(corr["bandedgeshifting_correction"], 1.5)
Esempio n. 15
0
    def test_run_band_edge_shifting(self):
        de = DefectEntry(self.vac,
                         0.,
                         corrections={},
                         parameters=self.band_edge_params,
                         entry_id=None)

        dc = DefectCompatibility()
        dentry = dc.perform_band_edge_shifting(de)
        val = dentry.parameters['bandshift_meta']
        self.assertEqual(val['vbmshift'], -0.5)
        self.assertEqual(val['cbmshift'], 0.4)
        self.assertEqual(val['bandedgeshifting_correction'], 1.5)
Esempio n. 16
0
    def test_run_bandfilling(self):
        de = DefectEntry(self.vac,
                         0.,
                         corrections={},
                         parameters=self.bandfill_params,
                         entry_id=None)
        dc = DefectCompatibility()
        dentry = dc.perform_bandfilling(de)

        val = dentry.parameters['bandfilling_meta']
        self.assertAlmostEqual(val['num_hole_vbm'], 0.)
        self.assertAlmostEqual(val['num_elec_cbm'], 0.)
        self.assertAlmostEqual(val['bandfilling_correction'], 0.)
Esempio n. 17
0
    def setUp(self):

        self.epsilon = 15.

        struc = PymatgenTest.get_structure("VO2")
        struc.make_supercell(3)
        vac = Vacancy(struc, struc.sites[0], charge=-3)

        # load neccessary parameters for defect_entry to make use
        # of Freysoldt and Kumagai corrections
        p = {}
        ids = vac.generate_defect_structure(1)
        abc = struc.lattice.abc
        axisdata = [np.arange(0., lattval, 0.2) for lattval in abc]
        bldata = [np.array([1. for u in np.arange(0., lattval, 0.2)]) for lattval in abc]
        dldata = [
            np.array([(-1 - np.cos(2 * np.pi * u / lattval)) for u in np.arange(0., lattval, 0.2)]) for lattval in abc
        ]
        p.update({'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata,
                  'initial_defect_structure': ids, 'defect_frac_sc_coords': struc.sites[0].frac_coords,
                  'bulk_sc_structure': struc})

        bulk_atomic_site_averages, defect_atomic_site_averages = [], []
        defect_site_with_sc_lattice = PeriodicSite( struc.sites[0].specie, struc.sites[0].coords,
                                                    struc.lattice, coords_are_cartesian = True)
        max_dist = 9.6
        pert_amnt = 1.
        for site_ind, site in enumerate(struc.sites):
            if site.specie.symbol == "O":
                Oval = -30.6825
                bulk_atomic_site_averages.append( Oval)
                if site_ind:
                    dist_to_defect = site.distance_and_image( defect_site_with_sc_lattice)[0]
                    defect_site_val = Oval - .3 + pert_amnt * ((max_dist - dist_to_defect)/max_dist)**2
                    defect_atomic_site_averages.append( defect_site_val)
            else:
                Vval = -51.6833
                bulk_atomic_site_averages.append( Vval)
                if site_ind:
                    dist_to_defect = site.distance_and_image( defect_site_with_sc_lattice)[0]
                    defect_site_val = Vval - .3 + pert_amnt * ((max_dist - dist_to_defect)/max_dist)**2
                    defect_atomic_site_averages.append( defect_site_val)

        site_matching_indices = [[ind, ind-1] for ind in range(len(struc.sites)) if ind != 0]

        p.update({ "bulk_atomic_site_averages": bulk_atomic_site_averages,
                   "defect_atomic_site_averages": defect_atomic_site_averages,
                   "site_matching_indices": site_matching_indices})
        self.defect_entry = DefectEntry( vac, 0., parameters = p)
Esempio n. 18
0
    def setUp(self):
        l = Lattice([[3.52,0.0,2.033], [1.174,3.32,2.033], \
                [0.0,0.0,4.066]])
        s_bulk = Structure(l, ['Ga', 'As'], \
                [[0.0000, 0.0000, 0.0000], \
                [0.2500, 0.2500, 0.2500]])
        defect_site = PeriodicSite('As', [0.25, 0.25, 0.25], l)
        defect = Vacancy(s_bulk, defect_site, charge=1.)
        defect_entry = DefectEntry(defect, 0.)

        entries = [defect_entry]
        vbm = 0.2
        band_gap = 1.
        dpd = DefectPhaseDiagram(entries, vbm, band_gap)
        self.dp = DefectPlotter(dpd)
Esempio n. 19
0
    def test_formation_energy(self):
        entry = DefectEntry(self.substitution, 2.5, corrections={"pot_corr": -0.3})

        # Test chemical potentials on formation energy
        self.assertAlmostEqual(entry.formation_energy(), 2.2)
        self.assertAlmostEqual(entry.formation_energy({"Sr": 0.2}), 2.0)
        self.assertAlmostEqual(entry.formation_energy({"V": 0.2}), 2.4)
        self.assertAlmostEqual(entry.formation_energy({"Sr": 0.2, "V": 0.2}), 2.2)
        self.assertAlmostEqual(entry.formation_energy({"Sr": 0.2, "V": 0.2, "O": 2}), 2.2)

        # Test Fermi level on formation energy
        self.assertAlmostEqual(entry.formation_energy({"Sr": 0.2, "V": 0.2}, fermi_level=0.2), 2.2)
        entry.parameters["vbm"] = 0
        self.assertAlmostEqual(entry.formation_energy({"Sr": 0.2, "V": 0.2}, fermi_level=0.2), 2.2)
        entry.defect._charge = 1
        self.assertAlmostEqual(entry.formation_energy({"Sr": 0.2, "V": 0.2}, fermi_level=0.2), 2.4)
Esempio n. 20
0
    def test_defect_concentration(self):
        entry = DefectEntry(self.substitution, 0.5, corrections={})
        entry.defect._charge = -1

        chem_pots = {"Sr": 0.0, "V": 0.0, "O": 0.0}
        self.assertAlmostEqual(entry.defect_concentration(chem_pots) / 1.2878334860092098e14, 1)

        # #test temperature dependence
        self.assertAlmostEqual(
            entry.defect_concentration(chem_pots, temperature=600) / 2.0402099809985405e18,
            1,
        )

        # test fermi level dependence
        self.assertAlmostEqual(
            entry.defect_concentration(chem_pots, fermi_level=0.3) / 1.411360305591838e19,
            1,
        )
    def setUp(self):
        self.vbm_val = 2.6682
        self.gap = 1.5
        self.entries = list(
            loadfn(
                os.path.join(os.path.dirname(__file__),
                             "GaAs_test_defentries.json")).values())
        for entry in self.entries:
            entry.parameters.update({'vbm': self.vbm_val})
        self.pd = DefectPhaseDiagram(self.entries, self.vbm_val, self.gap)
        self.mu_elts = {
            Element("As"): -4.658070555,
            Element("Ga"): -3.7317319750000006
        }

        # make Vac_As (q= -2) only defect test single-stable-charge exceptions
        self.extra_entry = DefectEntry(self.entries[5].defect.copy(), 100.)
        sep_entries = [
            ent for ent in self.entries if not (
                ent.name == 'Vac_As_mult4' and ent.charge in [-2, -1, 0, 1, 2])
        ]
        sep_entries.append(self.extra_entry.copy())
        self.sep_pd = DefectPhaseDiagram(sep_entries, self.vbm_val, self.gap)

        # make Vac_As (q= -2) is incompatible for larger supercell
        ls_entries = self.entries[:]
        for entry in ls_entries:
            if entry.name == 'Vac_As_mult4' and entry.charge == -2.:
                entry.parameters['is_compatible'] = False
        self.pd_ls_fcTrue = DefectPhaseDiagram(ls_entries,
                                               self.vbm_val,
                                               self.gap,
                                               filter_compatible=True)
        self.pd_ls_fcFalse = DefectPhaseDiagram(ls_entries,
                                                self.vbm_val,
                                                self.gap,
                                                filter_compatible=False)

        # load complete dos for fermi energy solving
        with open(os.path.join(test_dir, "complete_dos.json"), "r") as f:
            dos_dict = json.load(f)
        self.dos = CompleteDos.from_dict(dos_dict)
    def test_run_bandfilling(self):
        de = DefectEntry(self.vac,
                         0.,
                         corrections={},
                         parameters=self.bandfill_params,
                         entry_id=None)
        dc = DefectCompatibility()
        dentry = dc.perform_bandfilling(de)

        val = dentry.parameters['bandfilling_meta']
        self.assertAlmostEqual(val['num_hole_vbm'], 0.)
        self.assertAlmostEqual(val['num_elec_cbm'], 0.)
        self.assertAlmostEqual(val['bandfilling_correction'], 0.)
        occu = [[1.457, 0.0833333], [1.5204, 0.0833333], [1.53465, 0.0833333],
                [1.5498, 0.0416667]]
        self.assertArrayAlmostEqual(
            list(sorted(val['occupied_def_levels'], key=lambda x: x[0])),
            list(sorted(occu, key=lambda x: x[0])))
        self.assertAlmostEqual(val['total_occupation_defect_levels'],
                               0.29166669)
        self.assertFalse(val['unoccupied_def_levels'])
Esempio n. 23
0
    def test_bandedgeshifting(self):
        struc = PymatgenTest.get_structure("VO2")
        struc.make_supercell(3)
        struc = struc
        vac = Vacancy(struc, struc.sites[0], charge=-3)

        besc = BandEdgeShiftingCorrection()
        params = {
            'hybrid_cbm': 1.,
            'hybrid_vbm': -1.,
            'vbm': -0.5,
            'cbm': 0.6,
            'num_hole_vbm': 0.,
            'num_elec_cbm': 0.
        }
        de = DefectEntry(vac,
                         0.,
                         corrections={},
                         parameters=params,
                         entry_id=None)

        #test with no free carriers
        corr = besc.get_correction(de)
        self.assertEqual(corr['vbm_shift_correction'], 1.5)
        self.assertEqual(corr['elec_cbm_shift_correction'], 0.)
        self.assertEqual(corr['hole_vbm_shift_correction'], 0.)

        #test with free holes
        de.parameters.update({'num_hole_vbm': 1.})
        corr = besc.get_correction(de)
        self.assertEqual(corr['vbm_shift_correction'], 1.5)
        self.assertEqual(corr['elec_cbm_shift_correction'], 0.)
        self.assertEqual(corr['hole_vbm_shift_correction'], 0.5)

        #test with free electrons
        de.parameters.update({'num_hole_vbm': 0., 'num_elec_cbm': 1.})
        corr = besc.get_correction(de)
        self.assertEqual(corr['vbm_shift_correction'], 1.5)
        self.assertEqual(corr['elec_cbm_shift_correction'], 0.4)
        self.assertEqual(corr['hole_vbm_shift_correction'], 0.)
Esempio n. 24
0
 def test_init(self):
     entry = DefectEntry(self.substitution, 2.5)
     entry_doc = entry.as_dict()
     re_entry = DefectEntry.from_dict(entry_doc)
     self.assertNotEqual(re_entry, None)
    def test_check_final_relaxed_structure_delocalized(self):
        # test structure delocalization analysis
        # first test no movement in atoms
        initial_defect_structure = self.vac.generate_defect_structure()
        final_defect_structure = initial_defect_structure.copy()
        sampling_radius = 4.55
        defect_frac_sc_coords = self.vac.site.frac_coords[:]

        params = {
            "initial_defect_structure": initial_defect_structure,
            "final_defect_structure": final_defect_structure,
            "sampling_radius": sampling_radius,
            "defect_frac_sc_coords": defect_frac_sc_coords,
            "is_compatible": True,
        }
        dentry = DefectEntry(self.vac, 0.0, corrections={}, parameters=params, entry_id=None)

        dc = DefectCompatibility(tot_relax_tol=0.1, perc_relax_tol=0.1, defect_tot_relax_tol=0.1)
        dentry = dc.check_final_relaxed_structure_delocalized(dentry)

        struc_delocal = dentry.parameters["delocalization_meta"]["structure_relax"]
        self.assertTrue(dentry.parameters["is_compatible"])
        self.assertTrue(struc_delocal["is_compatible"])
        self.assertTrue(struc_delocal["metadata"]["structure_tot_relax_compatible"])
        self.assertEqual(struc_delocal["metadata"]["tot_relax_outside_rad"], 0.0)
        self.assertTrue(struc_delocal["metadata"]["structure_perc_relax_compatible"])
        self.assertEqual(struc_delocal["metadata"]["perc_relax_outside_rad"], 0.0)
        self.assertEqual(
            len(struc_delocal["metadata"]["full_structure_relax_data"]),
            len(initial_defect_structure),
        )
        self.assertIsNone(struc_delocal["metadata"]["defect_index"])

        defect_delocal = dentry.parameters["delocalization_meta"]["defectsite_relax"]
        self.assertTrue(defect_delocal["is_compatible"])
        self.assertIsNone(defect_delocal["metadata"]["relax_amount"])

        # next test for when structure has delocalized outside of radius from defect
        pert_struct_fin_struct = initial_defect_structure.copy()
        pert_struct_fin_struct.perturb(0.1)
        dentry.parameters.update({"final_defect_structure": pert_struct_fin_struct})
        dentry = dc.check_final_relaxed_structure_delocalized(dentry)

        struc_delocal = dentry.parameters["delocalization_meta"]["structure_relax"]
        self.assertFalse(dentry.parameters["is_compatible"])
        self.assertFalse(struc_delocal["is_compatible"])
        self.assertFalse(struc_delocal["metadata"]["structure_tot_relax_compatible"])
        self.assertAlmostEqual(struc_delocal["metadata"]["tot_relax_outside_rad"], 12.5)
        self.assertFalse(struc_delocal["metadata"]["structure_perc_relax_compatible"])
        self.assertAlmostEqual(struc_delocal["metadata"]["perc_relax_outside_rad"], 77.63975155)

        # now test for when an interstitial defect has migrated too much
        inter_def_site = PeriodicSite(
            "H",
            [7.58857304, 11.70848069, 12.97817518],
            self.vac.bulk_structure.lattice,
            to_unit_cell=True,
            coords_are_cartesian=True,
        )
        inter = Interstitial(self.vac.bulk_structure, inter_def_site, charge=0)

        initial_defect_structure = inter.generate_defect_structure()
        final_defect_structure = initial_defect_structure.copy()
        poss_deflist = sorted(
            final_defect_structure.get_sites_in_sphere(inter.site.coords, 2, include_index=True),
            key=lambda x: x[1],
        )
        def_index = poss_deflist[0][2]
        final_defect_structure.translate_sites(
            indices=[def_index], vector=[0.0, 0.0, 0.008]
        )  # fractional coords translation
        defect_frac_sc_coords = inter_def_site.frac_coords[:]

        params = {
            "initial_defect_structure": initial_defect_structure,
            "final_defect_structure": final_defect_structure,
            "sampling_radius": sampling_radius,
            "defect_frac_sc_coords": defect_frac_sc_coords,
            "is_compatible": True,
        }
        dentry = DefectEntry(inter, 0.0, corrections={}, parameters=params, entry_id=None)

        dentry = dc.check_final_relaxed_structure_delocalized(dentry)

        defect_delocal = dentry.parameters["delocalization_meta"]["defectsite_relax"]
        self.assertFalse(defect_delocal["is_compatible"])
        self.assertAlmostEqual(defect_delocal["metadata"]["relax_amount"], 0.10836054)
    def from_paths( path_to_defect, path_to_bulk, dielectric, defect_charge, mpid = None,
                    compatibility=DefectCompatibility(), initial_defect_structure = None):
        """
        Identify defect object based on file paths. Minimal parsing performing for
        instantiating the SingleDefectParser class.

        :param path_to_defect (str): path to defect file of interest
        :param path_to_bulk (str): path to bulk file of interest
        :param dielectric (float or 3x3 matrix): ionic + static contributions to dielectric constant
        :param defect_charge (int):
        :param mpid (str):
        :param compatibility (DefectCompatibility): Compatibility class instance for
            performing compatibility analysis on defect entry.

        Return:
            Instance of the SingleDefectParser class.
        """
        parameters = {"bulk_path": path_to_bulk, "defect_path": path_to_defect,
                      "dielectric": dielectric, "mpid": mpid}

        # add bulk simple properties
        bulk_vr = Vasprun( os.path.join(path_to_bulk, "vasprun.xml"))
        bulk_energy = bulk_vr.final_energy
        bulk_sc_structure = bulk_vr.initial_structure.copy()

        # add defect simple properties
        defect_vr = Vasprun( os.path.join(path_to_defect, "vasprun.xml"))
        defect_energy = defect_vr.final_energy
        # Can specify initial defect structure (to help PyCDT find the defect site if
        # multiple relaxations were required, else use from defect relaxation OUTCAR:
        if initial_defect_structure:
            initial_defect_structure = Poscar.from_file(initial_defect_structure).structure.copy()
        else:
            initial_defect_structure = defect_vr.initial_structure.copy()

        # identify defect site, structural information, and create defect object
        num_ids = len(initial_defect_structure)
        num_bulk = len(bulk_sc_structure)
        if num_ids == num_bulk - 1:
            defect_type = "Vacancy"
        elif num_ids == num_bulk + 1:
            defect_type = "Interstitial"
        elif num_ids == num_bulk:
            defect_type = "Substitution"
        else:
            raise ValueError("Could not identify defect type just from number of sites in structure: "
                             "{} in bulk vs. {} in defect?".format( num_ids, num_bulk ))

        defect_index_sc_coords = None
        transformation_path = os.path.join( path_to_defect, "transformation.json")
        if os.path.exists( transformation_path):
            tf = loadfn( transformation_path)
            site = tf["defect_supercell_site"]
            if defect_type == "Vacancy":
                poss_deflist = sorted(
                    bulk_sc_structure.get_sites_in_sphere(site.coords, 0.1, include_index=True), key=lambda x: x[1])
            else:
                poss_deflist = sorted(
                    initial_defect_structure.get_sites_in_sphere(site.coords, 0.1, include_index=True), key=lambda x: x[1])
            if not len(poss_deflist):
                raise ValueError("{} specified defect site {}, but could not find it in bulk_supercell."
                                 " Abandoning parsing".format( transformation_path, site))
            else:
                defect_index_sc_coords = poss_deflist[0][2]
        else:
            print("No transformation file exists at {}.\nCalculating defect index manually"
                  " (proceed with caution)".format( transformation_path))

        # IF not transformation file exists, the defect_index_sc_coords will not be identified in previous routine,
        # proceed by identifying the defect site through a comparison of bulk sites and initial defect structure sites.
        # WARNING: this can cause issues if intial_defect_structure is slightly different than
        # bulk_sc_structure (as a result of multiple relaxation steps, for example)
        if defect_index_sc_coords is None:
            bulksites = [site.frac_coords for site in bulk_sc_structure]
            initsites = [site.frac_coords for site in initial_defect_structure]
            distmatrix = initial_defect_structure.lattice.get_all_distances(bulksites,
                                                                            initsites)
            min_dist_with_index = [[min(distmatrix[bulk_index]), int(bulk_index),
                                    int(distmatrix[bulk_index].argmin())] for bulk_index in
                                   range(len(distmatrix))]  # list of [min dist, bulk ind, defect ind]

            site_matching_indices = []
            poss_defect = []
            if defect_type in ["Vacancy", "Interstitial"]:
                for mindist, bulk_index, defect_index in min_dist_with_index:
                    if mindist < 0.1:
                        site_matching_indices.append([bulk_index, defect_index])
                    elif defect_type == "Vacancy":
                        poss_defect.append([bulk_index, bulksites[bulk_index][:]])

                if defect_type == "Interstitial":
                    poss_defect = [[ind, fc[:]] for ind, fc in enumerate(initsites) \
                                   if ind not in np.array(site_matching_indices)[:, 1]]

            elif defect_type == "Substitution":
                for mindist, bulk_index, defect_index in min_dist_with_index:
                    species_match = bulk_sc_structure[bulk_index].specie == \
                                    initial_defect_structure[defect_index].specie
                    if mindist < 0.1 and species_match:
                        site_matching_indices.append([bulk_index, defect_index])

                    elif not species_match:
                        poss_defect.append([defect_index, initsites[defect_index][:]])

            if len(poss_defect) == 1:
                defect_index_sc_coords = poss_defect[0][0]
            else:
                raise ValueError("Found {} possible defect sites when matching bulk and "
                                 "defect structure".format(len(poss_defect)))

            if len(set(np.array(site_matching_indices)[:, 0])) != len(set(np.array(site_matching_indices)[:, 1])):
                raise ValueError("Error occured in site_matching routine. Double counting of site matching "
                                  "occured:{}\nAbandoning structure parsing.".format(site_matching_indices))

        if defect_type == "Vacancy":
            defect_site = bulk_sc_structure[ defect_index_sc_coords]
        else:
            defect_site = initial_defect_structure[ defect_index_sc_coords]


        for_monty_defect = {"@module": "pymatgen.analysis.defects.core",
                            "@class": defect_type,
                            "charge": defect_charge,
                            "structure": bulk_sc_structure,
                            "defect_site": defect_site}
        defect = MontyDecoder().process_decoded(for_monty_defect)
        test_defect_structure = defect.generate_defect_structure()
        if not StructureMatcher(stol=0.5, primitive_cell=False, scale=False, attempt_supercell=False,
                                allow_subset=False).fit(test_defect_structure,
                                                        defect_vr.initial_structure):
            # NOTE: this does not insure that cartesian coordinates or indexing are identical
            # Note: I've changed stol to 0.5 to fix matching for defects that move the f**k about yo
            raise ValueError("Error in defect object matching!")


        defect_entry = DefectEntry(defect, defect_energy - bulk_energy,
                                   corrections={}, parameters=parameters)

        return SingleDefectParser( defect_entry, compatibility=compatibility,
                                   defect_vr=defect_vr, bulk_vr=bulk_vr)
    def parse_defect_calculations(self):
        """
        Parses the defect calculations as DefectEntry objects,
        from a PyCDT root_fldr file structure.
        Charge correction is missing in the first run.
        """
        logger = logging.getLogger(__name__)
        parsed_defects = []
        subfolders = glob.glob(os.path.join(self._root_fldr, "vac_*"))
        subfolders += glob.glob(os.path.join(self._root_fldr, "as_*"))
        subfolders += glob.glob(os.path.join(self._root_fldr, "sub_*"))
        subfolders += glob.glob(os.path.join(self._root_fldr, "inter_*"))

        def get_vr_and_check_locpot(fldr):
            vr_file = os.path.join(fldr,"vasprun.xml")
            if not os.path.exists(vr_file):
                logger.warning("{} doesn't exit".format(vr_file))
                error_msg = ": Failure, vasprun.xml doesn't exist."
                return (None, error_msg) #Further processing is not useful

            try:
                vr = Vasprun(vr_file, parse_potcar_file=False)
            except:
                logger.warning("Couldn't parse {}".format(vr_file))
                error_msg = ": Failure, couldn't parse vaprun.xml file."
                return (None, error_msg)

            if not vr.converged:
                logger.warning(
                    "Vasp calculation at {} not converged".format(fldr))
                error_msg = ": Failure, Vasp calculation not converged."
                return (None, error_msg) # Further processing is not useful

            # Check if locpot exists
            locpot_file = os.path.join(fldr, "LOCPOT")
            if not os.path.exists(locpot_file):
                logger.warning("{} doesn't exit".format(locpot_file))
                error_msg = ": Failure, LOCPOT doesn't exist"
                return (None, error_msg) #Further processing is not useful

            return (vr, None)

        def get_encut_from_potcar(fldr):
            potcar_file = os.path.join(fldr,"POTCAR")
            if not os.path.exists(potcar_file):
                logger.warning("Not POTCAR in {} to parse ENCUT".format(fldr))
                error_msg = ": Failure, No POTCAR file."
                return (None, error_msg) #Further processing is not useful

            try:
                potcar = Potcar.from_file(potcar_file)
            except:
                logger.warning("Couldn't parse {}".format(potcar_file))
                error_msg = ": Failure, couldn't read POTCAR file."
                return (None, error_msg)

            encut = max(ptcr_sngl.enmax for ptcr_sngl in potcar)
            return (encut, None)

        # get bulk entry information first
        fldr = os.path.join(self._root_fldr, "bulk")
        vr, error_msg = get_vr_and_check_locpot(fldr)
        if error_msg:
            logger.error("Abandoning parsing of the calculations")
            return {}
        bulk_energy = vr.final_energy
        bulk_sc_struct = vr.final_structure
        try:
            encut = vr.incar["ENCUT"]
        except:  # ENCUT not specified in INCAR. Read from POTCAR
            encut, error_msg = get_encut_from_potcar(fldr)
            if error_msg:
                logger.error("Abandoning parsing of the calculations")
                return {}

        trans_dict = loadfn(
            os.path.join(fldr, "transformation.json"),
            cls=MontyDecoder)
        supercell_size = trans_dict["supercell"]

        bulk_file_path = fldr
        bulk_entry = ComputedStructureEntry(
            bulk_sc_struct, bulk_energy,
            data={"bulk_path": bulk_file_path,
                  "encut": encut,
                  "supercell_size": supercell_size})

        # get defect entry information
        for fldr in subfolders:
            fldr_name = os.path.split(fldr)[1]
            chrg_fldrs = glob.glob(os.path.join(fldr,"charge*"))
            for chrg_fldr in chrg_fldrs:
                trans_dict = loadfn(
                        os.path.join(chrg_fldr, "transformation.json"),
                        cls=MontyDecoder)
                chrg = trans_dict["charge"]
                vr, error_msg = get_vr_and_check_locpot(chrg_fldr)
                if error_msg:
                    logger.warning("Parsing the rest of the calculations")
                    continue
                if "substitution_specie" in trans_dict and \
                        trans_dict["substitution_specie"] not in bulk_sc_struct.symbol_set:
                    self._substitution_species.add(
                            trans_dict["substitution_specie"])
                elif "inter" in trans_dict["defect_type"] and \
                        trans_dict["defect_site"].specie.symbol not in bulk_sc_struct.symbol_set:
                    # added because extrinsic interstitials don't have
                    # "substitution_specie" character...
                    trans_dict["substitution_specie"] = trans_dict["defect_site"].specie.symbol
                    self._substitution_species.add(
                            trans_dict["defect_site"].specie.symbol)

                defect_type = trans_dict.get("defect_type", None)
                energy = vr.final_energy
                try:
                    encut = vr.incar["ENCUT"]
                except: # ENCUT not specified in INCAR. Read from POTCAR
                    encut, error_msg = get_encut_from_potcar(chrg_fldr)
                    if error_msg:
                        logger.warning("Not able to determine ENCUT "
                                       "in {}".format(fldr_name))
                        logger.warning("Parsing the rest of the "
                                       "calculations")
                        continue

                comp_data = {"bulk_path": bulk_file_path,
                             "defect_path": chrg_fldr, "encut": encut,
                             "fldr_name": fldr_name, "supercell_size": supercell_size}
                if "substitution_specie" in trans_dict:
                    comp_data["substitution_specie"] = \
                            trans_dict["substitution_specie"]

                # create Defect Object as dict, then load to DefectEntry object
                defect_dict = {"structure": bulk_sc_struct, "charge": chrg,
                               "@module": "pymatgen.analysis.defects.core"
                               }
                defect_site = trans_dict["defect_supercell_site"]
                if "vac_" in defect_type:
                    defect_dict["@class"] = "Vacancy"
                elif "as_" in defect_type or "sub_" in defect_type:
                    defect_dict["@class"] = "Substitution"
                    substitution_specie = trans_dict["substitution_specie"]
                    defect_site = PeriodicSite( substitution_specie, defect_site.frac_coords,
                                                defect_site.lattice, coords_are_cartesian=False)
                elif "int_" in defect_type:
                    defect_dict["@class"] = "Interstitial"
                else:
                    raise ValueError("defect type {} not recognized...".format(defect_type))

                defect_dict.update( {"defect_site": defect_site})
                defect = MontyDecoder().process_decoded( defect_dict)
                parsed_defects.append( DefectEntry( defect, energy - bulk_energy,
                                                    parameters=comp_data))

        try:
            parsed_defects_data = {}
            parsed_defects_data["bulk_entry"] = bulk_entry
            parsed_defects_data["defects"] = parsed_defects
            return parsed_defects_data
        except:
            return {} # Return Null dict due to failure
Esempio n. 28
0
    def test_kumagai(self):
        gamma = 0.19357221
        prec = 28
        lattice = Lattice(
            [[4.692882, -8.12831, 0.0], [4.692882, 8.12831, 0.0], [0.0, 0.0, 10.03391]]
        )

        # note that real/recip vector generation is not dependent on epsilon
        g_vecs, _, r_vecs, _ = generate_R_and_G_vecs(
            gamma, prec, lattice, 80.0 * np.identity(3)
        )

        # test real space summation (bigger for large epsilon)
        kc_high_diel = KumagaiCorrection(80.0 * np.identity(3), gamma=gamma)
        real_sum = kc_high_diel.get_real_summation(gamma, r_vecs[0])
        self.assertAlmostEqual(real_sum, 0.00843104)

        # test recip space summation (bigger for small epsilon)
        kc_low_diel = KumagaiCorrection(0.1 * np.identity(3), gamma=gamma)
        recip_sum = kc_low_diel.get_recip_summation(gamma, g_vecs[0], lattice.volume)
        self.assertAlmostEqual(recip_sum, 0.31117099)

        # test self interaction
        si_corr = kc_low_diel.get_self_interaction(gamma)
        self.assertAlmostEqual(si_corr, -0.54965249)

        # test potenital shift interaction correction
        ps_corr = kc_low_diel.get_potential_shift(gamma, lattice.volume)
        self.assertAlmostEqual(ps_corr, -0.00871593)

        # """Test Defect Entry approach to correction """
        bulk_struc = Poscar.from_file(
            os.path.join(PymatgenTest.TEST_FILES_DIR, "defect", "CONTCAR_bulk")
        ).structure
        bulk_out = Outcar(os.path.join(PymatgenTest.TEST_FILES_DIR, "defect", "OUTCAR_bulk.gz"))
        defect_out = Outcar(os.path.join(PymatgenTest.TEST_FILES_DIR, "defect", "OUTCAR_vac_Ga_-3.gz"))
        epsilon = 18.118 * np.identity(3)
        vac = Vacancy(bulk_struc, bulk_struc.sites[0], charge=-3)
        defect_structure = vac.generate_defect_structure()
        defect_frac_coords = [0.0, 0.0, 0.0]

        parameters = {
            "bulk_atomic_site_averages": bulk_out.electrostatic_potential,
            "defect_atomic_site_averages": defect_out.electrostatic_potential,
            "site_matching_indices": [[ind, ind - 1] for ind in range(len(bulk_struc))],
            "initial_defect_structure": defect_structure,
            "defect_frac_sc_coords": defect_frac_coords,
        }
        dentry = DefectEntry(vac, 0.0, parameters=parameters)
        kc = KumagaiCorrection(epsilon)
        kcorr = kc.get_correction(dentry)
        self.assertAlmostEqual(kcorr["kumagai_electrostatic"], 0.88236299)
        self.assertAlmostEqual(kcorr["kumagai_potential_alignment"], 2.09704862)

        # test ES correction
        high_diel_es_corr = kc_high_diel.perform_es_corr(gamma, prec, lattice, -3.0)
        self.assertAlmostEqual(high_diel_es_corr, 0.25176240)

        low_diel_es_corr = kc_low_diel.perform_es_corr(gamma, prec, lattice, -3.0)
        self.assertAlmostEqual(low_diel_es_corr, 201.28810966)

        # test pot correction
        site_list = []
        for bs_ind, ds_ind in dentry.parameters["site_matching_indices"]:
            Vqb = -(
                defect_out.electrostatic_potential[ds_ind]
                - bulk_out.electrostatic_potential[bs_ind]
            )
            site_list.append([defect_structure[ds_ind], Vqb])

        sampling_radius = dentry.parameters["kumagai_meta"]["sampling_radius"]
        gamma = dentry.parameters["kumagai_meta"]["gamma"]
        q = -3
        g_vecs, _, r_vecs, _ = generate_R_and_G_vecs(
            gamma, 28, defect_structure.lattice, np.identity(3)
        )
        high_diel_pot_corr = kc_high_diel.perform_pot_corr(
            defect_structure,
            defect_frac_coords,
            site_list,
            sampling_radius,
            q,
            r_vecs[0],
            g_vecs[0],
            gamma,
        )
        self.assertAlmostEqual(high_diel_pot_corr, 2.35840716)
        low_diel_pot_corr = kc_low_diel.perform_pot_corr(
            defect_structure,
            defect_frac_coords,
            site_list,
            sampling_radius,
            q,
            r_vecs[0],
            g_vecs[0],
            gamma,
        )
        self.assertAlmostEqual(low_diel_pot_corr, -58.83598095)

        # test the kumagai plotter
        kcp = kc.plot()
        self.assertTrue(kcp)

        # check that uncertainty metadata exists
        self.assertAlmostEqual(
            set(kc.metadata["pot_corr_uncertainty_md"].keys()),
            set(["number_sampled", "stats"]),
        )
Esempio n. 29
0
    def test_freysoldt(self):
        struc = PymatgenTest.get_structure("VO2")
        struc.make_supercell(3)
        struc = struc
        vac = Vacancy(struc, struc.sites[0], charge=-3)
        ids = vac.generate_defect_structure(1)

        abc = struc.lattice.abc
        axisdata = [np.arange(0.0, lattval, 0.2) for lattval in abc]
        bldata = [
            np.array([1.0 for u in np.arange(0.0, lattval, 0.2)]) for lattval in abc
        ]
        dldata = [
            np.array(
                [
                    (-1 - np.cos(2 * np.pi * u / lattval))
                    for u in np.arange(0.0, lattval, 0.2)
                ]
            )
            for lattval in abc
        ]
        params = {
            "axis_grid": axisdata,
            "bulk_planar_averages": bldata,
            "defect_planar_averages": dldata,
            "initial_defect_structure": ids,
            "defect_frac_sc_coords": struc.sites[0].frac_coords,
        }
        fc = FreysoldtCorrection(15)

        # test electrostatic correction
        es_corr = fc.perform_es_corr(struc.lattice, -3)
        self.assertAlmostEqual(es_corr, 0.975893)

        # test potential alignment method
        pot_corr = fc.perform_pot_corr(
            axisdata[0], bldata[0], dldata[0], struc.lattice, -3, vac.site.coords, 0
        )
        self.assertAlmostEqual(pot_corr, 2.836369987722345)

        # test entry full correction method
        de = DefectEntry(vac, 0.0, corrections={}, parameters=params, entry_id=None)
        val = fc.get_correction(de)
        self.assertAlmostEqual(val["freysoldt_electrostatic"], 0.975893)
        self.assertAlmostEqual(val["freysoldt_potential_alignment"], 4.4700574)

        # test the freysoldt plotter
        for ax in range(3):
            fcp = fc.plot(axis=ax)
            self.assertTrue(fcp)

        # check that uncertainty metadata exists
        for ax in range(3):
            self.assertAlmostEqual(
                set(fc.metadata["pot_corr_uncertainty_md"][ax].keys()),
                set(["potcorr", "stats"]),
            )

        # test a specified axis from entry
        fc = FreysoldtCorrection(15, axis=[1])
        val = fc.get_correction(de)
        self.assertAlmostEqual(val["freysoldt_potential_alignment"], 5.2869010593283132)

        # test a different charge
        #   for electrostatic correction
        es_corr = fc.perform_es_corr(struc.lattice, 2)
        self.assertAlmostEqual(es_corr, 0.43373)
        #   for potential alignment method
        pot_corr = fc.perform_pot_corr(
            axisdata[0], bldata[0], dldata[0], struc.lattice, 2, vac.site.coords, 0
        )
        self.assertAlmostEqual(pot_corr, -2.1375685936497768)

        # test an input anisotropic dielectric constant
        fc = FreysoldtCorrection([[1.0, 2.0, 3.0], [0.0, 3.0, 5.0], [4.0, 10.0, 8.0]])
        self.assertAlmostEqual(fc.dielectric, 4.0)
        val = fc.get_correction(de)
        self.assertAlmostEqual(val["freysoldt_electrostatic"], 3.659599)
        self.assertAlmostEqual(val["freysoldt_potential_alignment"], 3.3605255195745087)

        # test potalign being added to defect entry
        self.assertAlmostEqual(de.parameters["potalign"], 1.1201751731915028)

        # test that metadata entries exist in defect entry
        self.assertTrue("freysoldt_meta" in de.parameters.keys())
        self.assertAlmostEqual(
            set(de.parameters["freysoldt_meta"].keys()),
            set(["pot_plot_data", "pot_corr_uncertainty_md"]),
        )

        # test a charge of zero
        vac = Vacancy(struc, struc.sites[0], charge=0)
        de = DefectEntry(vac, 0.0, corrections={}, parameters=params, entry_id=None)
        val = fc.get_correction(de)
        self.assertAlmostEqual(val["freysoldt_electrostatic"], 0.0)
        self.assertAlmostEqual(val["freysoldt_potential_alignment"], 0.0)
Esempio n. 30
0
    def test_bandfilling(self):
        v = Vasprun(os.path.join(PymatgenTest.TEST_FILES_DIR, "vasprun.xml"))
        eigenvalues = v.eigenvalues.copy()
        kptweights = v.actual_kpoints_weights
        potalign = 0.0
        vbm = v.eigenvalue_band_properties[2]
        cbm = v.eigenvalue_band_properties[1]
        params = {
            "eigenvalues": eigenvalues,
            "kpoint_weights": kptweights,
            "potalign": potalign,
            "vbm": vbm,
            "cbm": cbm,
        }
        bfc = BandFillingCorrection()
        struc = PymatgenTest.get_structure("VO2")
        struc.make_supercell(3)
        vac = Vacancy(struc, struc.sites[0], charge=-3)

        # test trivial performing bandfilling correction
        bf_corr = bfc.perform_bandfill_corr(eigenvalues, kptweights, potalign, vbm, cbm)
        self.assertAlmostEqual(bf_corr, 0.0)
        self.assertFalse(bfc.metadata["num_elec_cbm"])
        self.assertFalse(bfc.metadata["num_hole_vbm"])
        self.assertFalse(bfc.metadata["potalign"])

        # test trivial full entry bandfill evaluation
        de = DefectEntry(vac, 0.0, corrections={}, parameters=params, entry_id=None)

        corr = bfc.get_correction(de)
        self.assertAlmostEqual(corr["bandfilling_correction"], 0.0)

        # modify the eigenvalue list to have free holes
        hole_eigenvalues = {}
        for spinkey, spinset in eigenvalues.items():
            hole_eigenvalues[spinkey] = []
            for kptset in spinset:
                hole_eigenvalues[spinkey].append([])
                for eig in kptset:
                    if (eig[0] < vbm) and (eig[0] > vbm - 0.8):
                        hole_eigenvalues[spinkey][-1].append([eig[0], 0.5])
                    else:
                        hole_eigenvalues[spinkey][-1].append(eig)

        hole_bf_corr = bfc.perform_bandfill_corr(
            hole_eigenvalues, kptweights, potalign, vbm, cbm
        )
        self.assertAlmostEqual(hole_bf_corr, -0.41138336)
        self.assertAlmostEqual(bfc.metadata["num_hole_vbm"], 0.8125000649)
        self.assertFalse(bfc.metadata["num_elec_cbm"])

        # test case with only one spin and eigen-occupations are 1.
        one_spin_eigen = hole_eigenvalues.copy()
        del one_spin_eigen[list(eigenvalues.keys())[0]]
        bf_corr = bfc.perform_bandfill_corr(
            one_spin_eigen, kptweights, potalign, vbm, cbm
        )
        self.assertAlmostEqual(bf_corr, -0.14487501159000005)

        # test case with only one spin and eigen-occupations are 2.
        one_spin_eigen_twooccu = one_spin_eigen.copy()
        for kptset in one_spin_eigen_twooccu.values():
            for bandset in kptset:
                for occuset in bandset:
                    if occuset[1] == 1.0:
                        occuset[1] = 2.0
                    elif occuset[1] == 0.5:
                        occuset[1] = 1.0
        bf_corr = bfc.perform_bandfill_corr(
            one_spin_eigen_twooccu, kptweights, potalign, vbm, cbm
        )
        self.assertAlmostEqual(bf_corr, -0.14487501159000005)