def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = { "axis_grid": axisdata, "bulk_planar_averages": bldata, "defect_planar_averages": dldata, "dielectric": 15, "initial_defect_structure": struc.copy(), "defect_frac_sc_coords": struc.sites[0].frac_coords[:], } kumagai_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")) self.kumagai_vac = Vacancy(kumagai_bulk_struc, kumagai_bulk_struc.sites[0], charge=-3) kumagai_defect_structure = self.kumagai_vac.generate_defect_structure() self.kumagai_params = { "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(kumagai_bulk_struc))], "defect_frac_sc_coords": [0.0, 0.0, 0.0], "initial_defect_structure": kumagai_defect_structure, "dielectric": 18.118 * np.identity(3), "gamma": 0.153156, # not neccessary to load gamma, but speeds up unit test } v = Vasprun(os.path.join(PymatgenTest.TEST_FILES_DIR, "vasprun.xml")) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { "eigenvalues": eigenvalues, "kpoint_weights": kptweights, "potalign": potalign, "vbm": vbm, "cbm": cbm, } self.band_edge_params = { "hybrid_cbm": 1.0, "hybrid_vbm": -1.0, "vbm": -0.5, "cbm": 0.6, "num_hole_vbm": 1.0, "num_elec_cbm": 1.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)
def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = {'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata, 'dielectric': 15} v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { 'eigenvalues': eigenvalues, 'kpoint_weights': kptweights, 'potalign': potalign, 'vbm': vbm, 'cbm': cbm } self.band_edge_params = {'hybrid_cbm': 1., 'hybrid_vbm': -1., 'vbm': -0.5, 'cbm': 0.6, 'num_hole_vbm': 1., 'num_elec_cbm': 1.}
def test_convert_cd_to_de(self): #create a ComputedDefect object similar to legacy format # Vacancy type first struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) vac = Vacancy(struc, struc.sites[0], charge=-3) ids = vac.generate_defect_structure(1) defect_data = { "locpot_path": "defect/path/to/files/LOCPOT", "encut": 520 } bulk_data = {"locpot_path": "bulk/path/to/files/LOCPOT"} cse_defect = ComputedStructureEntry(ids, 100., data=defect_data) cd = ComputedDefect(cse_defect, struc.sites[0], charge=-3, name="Vac_1_O") b_cse = ComputedStructureEntry(struc, 10., data=bulk_data) de = convert_cd_to_de(cd, b_cse) self.assertIsInstance(de.defect, Vacancy) self.assertIsInstance(de, DefectEntry) self.assertEqual(de.parameters["defect_path"], "defect/path/to/files") self.assertEqual(de.parameters["bulk_path"], "bulk/path/to/files") self.assertEqual(de.parameters["encut"], 520) self.assertEqual(de.site.specie.symbol, "O") # try again for substitution type # (site object had bulk specie for ComputedDefects, # while it should have substituional site specie for DefectEntrys...) de_site_type = PeriodicSite("Sb", vac.site.frac_coords, struc.lattice) sub = Substitution(struc, de_site_type, charge=1) ids = sub.generate_defect_structure(1) cse_defect = ComputedStructureEntry(ids, 100., data=defect_data) cd = ComputedDefect(cse_defect, struc.sites[0], charge=1, name="Sub_1_Sb_on_O") de = convert_cd_to_de(cd, b_cse) self.assertIsInstance(de.defect, Substitution) self.assertIsInstance(de, DefectEntry) self.assertEqual(de.site.specie.symbol, "Sb")
def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = { 'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata, 'dielectric': 15 } v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { 'eigenvalues': eigenvalues, 'kpoint_weights': kptweights, 'potalign': potalign, 'vbm': vbm, 'cbm': cbm } self.band_edge_params = { 'hybrid_cbm': 1., 'hybrid_vbm': -1., 'vbm': -0.5, 'cbm': 0.6, 'num_hole_vbm': 1., 'num_elec_cbm': 1. }
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
def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = {'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata, 'dielectric': 15, 'initial_defect_structure': struc.copy(), 'defect_frac_sc_coords': struc.sites[0].frac_coords[:]} kumagai_bulk_struc = Poscar.from_file(os.path.join( test_dir, 'defect', 'CONTCAR_bulk')).structure bulk_out = Outcar( os.path.join( test_dir, 'defect', 'OUTCAR_bulk.gz')) defect_out = Outcar( os.path.join( test_dir, 'defect', 'OUTCAR_vac_Ga_-3.gz')) self.kumagai_vac = Vacancy(kumagai_bulk_struc, kumagai_bulk_struc.sites[0], charge=-3) kumagai_defect_structure = self.kumagai_vac.generate_defect_structure() self.kumagai_params = {'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(kumagai_bulk_struc))], 'defect_frac_sc_coords': [0.,0.,0.], 'initial_defect_structure': kumagai_defect_structure, 'dielectric': 18.118 * np.identity(3), 'gamma': 0.153156 #not neccessary to load gamma, but speeds up unit test } v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { 'eigenvalues': eigenvalues, 'kpoint_weights': kptweights, 'potalign': potalign, 'vbm': vbm, 'cbm': cbm } self.band_edge_params = {'hybrid_cbm': 1., 'hybrid_vbm': -1., 'vbm': -0.5, 'cbm': 0.6, 'num_hole_vbm': 1., 'num_elec_cbm': 1.}
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)
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 __next__(self): """ Returns the next vacancy in the sequence or raises StopIteration """ if len(self.equiv_site_seq) > 0: vac_site = self.equiv_site_seq.pop(0) charge = 0.0 if self.struct_valences: site_index = self.structure.get_sites_in_sphere(vac_site[0].coords, 0.1, include_index=True)[0][2] charge = -1 * self.struct_valences[site_index] return Vacancy(self.structure, vac_site[0], charge=charge) raise StopIteration
def test_apply_transformation(self): struc = PymatgenTest.get_structure("VO2") vac = Vacancy(struc, struc[0], charge=1) def_transform = DefectTransformation([2, 2, 2], vac) trans_structure = def_transform.apply_transformation(struc) self.assertEqual(len(trans_structure), 47) # confirm that transformation doesnt work for bulk structures # which are slightly different than those used for defect object # scaled volume scaled_struc = struc.copy() scaled_struc.scale_lattice(1.1 * struc.volume) self.assertRaises(ValueError, def_transform.apply_transformation, scaled_struc) # slightly different atomic positions pert_struc = struc.copy() pert_struc.perturb(.1) self.assertRaises(ValueError, def_transform.apply_transformation, pert_struc)
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.)
def test_sublattice_generation(self): struc = PymatgenTest.get_structure("CsCl") sc_struc = struc.copy() sc_struc.make_supercell(3) # test for vacancy and sub (should not change structure) Cs_index = sc_struc.indices_from_symbol("Cs")[0] cs_vac = Vacancy(sc_struc, sc_struc[Cs_index]) decorated_cs_vac = create_saturated_interstitial_structure(cs_vac) self.assertEqual(len(decorated_cs_vac), len(sc_struc)) Cl_index = sc_struc.indices_from_symbol("Cl")[0] cl_vac = Vacancy(sc_struc, sc_struc[Cl_index]) decorated_cl_vac = create_saturated_interstitial_structure(cl_vac) self.assertEqual(len(decorated_cl_vac), len(sc_struc)) sub_site = PeriodicSite("Sr", sc_struc[Cs_index].coords, sc_struc.lattice, coords_are_cartesian=True) sub = Substitution(sc_struc, sub_site) decorated_sub = create_saturated_interstitial_structure(sub) self.assertEqual(len(decorated_sub), len(sc_struc)) # test interstitial in symmorphic structure type inter_site = PeriodicSite("H", [0., 1.05225, 2.1045], struc.lattice, coords_are_cartesian=True) # voronoi type interstitial = Interstitial(struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 14) inter_site = PeriodicSite("H", [0.10021429, 0.10021429, 2.1045], struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 14) inter_site = PeriodicSite("H", [4.10878571, 1.10235714, 2.1045], struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 26) inter_site = PeriodicSite( "H", [0., 0., 0.5], struc.lattice, coords_are_cartesian=False) # a reasonable guess type interstitial = Interstitial(struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 5) # test interstitial in non-symmorphic structure type # (voronoi and InFit generator of different types...) ns_struc = Structure.from_file(os.path.join(test_dir, "CuCl.cif")) inter_site = PeriodicSite("H", [0.45173594, 0.41157895, 5.6604067], ns_struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(ns_struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 40) inter_site = PeriodicSite("H", [0.47279906, 0.82845998, 5.62015285], ns_struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(ns_struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 40) inter_site = PeriodicSite("H", [0.70845255, 6.50298148, 5.16979425], ns_struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(ns_struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 40) inter_site = PeriodicSite("H", [0.98191329, 0.36460337, 4.64718203], ns_struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(ns_struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 40) inter_site = PeriodicSite("H", [0.39286561, 3.92702149, 1.05802631], ns_struc.lattice, coords_are_cartesian=True) # InFit type interstitial = Interstitial(ns_struc, inter_site) decorated_inter = create_saturated_interstitial_structure(interstitial) self.assertEqual(len(decorated_inter), 40)
def test_vacancy(self): struc = PymatgenTest.get_structure("VO2") V_index = struc.indices_from_symbol("V")[0] vac = Vacancy(struc, struc[V_index]) # test generation and super cell vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.composition.as_dict(), {"V": 1, "O": 4}) vac_struc = vac.generate_defect_structure(2) self.assertEqual(vac_struc.composition.as_dict(), {"V": 15, "O": 32}) vac_struc = vac.generate_defect_structure(3) self.assertEqual(vac_struc.composition.as_dict(), {"V": 53, "O": 108}) vac_struc = vac.generate_defect_structure([[2., 0, 0], [0, 0, -3.], [0, 2., 0]]) self.assertEqual(vac_struc.composition.as_dict(), {"V": 23, "O": 48}) # test charge vac = Vacancy(struc, struc[V_index]) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 0.0) vac = Vacancy(struc, struc[V_index], charge=1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 1.0) vac = Vacancy(struc, struc[V_index], charge=-1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, -1.0) # test multiplicity vac = Vacancy(struc, struc[V_index]) self.assertEqual(vac.multiplicity, 2) O_index = struc.indices_from_symbol("O")[0] vac = Vacancy(struc, struc[O_index]) self.assertEqual(vac.multiplicity, 4) # Test composition self.assertEqual(dict(vac.defect_composition.as_dict()), { "V": 2, "O": 3 }) # test lattice value error occurs for differnet lattices sc_scaled_struc = struc.copy() sc_scaled_struc.make_supercell(2) self.assertRaises(ValueError, Vacancy, struc, sc_scaled_struc[V_index]) self.assertRaises(ValueError, Vacancy, sc_scaled_struc, struc[V_index])
def substitute_def_struct_gen(mpid, solute, mapi_key, cellmax, struct_file=None): if not mpid and not struct_file: print ("============\nERROR: Provide an mpid\n============") return if not solute: print ("============\nERROR: Provide solute atom\n============") return # Get primitive structure from the Materials Project DB if not struct_file: if not mapi_key: with MPRester() as mp: struct = mp.get_structure_by_material_id(mpid) else: with MPRester(mapi_key) as mp: struct = mp.get_structure_by_material_id(mpid) else: struct = Structure.from_file(struct_file) if mpid: root_fldr = mpid else: root_fldr = struct.composition.reduced_formula sga = SpacegroupAnalyzer(struct) prim_struct = sga.find_primitive() #prim_struct_sites = len(prim_struct.sites) #conv_struct = sga.get_conventional_standard_structure() #conv_struct_sites = len(conv_struct.sites) #conv_prim_ratio = int(conv_struct_sites / prim_struct_sites) # Default VASP settings def_vasp_incar_param = {'ISIF':2, 'EDIFF':1e-6, 'EDIFFG':0.001,} kpoint_den = 15000 # Create each substitutional defect structure and associated VASP files sc_scale = get_sc_scale(inp_struct=prim_struct, final_site_no=cellmax) blk_sc = prim_struct.copy() blk_sc.make_supercell(scaling_matrix=sc_scale) site_no = blk_sc.num_sites # Rescale if needed while site_no > cellmax: max_sc_dim = max(sc_scale) i = sc_scale.index(max_sc_dim) sc_scale[i] -= 1 blk_sc = prim_struct.copy() blk_sc.make_supercell(scaling_matrix=sc_scale) site_no = blk_sc.num_sites # Create solute structures at vacancy sites # First find all unique defect sites blk_str_sites = set(blk_sc.sites) symm_struct = SpacegroupAnalyzer(prim_struct).get_symmetrized_structure() unique_sites = sorted([site[0] for site in symm_struct.equivalent_sites], \ key=lambda s: s.species_string) for i, site in enumerate(unique_sites): vac = Vacancy(structure=prim_struct, defect_site=site) vac_sc = vac.generate_defect_structure(supercell=sc_scale) # Get vacancy site information vac_str_sites = set(vac_sc.sites) vac_sites = blk_str_sites - vac_str_sites vac_site = next(iter(vac_sites)) vac_specie = vac_site.specie.symbol site_mult = vac.get_multiplicity() # Solute substitution defect generation at the vacancy site solute_struct = vac_sc.copy() solute_struct.append(solute, vac_site.frac_coords) custom_kpoints = Kpoints.automatic_density(solute_struct, kppa=kpoint_den) mpvis = MPMetalRelaxSet(solute_struct, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) # Generate VASP directory sub_def_dir ='solute_{}_mult-{}_sitespecie-{}_subspecie-{}'.format( str(i+1), site_mult, vac_specie, solute) fin_dir = os.path.join(root_fldr, sub_def_dir) mpvis.write_input(fin_dir)
def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = { 'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata, 'dielectric': 15, 'initial_defect_structure': struc.copy(), 'defect_frac_sc_coords': struc.sites[0].frac_coords[:] } kumagai_bulk_struc = Poscar.from_file( os.path.join(test_dir, 'defect', 'CONTCAR_bulk')).structure bulk_out = Outcar(os.path.join(test_dir, 'defect', 'OUTCAR_bulk.gz')) defect_out = Outcar( os.path.join(test_dir, 'defect', 'OUTCAR_vac_Ga_-3.gz')) self.kumagai_vac = Vacancy(kumagai_bulk_struc, kumagai_bulk_struc.sites[0], charge=-3) kumagai_defect_structure = self.kumagai_vac.generate_defect_structure() self.kumagai_params = { '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(kumagai_bulk_struc))], 'defect_frac_sc_coords': [0., 0., 0.], 'initial_defect_structure': kumagai_defect_structure, 'dielectric': 18.118 * np.identity(3), 'gamma': 0.153156 #not neccessary to load gamma, but speeds up unit test } v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { 'eigenvalues': eigenvalues, 'kpoint_weights': kptweights, 'potalign': potalign, 'vbm': vbm, 'cbm': cbm } self.band_edge_params = { 'hybrid_cbm': 1., 'hybrid_vbm': -1., 'vbm': -0.5, 'cbm': 0.6, 'num_hole_vbm': 1., 'num_elec_cbm': 1. }
class DefectCompatibilityTest(PymatgenTest): def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = {'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata, 'dielectric': 15, 'initial_defect_structure': struc.copy(), 'defect_frac_sc_coords': struc.sites[0].frac_coords[:]} kumagai_bulk_struc = Poscar.from_file(os.path.join( test_dir, 'defect', 'CONTCAR_bulk')).structure bulk_out = Outcar( os.path.join( test_dir, 'defect', 'OUTCAR_bulk.gz')) defect_out = Outcar( os.path.join( test_dir, 'defect', 'OUTCAR_vac_Ga_-3.gz')) self.kumagai_vac = Vacancy(kumagai_bulk_struc, kumagai_bulk_struc.sites[0], charge=-3) kumagai_defect_structure = self.kumagai_vac.generate_defect_structure() self.kumagai_params = {'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(kumagai_bulk_struc))], 'defect_frac_sc_coords': [0.,0.,0.], 'initial_defect_structure': kumagai_defect_structure, 'dielectric': 18.118 * np.identity(3), 'gamma': 0.153156 #not neccessary to load gamma, but speeds up unit test } v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { 'eigenvalues': eigenvalues, 'kpoint_weights': kptweights, 'potalign': potalign, 'vbm': vbm, 'cbm': cbm } self.band_edge_params = {'hybrid_cbm': 1., 'hybrid_vbm': -1., 'vbm': -0.5, 'cbm': 0.6, 'num_hole_vbm': 1., 'num_elec_cbm': 1.} def test_process_entry(self): # basic process with no corrections dentry = DefectEntry(self.vac, 0., corrections={}, parameters={'vbm': 0., 'cbm': 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'] + .2, 'hybrid_vbm': params['vbm'] - .4, }) dentry = DefectEntry(self.vac, 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'] - .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., 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.) # 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.) self.assertAlmostEqual( dentry.corrections['bandfilling_correction'], 0.) self.assertAlmostEqual( dentry.corrections['charge_correction'], 0.) 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., corrections={}, parameters={}, entry_id=None) dc = DefectCompatibility() dentry = dc.perform_all_corrections( de) self.assertIsNotNone( dentry) #all other correction applications are tested in unit tests below def test_perform_freysoldt(self): de = DefectEntry(self.vac, 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., 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_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.) 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) 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., corrections={}, parameters={}, entry_id=None) dc = DefectCompatibility() dentry = dc.delocalization_analysis( de) self.assertIsNotNone( dentry) #all other correction applications are tested in unit tests below def test_check_freysoldt_delocalized(self): de = DefectEntry(self.vac, 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_check_kumagai_delocalized(self): de = DefectEntry( self.kumagai_vac, 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_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., 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.) self.assertTrue( struc_delocal['metadata']['structure_perc_relax_compatible']) self.assertEqual( struc_delocal['metadata']['perc_relax_outside_rad'], 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.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., 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)
class DefectCompatibilityTest(PymatgenTest): def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = { "axis_grid": axisdata, "bulk_planar_averages": bldata, "defect_planar_averages": dldata, "dielectric": 15, "initial_defect_structure": struc.copy(), "defect_frac_sc_coords": struc.sites[0].frac_coords[:], } kumagai_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")) self.kumagai_vac = Vacancy(kumagai_bulk_struc, kumagai_bulk_struc.sites[0], charge=-3) kumagai_defect_structure = self.kumagai_vac.generate_defect_structure() self.kumagai_params = { "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(kumagai_bulk_struc))], "defect_frac_sc_coords": [0.0, 0.0, 0.0], "initial_defect_structure": kumagai_defect_structure, "dielectric": 18.118 * np.identity(3), "gamma": 0.153156, # not neccessary to load gamma, but speeds up unit test } v = Vasprun(os.path.join(PymatgenTest.TEST_FILES_DIR, "vasprun.xml")) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] defect_incar = v.incar self.bandfill_params = { "eigenvalues": eigenvalues, "kpoint_weights": kptweights, "potalign": potalign, "vbm": vbm, "cbm": cbm, "run_metadata": {"defect_incar": defect_incar}, } self.band_edge_params = { "hybrid_cbm": 1.0, "hybrid_vbm": -1.0, "vbm": -0.5, "cbm": 0.6, "num_hole_vbm": 1.0, "num_elec_cbm": 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 params = self.bandfill_params.copy() # No Freysoldt metadata params.update( { "hybrid_cbm": params["cbm"] + 0.2, "hybrid_vbm": params["vbm"] - 0.4, } ) # 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"], -0.492633372744) 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_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) # all other correction applications are tested in unit tests below 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_run_bandfilling(self): de = DefectEntry( self.vac, 0.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.0) self.assertAlmostEqual(val["num_elec_cbm"], 0.0) self.assertAlmostEqual(val["bandfilling_correction"], 0.0) def test_run_band_edge_shifting(self): de = DefectEntry( self.vac, 0.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) 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) # all other correction applications are tested in unit tests below 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_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_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 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_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_kumagai(self): gamma = 0.19357221 prec = 28 lattice = Lattice( [[ 4.692882, -8.12831 , 0.], [ 4.692882, 8.12831 , 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. * np.identity(3)) #test real space summation (bigger for large epsilon) kc_high_diel = KumagaiCorrection( 80. * 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( test_dir, 'defect', 'CONTCAR_bulk')).structure bulk_out = Outcar( os.path.join( test_dir, 'defect', 'OUTCAR_bulk.gz')) defect_out = Outcar( os.path.join( test_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.] 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., 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.) self.assertAlmostEqual( high_diel_es_corr, 0.25176240) low_diel_es_corr = kc_low_diel.perform_es_corr( gamma, prec, lattice, -3.) 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']))
def test_defect_matching(self): # SETUP DEFECTS FOR TESTING # symmorphic defect test set s_struc = Structure.from_file(os.path.join( test_dir, "CsSnI3.cif")) # tetragonal CsSnI3 identical_Cs_vacs = [ Vacancy(s_struc, s_struc[0]), Vacancy(s_struc, s_struc[1]) ] identical_I_vacs_sublattice1 = [ Vacancy(s_struc, s_struc[4]), Vacancy(s_struc, s_struc[5]), Vacancy(s_struc, s_struc[8]), Vacancy(s_struc, s_struc[9]) ] # in plane halides identical_I_vacs_sublattice2 = [ Vacancy(s_struc, s_struc[6]), Vacancy(s_struc, s_struc[7]) ] # out of plane halides pdc = PointDefectComparator() # NOW TEST DEFECTS # test vacancy matching self.assertTrue( pdc.are_equal(identical_Cs_vacs[0], identical_Cs_vacs[0])) # trivial vacancy test self.assertTrue( pdc.are_equal( identical_Cs_vacs[0], identical_Cs_vacs[1])) # vacancies on same sublattice for i, j in itertools.combinations(range(4), 2): self.assertTrue( pdc.are_equal(identical_I_vacs_sublattice1[i], identical_I_vacs_sublattice1[j])) self.assertTrue( pdc.are_equal(identical_I_vacs_sublattice2[0], identical_I_vacs_sublattice2[1])) self.assertFalse( pdc.are_equal( identical_Cs_vacs[ 0], # both vacancies, but different specie types identical_I_vacs_sublattice1[0])) self.assertFalse( pdc.are_equal( identical_I_vacs_sublattice1[ 0], # same specie type, different sublattice identical_I_vacs_sublattice2[0])) # test substitutional matching sub_Cs_on_I_sublattice1_set1 = PeriodicSite( 'Cs', identical_I_vacs_sublattice1[0].site.frac_coords, s_struc.lattice) sub_Cs_on_I_sublattice1_set2 = PeriodicSite( 'Cs', identical_I_vacs_sublattice1[1].site.frac_coords, s_struc.lattice) sub_Cs_on_I_sublattice2 = PeriodicSite( 'Cs', identical_I_vacs_sublattice2[0].site.frac_coords, s_struc.lattice) sub_Rb_on_I_sublattice2 = PeriodicSite( 'Rb', identical_I_vacs_sublattice2[0].site.frac_coords, s_struc.lattice) self.assertTrue( pdc.are_equal( # trivial substitution test Substitution(s_struc, sub_Cs_on_I_sublattice1_set1), Substitution(s_struc, sub_Cs_on_I_sublattice1_set1))) self.assertTrue( pdc.are_equal( # same sublattice, different coords Substitution(s_struc, sub_Cs_on_I_sublattice1_set1), Substitution(s_struc, sub_Cs_on_I_sublattice1_set2))) self.assertFalse( pdc.are_equal( # different subs (wrong specie) Substitution(s_struc, sub_Cs_on_I_sublattice2), Substitution(s_struc, sub_Rb_on_I_sublattice2))) self.assertFalse( pdc.are_equal( # different subs (wrong sublattice) Substitution(s_struc, sub_Cs_on_I_sublattice1_set1), Substitution(s_struc, sub_Cs_on_I_sublattice2))) # test symmorphic interstitial matching # (using set generated from Voronoi generator, with same sublattice given by saturatated_interstitial_structure function) inter_H_sublattice1_set1 = PeriodicSite('H', [0., 0.75, 0.25], s_struc.lattice) inter_H_sublattice1_set2 = PeriodicSite('H', [0., 0.75, 0.75], s_struc.lattice) inter_H_sublattice2 = PeriodicSite( 'H', [0.57796112, 0.06923687, 0.56923687], s_struc.lattice) inter_H_sublattice3 = PeriodicSite('H', [0.25, 0.25, 0.54018268], s_struc.lattice) inter_He_sublattice3 = PeriodicSite('He', [0.25, 0.25, 0.54018268], s_struc.lattice) self.assertTrue( pdc.are_equal( # trivial interstitial test Interstitial(s_struc, inter_H_sublattice1_set1), Interstitial(s_struc, inter_H_sublattice1_set1))) self.assertTrue( pdc.are_equal( # same sublattice, different coords Interstitial(s_struc, inter_H_sublattice1_set1), Interstitial(s_struc, inter_H_sublattice1_set2))) self.assertFalse( pdc.are_equal( # different interstitials (wrong sublattice) Interstitial(s_struc, inter_H_sublattice1_set1), Interstitial(s_struc, inter_H_sublattice2))) self.assertFalse( pdc.are_equal( # different interstitials (wrong sublattice) Interstitial(s_struc, inter_H_sublattice1_set1), Interstitial(s_struc, inter_H_sublattice3))) self.assertFalse( pdc.are_equal( # different interstitials (wrong specie) Interstitial(s_struc, inter_H_sublattice3), Interstitial(s_struc, inter_He_sublattice3))) # test non-symmorphic interstitial matching # (using set generated from Voronoi generator, with same sublattice given by saturatated_interstitial_structure function) ns_struc = Structure.from_file(os.path.join(test_dir, "CuCl.cif")) ns_inter_H_sublattice1_set1 = PeriodicSite( 'H', [0.06924513, 0.06308959, 0.86766528], ns_struc.lattice) ns_inter_H_sublattice1_set2 = PeriodicSite( 'H', [0.43691041, 0.36766528, 0.06924513], ns_struc.lattice) ns_inter_H_sublattice2 = PeriodicSite( 'H', [0.06022109, 0.60196031, 0.1621814], ns_struc.lattice) ns_inter_He_sublattice2 = PeriodicSite( 'He', [0.06022109, 0.60196031, 0.1621814], ns_struc.lattice) self.assertTrue( pdc.are_equal( # trivial interstitial test Interstitial(ns_struc, ns_inter_H_sublattice1_set1), Interstitial(ns_struc, ns_inter_H_sublattice1_set1))) self.assertTrue( pdc.are_equal( # same sublattice, different coords Interstitial(ns_struc, ns_inter_H_sublattice1_set1), Interstitial(ns_struc, ns_inter_H_sublattice1_set2))) self.assertFalse( pdc.are_equal( Interstitial(ns_struc, ns_inter_H_sublattice1_set1 ), # different interstitials (wrong sublattice) Interstitial(ns_struc, ns_inter_H_sublattice2))) self.assertFalse( pdc.are_equal( # different interstitials (wrong specie) Interstitial(ns_struc, ns_inter_H_sublattice2), Interstitial(ns_struc, ns_inter_He_sublattice2))) # test influence of charge on defect matching (default is to be charge agnostic) vac_diff_chg = identical_Cs_vacs[0].copy() vac_diff_chg.set_charge(3.) self.assertTrue(pdc.are_equal(identical_Cs_vacs[0], vac_diff_chg)) chargecheck_pdc = PointDefectComparator( check_charge=True) # switch to PDC which cares about charge state self.assertFalse( chargecheck_pdc.are_equal(identical_Cs_vacs[0], vac_diff_chg)) # test different supercell size # (comparing same defect but different supercells - default is to not check for this) sc_agnostic_pdc = PointDefectComparator(check_primitive_cell=True) sc_scaled_s_struc = s_struc.copy() sc_scaled_s_struc.make_supercell([2, 2, 3]) sc_scaled_I_vac_sublatt1_ps1 = PeriodicSite( 'I', identical_I_vacs_sublattice1[0].site.coords, sc_scaled_s_struc.lattice, coords_are_cartesian=True) sc_scaled_I_vac_sublatt1_ps2 = PeriodicSite( 'I', identical_I_vacs_sublattice1[1].site.coords, sc_scaled_s_struc.lattice, coords_are_cartesian=True) sc_scaled_I_vac_sublatt2_ps = PeriodicSite( 'I', identical_I_vacs_sublattice2[1].site.coords, sc_scaled_s_struc.lattice, coords_are_cartesian=True) sc_scaled_I_vac_sublatt1_defect1 = Vacancy( sc_scaled_s_struc, sc_scaled_I_vac_sublatt1_ps1) sc_scaled_I_vac_sublatt1_defect2 = Vacancy( sc_scaled_s_struc, sc_scaled_I_vac_sublatt1_ps2) sc_scaled_I_vac_sublatt2_defect = Vacancy(sc_scaled_s_struc, sc_scaled_I_vac_sublatt2_ps) self.assertFalse( pdc.are_equal( identical_I_vacs_sublattice1[ 0], # trivially same defect site but between different supercells sc_scaled_I_vac_sublatt1_defect1)) self.assertTrue( sc_agnostic_pdc.are_equal(identical_I_vacs_sublattice1[0], sc_scaled_I_vac_sublatt1_defect1)) self.assertFalse( pdc.are_equal( identical_I_vacs_sublattice1[ 1], # same coords, different lattice structure sc_scaled_I_vac_sublatt1_defect1)) self.assertTrue( sc_agnostic_pdc.are_equal(identical_I_vacs_sublattice1[1], sc_scaled_I_vac_sublatt1_defect1)) self.assertFalse( pdc.are_equal( identical_I_vacs_sublattice1[ 0], # same sublattice, different coords sc_scaled_I_vac_sublatt1_defect2)) self.assertTrue( sc_agnostic_pdc.are_equal(identical_I_vacs_sublattice1[0], sc_scaled_I_vac_sublatt1_defect2)) self.assertFalse( sc_agnostic_pdc.are_equal( identical_I_vacs_sublattice1[ 0], # different defects (wrong sublattice) sc_scaled_I_vac_sublatt2_defect)) # test same structure size, but scaled lattice volume # (default is to not allow these to be equal, but check_lattice_scale=True allows for this) vol_agnostic_pdc = PointDefectComparator(check_lattice_scale=True) vol_scaled_s_struc = s_struc.copy() vol_scaled_s_struc.scale_lattice(s_struc.volume * 0.95) vol_scaled_I_vac_sublatt1_defect1 = Vacancy(vol_scaled_s_struc, vol_scaled_s_struc[4]) vol_scaled_I_vac_sublatt1_defect2 = Vacancy(vol_scaled_s_struc, vol_scaled_s_struc[5]) vol_scaled_I_vac_sublatt2_defect = Vacancy(vol_scaled_s_struc, vol_scaled_s_struc[6]) self.assertFalse( pdc.are_equal( identical_I_vacs_sublattice1[ 0], # trivially same defect (but vol change) vol_scaled_I_vac_sublatt1_defect1)) self.assertTrue( vol_agnostic_pdc.are_equal(identical_I_vacs_sublattice1[0], vol_scaled_I_vac_sublatt1_defect1)) self.assertFalse( pdc.are_equal( identical_I_vacs_sublattice1[ 0], # same defect, different sublattice point (and vol change) vol_scaled_I_vac_sublatt1_defect2)) self.assertTrue( vol_agnostic_pdc.are_equal(identical_I_vacs_sublattice1[0], vol_scaled_I_vac_sublatt1_defect2)) self.assertFalse( vol_agnostic_pdc.are_equal( identical_I_vacs_sublattice1[ 0], # different defect (wrong sublattice) vol_scaled_I_vac_sublatt2_defect)) # test identical defect which has had entire lattice shifted shift_s_struc = s_struc.copy() shift_s_struc.translate_sites(range(len(s_struc)), [0.2, 0.3, 0.4], frac_coords=True, to_unit_cell=True) shifted_identical_Cs_vacs = [ Vacancy(shift_s_struc, shift_s_struc[0]), Vacancy(shift_s_struc, shift_s_struc[1]) ] self.assertTrue( pdc.are_equal( identical_Cs_vacs[0], # trivially same defect (but shifted) shifted_identical_Cs_vacs[0])) self.assertTrue( pdc.are_equal( identical_Cs_vacs[ 0], # same defect on different sublattice point (and shifted) shifted_identical_Cs_vacs[1])) # test uniform lattice shift within non-symmorphic structure shift_ns_struc = ns_struc.copy() shift_ns_struc.translate_sites(range(len(ns_struc)), [0., 0.6, 0.3], frac_coords=True, to_unit_cell=True) shift_ns_inter_H_sublattice1_set1 = PeriodicSite( 'H', ns_inter_H_sublattice1_set1.frac_coords + [0., 0.6, 0.3], shift_ns_struc.lattice) shift_ns_inter_H_sublattice1_set2 = PeriodicSite( 'H', ns_inter_H_sublattice1_set2.frac_coords + [0., 0.6, 0.3], shift_ns_struc.lattice) self.assertTrue( pdc.are_equal( Interstitial(ns_struc, ns_inter_H_sublattice1_set1 ), # trivially same defect (but shifted) Interstitial(shift_ns_struc, shift_ns_inter_H_sublattice1_set1))) self.assertTrue( pdc.are_equal( Interstitial(ns_struc, ns_inter_H_sublattice1_set1), # same defect on different sublattice point (and shifted) Interstitial(shift_ns_struc, shift_ns_inter_H_sublattice1_set2))) # test a rotational + supercell type structure transformation (requires check_primitive_cell=True) rotated_s_struc = s_struc.copy() rotated_s_struc.make_supercell([[2, 1, 0], [-1, 3, 0], [0, 0, 2]]) rotated_identical_Cs_vacs = [ Vacancy(rotated_s_struc, rotated_s_struc[0]), Vacancy(rotated_s_struc, rotated_s_struc[1]) ] self.assertFalse( pdc.are_equal( identical_Cs_vacs[0], # trivially same defect (but rotated) rotated_identical_Cs_vacs[0])) self.assertTrue( sc_agnostic_pdc.are_equal(identical_Cs_vacs[0], rotated_identical_Cs_vacs[0])) self.assertFalse( pdc.are_equal( identical_Cs_vacs[ 0], # same defect on different sublattice (and rotated) rotated_identical_Cs_vacs[1])) self.assertTrue( sc_agnostic_pdc.are_equal( identical_Cs_vacs[ 0], # same defect on different sublattice point (and rotated) rotated_identical_Cs_vacs[1])) # test a rotational + supercell + shift type structure transformation for non-symmorphic structure rotANDshift_ns_struc = ns_struc.copy() rotANDshift_ns_struc.translate_sites(range(len(ns_struc)), [0., 0.6, 0.3], frac_coords=True, to_unit_cell=True) rotANDshift_ns_struc.make_supercell([[2, 1, 0], [-1, 3, 0], [0, 0, 2]]) ns_vac_Cs_set1 = Vacancy(ns_struc, ns_struc[0]) rotANDshift_ns_vac_Cs_set1 = Vacancy(rotANDshift_ns_struc, rotANDshift_ns_struc[0]) rotANDshift_ns_vac_Cs_set2 = Vacancy(rotANDshift_ns_struc, rotANDshift_ns_struc[1]) self.assertTrue( sc_agnostic_pdc.are_equal( ns_vac_Cs_set1, # trivially same defect (but rotated and sublattice shifted) rotANDshift_ns_vac_Cs_set1)) self.assertTrue( sc_agnostic_pdc.are_equal( ns_vac_Cs_set1, # same defect on different sublattice point (shifted and rotated) rotANDshift_ns_vac_Cs_set2))
def test_freysoldt(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc vac = Vacancy(struc, struc.sites[0], charge=-3) 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 ] params = { 'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata } 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., 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 and that plot metadata exists pltsaver = [] for ax in range(3): pltsaver.append(fc.plot(axis=ax)) self.assertAlmostEqual(len(pltsaver), 3) #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., 2., 3.], [0., 3., 5.], [4., 10., 8.]]) self.assertAlmostEqual(fc.dielectric, 4.) 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., corrections={}, parameters=params, entry_id=None) val = fc.get_correction(de) self.assertAlmostEqual(val['freysoldt_electrostatic'], 0.) self.assertAlmostEqual(val['freysoldt_potential_alignment'], 0.)
def test_bandfilling(self): v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = 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.) self.assertFalse(bfc.metadata['occupied_def_levels']) self.assertFalse(bfc.metadata['unoccupied_def_levels']) self.assertFalse(bfc.metadata['total_occupation_defect_levels']) 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., corrections={}, parameters=params, entry_id=None) corr = bfc.get_correction(de) self.assertAlmostEqual(corr['bandfilling'], 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 - .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']) #modify the eigenvalue list to have free electrons elec_eigenvalues = {} for spinkey, spinset in eigenvalues.items(): elec_eigenvalues[spinkey] = [] for kptset in spinset: elec_eigenvalues[spinkey].append([]) for eig in kptset: if (eig[0] > cbm) and (eig[0] < cbm + .2): elec_eigenvalues[spinkey][-1].append([eig[0], 0.5]) else: elec_eigenvalues[spinkey][-1].append(eig) elec_bf_corr = bfc.perform_bandfill_corr(elec_eigenvalues, kptweights, potalign, vbm, cbm) self.assertAlmostEqual(elec_bf_corr, -0.0903187572254) self.assertAlmostEqual(bfc.metadata['num_elec_cbm'], 0.8541667349) self.assertFalse(bfc.metadata['num_hole_vbm']) #modify the potalignment and introduce new occupied defect levels from vbm states potalign = -0.1 bf_corr = bfc.perform_bandfill_corr(eigenvalues, kptweights, potalign, vbm, cbm) self.assertAlmostEqual(bfc.metadata['num_hole_vbm'], 0.) self.assertAlmostEqual(bf_corr, 0.) occu = [[1.457, 0.0833333], [1.5204, 0.0833333], [1.53465, 0.0833333], [1.5498, 0.0416667]] self.assertArrayAlmostEqual( list( sorted(bfc.metadata['occupied_def_levels'], key=lambda x: x[0])), list(sorted(occu, key=lambda x: x[0]))) self.assertAlmostEqual(bfc.metadata['total_occupation_defect_levels'], 0.29166669) self.assertFalse(bfc.metadata['unoccupied_def_levels'])
def test_vacancy(self): struc = PymatgenTest.get_structure("VO2") V_index = struc.indices_from_symbol("V")[0] vac = Vacancy(struc, struc[V_index]) # test generation and super cell vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.composition.as_dict(), {"V": 1, "O": 4}) vac_struc = vac.generate_defect_structure(2) self.assertEqual(vac_struc.composition.as_dict(), {"V": 15, "O": 32}) vac_struc = vac.generate_defect_structure(3) self.assertEqual(vac_struc.composition.as_dict(), {"V": 53, "O": 108}) vac_struc = vac.generate_defect_structure([[2., 0, 0], [0, 0, -3.], [0, 2., 0]]) self.assertEqual(vac_struc.composition.as_dict(), {"V": 23, "O": 48}) # test charge vac = Vacancy(struc, struc[V_index]) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 0.0) vac = Vacancy(struc, struc[V_index], charge=1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 1.0) vac = Vacancy(struc, struc[V_index], charge=-1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, -1.0) # test multiplicity vac = Vacancy(struc, struc[V_index]) self.assertEqual(vac.multiplicity, 2) O_index = struc.indices_from_symbol("O")[0] vac = Vacancy(struc, struc[O_index]) self.assertEqual(vac.multiplicity, 4) # Test composition self.assertEqual(dict(vac.defect_composition.as_dict()), {"V": 2, "O": 3}) # test lattice value error occurs for differnet lattices sc_scaled_struc = struc.copy() sc_scaled_struc.make_supercell(2) self.assertRaises( ValueError, Vacancy, struc, sc_scaled_struc[V_index]) self.assertRaises( ValueError, Vacancy, sc_scaled_struc, struc[V_index])
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"]), )
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_vacancy(self): struc = PymatgenTest.get_structure("VO2") V_index = struc.indices_from_symbol("V")[0] vac = Vacancy(struc, struc[V_index]) # test generation and super cell vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.composition.as_dict(), {"V": 1, "O": 4}) vac_struc = vac.generate_defect_structure(2) self.assertEqual(vac_struc.composition.as_dict(), {"V": 15, "O": 32}) vac_struc = vac.generate_defect_structure(3) self.assertEqual(vac_struc.composition.as_dict(), {"V": 53, "O": 108}) # test charge vac = Vacancy(struc, struc[V_index]) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 0.0) vac = Vacancy(struc, struc[V_index], charge=1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, 1.0) vac = Vacancy(struc, struc[V_index], charge=-1.0) vac_struc = vac.generate_defect_structure(1) self.assertEqual(vac_struc.charge, -1.0) # test multiplicity vac = Vacancy(struc, struc[V_index]) self.assertEqual(vac.multiplicity, 2) O_index = struc.indices_from_symbol("O")[0] vac = Vacancy(struc, struc[O_index]) self.assertEqual(vac.multiplicity, 4) # Test composoition self.assertEqual(dict(vac.defect_composition.as_dict()), { "V": 2, "O": 3 })
def vac_antisite_def_struct_gen(mpid, mapi_key, cellmax): if not mpid: print("============\nERROR: Provide an mpid\n============") return # Get primitive structure from the Materials Project DB if not mapi_key: with MPRester() as mp: struct = mp.get_structure_by_material_id(mpid) else: with MPRester(mapi_key) as mp: struct = mp.get_structure_by_material_id(mpid) sga = SpacegroupAnalyzer(struct) prim_struct = sga.find_primitive() #prim_struct_sites = len(prim_struct.sites) #conv_struct = sga.get_conventional_standard_structure() #conv_struct_sites = len(conv_struct.sites) #conv_prim_ratio = int(conv_struct_sites / prim_struct_sites) # Default VASP settings def_vasp_incar_param = { 'ISIF': 2, 'EDIFF': 1e-6, 'EDIFFG': 0.001, } kpoint_den = 15000 # Create bulk structure and associated VASP files sc_scale = get_sc_scale(inp_struct=prim_struct, final_site_no=cellmax) blk_sc = prim_struct.copy() blk_sc.make_supercell(scaling_matrix=sc_scale) site_no = blk_sc.num_sites # Rescale if needed if site_no > cellmax: max_sc_dim = max(sc_scale) i = sc_scale.index(max_sc_dim) sc_scale[i] -= 1 blk_sc = prim_struct.copy() blk_sc.make_supercell(scaling_matrix=sc_scale) blk_str_sites = set(blk_sc.sites) custom_kpoints = Kpoints.automatic_density(blk_sc, kppa=kpoint_den) mpvis = MPMetalRelaxSet(blk_sc, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) ptcr_flag = True try: potcar = mpvis.potcar except: print ("VASP POTCAR folder not detected.\n" \ "Only INCAR, POSCAR, KPOINTS are generated.\n" \ "If you have VASP installed on this system, \n" \ "refer to pymatgen documentation for configuring the settings.") ptcr_flag = False fin_dir = os.path.join(mpid, 'bulk') mpvis.write_input(fin_dir) # Create each defect structure and associated VASP files # First find all unique defect sites periodic_struct = sga.get_symmetrized_structure() unique_sites = list(set([periodic_struct.find_equivalent_sites(site)[0] \ for site in periodic_struct.sites])) temp_struct = Structure.from_sites(sorted(unique_sites)) prim_struct2 = SpacegroupAnalyzer(temp_struct).find_primitive() prim_struct2.lattice = prim_struct.lattice # a little hacky for i, site in enumerate(prim_struct2.sites): vac = Vacancy(structure=prim_struct, defect_site=site) vac_sc = vac.generate_defect_structure(supercell=sc_scale) # Get vacancy site information vac_str_sites = set(vac_sc.sites) vac_sites = blk_str_sites - vac_str_sites vac_site = next(iter(vac_sites)) site_mult = vac.get_multiplicity() vac_site_specie = vac_site.specie vac_symbol = vac_site_specie.symbol custom_kpoints = Kpoints.automatic_density(vac_sc, kppa=kpoint_den) mpvis = MPMetalRelaxSet(vac_sc, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) vac_dir = 'vacancy_{}_mult-{}_sitespecie-{}'.format( str(i + 1), site_mult, vac_symbol) fin_dir = os.path.join(mpid, vac_dir) mpvis.write_input(fin_dir) # Antisites generation at the vacancy site struct_species = blk_sc.species for specie in set(struct_species) - set([vac_site_specie]): specie_symbol = specie.symbol anti_sc = vac_sc.copy() anti_sc.append(specie, vac_site.frac_coords) mpvis = MPMetalRelaxSet(anti_sc, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) anti_dir = 'antisite_{}_mult-{}_sitespecie-{}_subspecie-{}'.format( str(i + 1), site_mult, vac_symbol, specie_symbol) fin_dir = os.path.join(mpid, anti_dir) mpvis.write_input(fin_dir)
class DefectCompatibilityTest(PymatgenTest): def setUp(self): struc = PymatgenTest.get_structure("VO2") struc.make_supercell(3) struc = struc self.vac = Vacancy(struc, struc.sites[0], charge=-3) abc = self.vac.bulk_structure.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 ] self.frey_params = { 'axis_grid': axisdata, 'bulk_planar_averages': bldata, 'defect_planar_averages': dldata, 'dielectric': 15, 'initial_defect_structure': struc.copy(), 'defect_frac_sc_coords': struc.sites[0].frac_coords[:] } kumagai_bulk_struc = Poscar.from_file( os.path.join(test_dir, 'defect', 'CONTCAR_bulk')).structure bulk_out = Outcar(os.path.join(test_dir, 'defect', 'OUTCAR_bulk.gz')) defect_out = Outcar( os.path.join(test_dir, 'defect', 'OUTCAR_vac_Ga_-3.gz')) self.kumagai_vac = Vacancy(kumagai_bulk_struc, kumagai_bulk_struc.sites[0], charge=-3) kumagai_defect_structure = self.kumagai_vac.generate_defect_structure() self.kumagai_params = { '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(kumagai_bulk_struc))], 'defect_frac_sc_coords': [0., 0., 0.], 'initial_defect_structure': kumagai_defect_structure, 'dielectric': 18.118 * np.identity(3), 'gamma': 0.153156 #not neccessary to load gamma, but speeds up unit test } v = Vasprun(os.path.join(test_dir, 'vasprun.xml')) eigenvalues = v.eigenvalues.copy() kptweights = v.actual_kpoints_weights potalign = -0.1 vbm = v.eigenvalue_band_properties[2] cbm = v.eigenvalue_band_properties[1] self.bandfill_params = { 'eigenvalues': eigenvalues, 'kpoint_weights': kptweights, 'potalign': potalign, 'vbm': vbm, 'cbm': cbm } self.band_edge_params = { 'hybrid_cbm': 1., 'hybrid_vbm': -1., 'vbm': -0.5, 'cbm': 0.6, 'num_hole_vbm': 1., 'num_elec_cbm': 1. } def test_process_entry(self): # basic process with no corrections dentry = DefectEntry(self.vac, 0., corrections={}, parameters={ 'vbm': 0., 'cbm': 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'] + .2, 'hybrid_vbm': params['vbm'] - .4, }) dentry = DefectEntry(self.vac, 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'] - .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., 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.) # 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.) self.assertAlmostEqual(dentry.corrections['bandfilling_correction'], 0.) self.assertAlmostEqual(dentry.corrections['charge_correction'], 0.) 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., corrections={}, parameters={}, entry_id=None) dc = DefectCompatibility() dentry = dc.perform_all_corrections(de) self.assertIsNotNone(dentry) #all other correction applications are tested in unit tests below def test_perform_freysoldt(self): de = DefectEntry(self.vac, 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., 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_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.) 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) 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., corrections={}, parameters={}, entry_id=None) dc = DefectCompatibility() dentry = dc.delocalization_analysis(de) self.assertIsNotNone(dentry) #all other correction applications are tested in unit tests below def test_check_freysoldt_delocalized(self): de = DefectEntry(self.vac, 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_check_kumagai_delocalized(self): de = DefectEntry(self.kumagai_vac, 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_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., 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.) self.assertTrue( struc_delocal['metadata']['structure_perc_relax_compatible']) self.assertEqual(struc_delocal['metadata']['perc_relax_outside_rad'], 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.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., 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 list_Defect(list_struc): ls = [] for structure in list_struc: i = Vacancy(structure) ls.append(i.bulk_structure) return ls
q = int( os.path.basename(os.path.dirname(charge_dir)).replace( 'Charged', '')) if os.path.isfile(charge_dir + calc_scheme['vasprun'] + '/vasprun.xml'): # reading vasprun.xml file vasprun = Vasprun(charge_dir + calc_scheme['vasprun'] + '/vasprun.xml') # getting total energy from VASP output total_energies_dict[q] = vasprun.final_energy for q in total_energies_dict: # creating vacancy object vacancy = Vacancy(structure_pure, defect_site, charge=q, multiplicity=1) # including corrections corrections = corrections_init.copy() # if requested calculate freysoldt corrections if include_freysoldt_corrections: path_to_defect_locpot = charge_dir + calc_scheme[ 'locpot'] + '/LOCPOT' freysoldt_corrections = get_freysoldt_correction( 'vacancy', vacancy_type, path_to_defect_locpot, path_to_pure_locpot, q,
def vac_antisite_def_struct_gen(mpid, mapi_key, cellmax, struct_file=None): if not mpid and not struct_file: print ("============\nERROR: Provide an mpid\n============") return # Get primitive structure from the Materials Project DB if not struct_file: if not mapi_key: with MPRester() as mp: struct = mp.get_structure_by_material_id(mpid) else: with MPRester(mapi_key) as mp: struct = mp.get_structure_by_material_id(mpid) else: struct = Structure.from_file(struct_file) sga = SpacegroupAnalyzer(struct) prim_struct = sga.find_primitive() #prim_struct_sites = len(prim_struct.sites) #conv_struct = sga.get_conventional_standard_structure() #conv_struct_sites = len(conv_struct.sites) #conv_prim_ratio = int(conv_struct_sites / prim_struct_sites) # Default VASP settings def_vasp_incar_param = {'ISIF':2, 'EDIFF':1e-6, 'EDIFFG':0.001,} kpoint_den = 15000 # Create bulk structure and associated VASP files sc_scale = get_sc_scale(inp_struct=prim_struct, final_site_no=cellmax) blk_sc = prim_struct.copy() blk_sc.make_supercell(scaling_matrix=sc_scale) site_no = blk_sc.num_sites # Rescale if needed while site_no > cellmax: max_sc_dim = max(sc_scale) i = sc_scale.index(max_sc_dim) sc_scale[i] -= 1 blk_sc = prim_struct.copy() blk_sc.make_supercell(scaling_matrix=sc_scale) site_no = blk_sc.num_sites blk_str_sites = set(blk_sc.sites) custom_kpoints = Kpoints.automatic_density(blk_sc, kppa=kpoint_den) mpvis = MPMetalRelaxSet(blk_sc, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) if mpid: root_fldr = mpid else: root_fldr = struct.composition.reduced_formula fin_dir = os.path.join(root_fldr, 'bulk') mpvis.write_input(fin_dir) if not mpid: # write the input structure if mpid is not used struct.to(fmt='poscar', filename=os.path.join(fin_dir, 'POSCAR.uc')) # Create each defect structure and associated VASP files # First find all unique defect sites periodic_struct = sga.get_symmetrized_structure() unique_sites = list(set([periodic_struct.find_equivalent_sites(site)[0] \ for site in periodic_struct.sites])) temp_struct = Structure.from_sites(sorted(unique_sites)) prim_struct2 = SpacegroupAnalyzer(temp_struct).find_primitive() prim_struct2.lattice = prim_struct.lattice # a little hacky for i, site in enumerate(prim_struct2.sites): vac = Vacancy(structure=prim_struct, defect_site=site) vac_sc = vac.generate_defect_structure(supercell=sc_scale) # Get vacancy site information vac_str_sites = set(vac_sc.sites) vac_sites = blk_str_sites - vac_str_sites vac_site = next(iter(vac_sites)) site_mult = vac.get_multiplicity() vac_site_specie = vac_site.specie vac_symbol = vac_site_specie.symbol custom_kpoints = Kpoints.automatic_density(vac_sc, kppa=kpoint_den) mpvis = MPMetalRelaxSet(vac_sc, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) vac_dir = 'vacancy_{}_mult-{}_sitespecie-{}'.format( str(i+1), site_mult, vac_symbol) fin_dir = os.path.join(root_fldr, vac_dir) mpvis.write_input(fin_dir) # Antisites generation at the vacancy site struct_species = blk_sc.species for specie in set(struct_species) - set([vac_site_specie]): specie_symbol = specie.symbol anti_sc = vac_sc.copy() anti_sc.append(specie, vac_site.frac_coords) mpvis = MPMetalRelaxSet(anti_sc, user_incar_settings=def_vasp_incar_param, user_kpoints_settings=custom_kpoints) anti_dir = 'antisite_{}_mult-{}_sitespecie-{}_subspecie-{}'.format( str(i+1), site_mult, vac_symbol, specie_symbol) fin_dir = os.path.join(root_fldr, anti_dir) mpvis.write_input(fin_dir)
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., 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 ] 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., 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., 2., 3.], [0., 3., 5.], [4., 10., 8.]]) self.assertAlmostEqual(fc.dielectric, 4.) 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., corrections={}, parameters=params, entry_id=None) val = fc.get_correction(de) self.assertAlmostEqual(val['freysoldt_electrostatic'], 0.) self.assertAlmostEqual(val['freysoldt_potential_alignment'], 0.)