Exemple #1
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)
Exemple #2
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)
Exemple #3
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)
Exemple #4
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)
Exemple #5
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)
    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)
    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)
Exemple #8
0
    def from_dict(cls, d):
        """
        Reconstitute a DefectPhaseDiagram object from a dict representation created using
        as_dict().

        Args:
            d (dict): dict representation of DefectPhaseDiagram.

        Returns:
            DefectPhaseDiagram object
        """
        entries = [
            DefectEntry.from_dict(entry_dict)
            for entry_dict in d.get("entries")
        ]
        vbm = d["vbm"]
        band_gap = d["band_gap"]
        filter_compatible = d.get("filter_compatible", True)
        metadata = d.get("metadata", {})
        if 'entry_id' in d.keys() and 'entry_id' not in metadata:
            metadata['entry_id'] = d['entry_id']

        return cls(entries,
                   vbm,
                   band_gap,
                   filter_compatible=filter_compatible,
                   metadata=metadata)
    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"])
Exemple #10
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)
    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_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)
    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
Exemple #16
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,
        )
Exemple #17
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 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_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())
    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)
Exemple #22
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)
Exemple #23
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.)
Exemple #24
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)
Exemple #25
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)
    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'])
    def from_dict(cls, d):
        """
        Reconstitute a DefectPhaseDiagram object from a dict representation created using
        as_dict().

        Args:
            d (dict): dict representation of DefectPhaseDiagram.

        Returns:
            DefectPhaseDiagram object
        """
        entries = [DefectEntry.from_dict(entry_dict) for entry_dict in d.get("entries")]
        vbm = d["vbm"]
        band_gap = d["band_gap"]
        filter_compatible = d.get("filter_compatible", True)
        metadata = d.get("metadata", {})
        if 'entry_id' in d.keys() and 'entry_id' not in metadata:
            metadata['entry_id'] = d['entry_id']

        return cls(entries, vbm, band_gap, filter_compatible=filter_compatible,
                   metadata=metadata)
Exemple #28
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.)
Exemple #29
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)
Exemple #30
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 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
class DefectsThermodynamicsTest(PymatgenTest):
    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_good_test_data(self):
        self.assertEqual(len(self.entries), 48)

    def test_suggest_charges(self):
        suggested_charges = self.pd.suggest_charges()
        for k in [
                    "Vac_As_mult4@0-1-2-3-4-5", "Sub_Ga_on_As_mult4@6-7-8-9-10-11", "Vac_Ga_mult4@12-13-14-15",
                    "Sub_As_on_Ga_mult4@16-17-18-19-20-21", "Int_Ga_mult1@22-23-24-25",
                    "Int_As_mult1@26-27-28-29-30-31-32-33-34", "Int_As_mult1@35-36-37-38-39-40-41-42-43",
                    "Int_Ga_mult1@44-45-46-47"
        ]:
            self.assertTrue(
                len(suggested_charges[k]) > 0, "Could not find any suggested charges for {} with band_gap of {}".format(
                    k, self.pd.band_gap))

        pd = DefectPhaseDiagram(self.entries, 2.6682, 1.0)
        suggested_charges = self.pd.suggest_charges()
        for k in ["Vac_As_mult4@0-1-2-3-4-5", "Vac_Ga_mult4@12-13-14-15"]:
            self.assertTrue(
                len(suggested_charges[k]) > 0, "Could not find any suggested charges for {} with band_gap of {}".format(
                    k, pd.band_gap))

        #test again but with only one charge state stable for Vac_As
        suggested_charges = self.sep_pd.suggest_charges()
        self.assertEqual( set(suggested_charges['Vac_As_mult4@0-43']), set([-4]))

    def test_suggest_larger_supercells(self):
        suggested_larger_cells = self.pd_ls_fcFalse.suggest_larger_supercells()
        self.assertEqual( suggested_larger_cells['Vac_As_mult4@0-1-2-3-4-5'], [-2])

        # raise error if filter_compatibile = True
        self.assertRaises( ValueError, self.pd_ls_fcTrue.suggest_larger_supercells)

    def test_entries(self):
        all_stable_entries = self.pd.all_stable_entries

        self.assertEqual(len(self.pd.defect_types), 8)
        self.assertEqual(len(all_stable_entries), sum([len(v) for v in self.pd.stable_charges.values()]))

        #test again but with only one charge state stable for Vac_As
        self.assertEqual( len(self.sep_pd.transition_level_map['Vac_As_mult4@0-43']), 0)
        self.assertEqual( len(self.sep_pd.stable_entries['Vac_As_mult4@0-43']), 1)
        self.assertEqual( len(self.sep_pd.finished_charges['Vac_As_mult4@0-43']), 2)
    #
    def test_solve_for_fermi_energy(self):
        fermi_energy = self.pd.solve_for_fermi_energy( 100., self.mu_elts, self.dos)
        self.assertAlmostEqual( fermi_energy, 0.57387314)
        fermi_energy = self.pd.solve_for_fermi_energy( 1000., self.mu_elts, self.dos)
        self.assertAlmostEqual( fermi_energy, 0.74139553)

    def test_solve_for_non_equilibrium_fermi_energy(self):
        fermi_energy = self.pd.solve_for_non_equilibrium_fermi_energy( 300., 1000., self.mu_elts, self.dos)
        self.assertAlmostEqual( fermi_energy, 0.29500637)
        fermi_energy = self.pd.solve_for_non_equilibrium_fermi_energy( 1000., 1000., self.mu_elts, self.dos)
        self.assertAlmostEqual( fermi_energy, 0.74139553)

    def test_get_dopability_limits(self):
        lower_lim, upper_lim = self.pd.get_dopability_limits( self.mu_elts)
        self.assertAlmostEqual( lower_lim, -0.39996272)
        self.assertAlmostEqual( upper_lim, 1.064193047)
        # raise error if defects are negative across gap
        bad_mu_elts = self.mu_elts.copy()
        bad_mu_elts[Element("Ga")] += 10.
        lower_lim, upper_lim = self.pd.get_dopability_limits( bad_mu_elts)
        self.assertIsNone( lower_lim)
        self.assertIsNone( upper_lim)

    def test_plot(self):
        #simple test that plot is produced
        p = self.pd.plot(saved=False)
        self.assertTrue( p)
Exemple #34
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_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)
    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)
    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"]),
        )