def perform_kumagai(self, defect_entry): """ Perform Kumagai correction. Args: defect_entry (DefectEntry): Defect to correct. Returns: Corrected DefectEntry """ gamma = defect_entry.parameters[ 'gamma'] if 'gamma' in defect_entry.parameters.keys() else None sampling_radius = defect_entry.parameters[ 'sampling_radius'] if 'sampling_radius' in defect_entry.parameters.keys( ) else None KC = KumagaiCorrection(defect_entry.parameters['dielectric'], sampling_radius=sampling_radius, gamma=gamma) kumagaicorr = KC.get_correction(defect_entry) kumagai_meta = {k: v for k, v in KC.metadata.items()} kumagai_meta["kumagai_potalign"] = defect_entry.parameters["potalign"] kumagai_meta["kumagai_electrostatic"] = kumagaicorr[ "kumagai_electrostatic"] kumagai_meta["kumagai_potential_alignment_correction"] = kumagaicorr[ "kumagai_potential_alignment"] defect_entry.parameters.update({'kumagai_meta': kumagai_meta}) return defect_entry
def perform_kumagai(defect_entry): """ Perform Kumagai correction. Args: defect_entry (DefectEntry): Defect to correct. Returns: Corrected DefectEntry """ gamma = defect_entry.parameters[ "gamma"] if "gamma" in defect_entry.parameters.keys() else None sampling_radius = (defect_entry.parameters["sampling_radius"] if "sampling_radius" in defect_entry.parameters.keys() else None) KC = KumagaiCorrection( defect_entry.parameters["dielectric"], sampling_radius=sampling_radius, gamma=gamma, ) kumagaicorr = KC.get_correction(defect_entry) kumagai_meta = dict(KC.metadata.items()) kumagai_meta["kumagai_potalign"] = defect_entry.parameters["potalign"] kumagai_meta["kumagai_electrostatic"] = kumagaicorr[ "kumagai_electrostatic"] kumagai_meta["kumagai_potential_alignment_correction"] = kumagaicorr[ "kumagai_potential_alignment"] defect_entry.parameters.update({"kumagai_meta": kumagai_meta}) return defect_entry
def perform_kumagai(self, defect_entry): gamma = defect_entry.parameters['gamma'] if 'gamma' in defect_entry.parameters.keys() else None sampling_radius = defect_entry.parameters['sampling_radius'] if 'sampling_radius' in defect_entry.parameters.keys() else None KC = KumagaiCorrection(defect_entry.parameters['dielectric'], sampling_radius=sampling_radius, gamma=gamma) kumagaicorr = KC.get_correction(defect_entry) kumagai_meta = {k: v for k, v in KC.metadata.items()} kumagai_meta["kumagai_potalign"] = defect_entry.parameters["potalign"] kumagai_meta["kumagai_electrostatic"] = kumagaicorr["kumagai_electrostatic"] kumagai_meta["kumagai_potential_alignment_correction"] = kumagaicorr["kumagai_potential_alignment"] defect_entry.parameters.update({'kumagai_meta': kumagai_meta}) return defect_entry
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 get_correction_kumagai(defect_entry, epsilon, title=None, partflag='All'): """ Function to compute the Kumagai correction for each defect (modified freysoldt for anisotropic dielectric). NOTE that bulk_init class must be pre-instantiated to use this function Args: defect_entry: DefectEntry object with the following keys stored in defect.parameters: required: bulk_atomic_site_averages (list): list of bulk structure"s atomic site averaged ESPs * charge, in same order as indices of bulk structure note this is list given by VASP's OUTCAR (so it is multiplied by a test charge of -1) defect_atomic_site_averages (list): list of defect structure"s atomic site averaged ESPs * charge, in same order as indices of defect structure note this is list given by VASP's OUTCAR (so it is multiplied by a test charge of -1) site_matching_indices (list): list of corresponding site index values for bulk and defect site structures EXCLUDING the defect site itself (ex. [[bulk structure site index, defect structure"s corresponding site index], ... ] initial_defect_structure (Structure): Pymatgen Structure object representing un-relaxed defect structure defect_frac_sc_coords (array): Defect Position in fractional coordinates of the supercell given in bulk_structure optional: gamma (float): Ewald parameter, Default is to determine it based on convergence of brute summation tolerance sampling_radius (float):r adius (in Angstrom) which sites must be outside of to be included in the correction. Publication by Kumagai advises to use Wigner-Seitz radius of defect supercell, so this is default value. epsilon (float or 3x3 matrix): Dielectric constant for the structure title: decides whether to plot electrostatic potential plots or not... if None, no plot is printed, if a string, then the plot will be saved using the string partflag: four options for correction output: 'pc' for just point charge correction, or 'potalign' for just potalign correction, or 'All' for both (added together), or 'AllSplit' for individual parts split up (form is [PC, potterm, full]) """ if partflag not in ['All', 'AllSplit', 'pc', 'potalign']: print( '{} is incorrect potalign type. Must be "All", "AllSplit", "pc", or ' '"potalign".'.format(partflag)) return sampling_radius = defect_entry.parameters.get('sampling_radius', None) gamma = defect_entry.parameters.get('gamma', None) if not defect_entry.charge: print('charge is zero so charge correction is zero') return 0. template_defect = defect_entry.copy() corr_class = KumagaiCorrection(epsilon, sampling_radius=sampling_radius, gamma=gamma) k_corr_summ = corr_class.get_correction(template_defect) if title: p = corr_class.plot(title="Kumagai", saved=False) p.savefig(title + '_kumagaiplot.pdf', bbox_inches='tight') if partflag in ['AllSplit', 'All']: kumagai_val = np.sum(list(k_corr_summ.values())) elif partflag == 'pc': kumagai_val = k_corr_summ['kumagai_electrostatic'] elif partflag == 'potalign': kumagai_val = k_corr_summ['kumagai_potential_alignment'] print('\n Final Kumagai correction is {}'.format(kumagai_val)) if partflag == 'AllSplit': kumagai_val = [ k_corr_summ['kumagai_electrostatic'], k_corr_summ['kumagai_potential_alignment'], kumagai_val ] return kumagai_val
def get_kumagai_correction(structure_defect,structure_bulk,path_to_defect_outcar,path_to_bulk_outcar,dielectric_tensor, charge,defect_type=None,defect_specie=None,defect_site=None,sampling_radius=None,gamma=None, get_plot=False): """ Get Kumagai correction with Pymatgen. Parameters ---------- structure_defect : (Structure) Structure of defect. structure_bulk : (Structure) Bulk structure. path_to_defect_outcar : (str) Path to OUTCAR of defect calculation. path_to_bulk_outcar : (str) Path to OUTCAR of pure calculation. dielectric_tensor : (array or float) Dielectric tensor, if is a float a diagonal matrix is constructed. charge : (int or float) Charge of the defect. defect_type : (str), optional Type of defect ('Vacancy','Interstitial' or 'Substitution') If None it's determined with defect_finder. The default is None. defect_specie : (str), optional Symbol of the defect specie. If None it's determined with defect_finder. The default is None. defect_site : (Site), optional Site of defect. If None it's determined with defect_finder. The default is None. sampling_radius (float): radius (in Angstrom) which sites must be outside of to be included in the correction. Publication by Kumagai advises to use Wigner-Seitz radius of defect supercell, so this is default value. gamma (float): convergence parameter for gamma function. Code will automatically determine this if set to None. get_plot : (bool), optional Get Matplotlib object with plot. The default is False. Returns ------- corr : (dict or tuple) Dictionary with corrections, if get_plot is True a tuple with dict and plt object is returned. """ if not defect_site and not defect_type and not defect_specie: defect_site, defect_type = defect_finder(structure_defect, structure_bulk) defect_specie = defect_site.specie.symbol site_matching_indices = [] for site in structure_defect: site_in_str ,index_bulk = is_site_in_structure(site, structure_bulk) if site_in_str: site_matching_indices.append([index_bulk,structure_defect.index(site)]) else: print(f'Warning in Kumagai corrections: Site {site} is not in bulk structure') bulk_atomic_site_averages = Outcar(op.join(path_to_bulk_outcar,'OUTCAR')).read_avg_core_poten()[-1] defect_atomic_site_averages = Outcar(op.join(path_to_defect_outcar,'OUTCAR')).read_avg_core_poten()[0] defect_frac_sc_coords = defect_site.frac_coords initial_defect_structure = structure_defect parameters = {} parameters['bulk_atomic_site_averages'] = bulk_atomic_site_averages parameters['defect_atomic_site_averages'] = defect_atomic_site_averages parameters['site_matching_indices'] = site_matching_indices parameters['initial_defect_structure'] = initial_defect_structure parameters['defect_frac_sc_coords'] = defect_frac_sc_coords module = importlib.import_module("pymatgen.analysis.defects.core") defect_class = getattr(module,defect_type) defect = defect_class(structure_bulk, defect_site, charge=charge, multiplicity=None) defect_entry = DefectEntry(defect,None,corrections=None,parameters=parameters) kumagai = KumagaiCorrection(dielectric_tensor,sampling_radius,gamma) kumagai_corrections = kumagai.get_correction(defect_entry) if get_plot: plt = kumagai.plot() return kumagai_corrections , plt else: return kumagai_corrections
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']))