def perform_es_corr(self, gamma, prec, lattice, charge): """ Peform Electrostatic Kumagai Correction Args: gamma (float): Ewald parameter prec (int): Precision parameter for reciprical/real lattice vector generation lattice: Pymatgen Lattice object corresponding to defect supercell charge (int): Defect charge Return: Electrostatic Point Charge contribution to Kumagai Correction (float) """ volume = lattice.volume g_vecs, recip_summation, r_vecs, real_summation = generate_R_and_G_vecs( gamma, [prec], lattice, self.dielectric) recip_summation = recip_summation[0] real_summation = real_summation[0] es_corr = (recip_summation + real_summation + self.get_potential_shift(gamma, volume) + self.get_self_interaction(gamma)) es_corr *= -(charge**2.) * kumagai_to_V / 2. # [eV] return es_corr
def test_generate_R_and_G_vecs(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]]) epsilon = 10.0 * np.identity(3) g_vecs, recip_summation, r_vecs, real_summation = generate_R_and_G_vecs(gamma, prec, lattice, epsilon) self.assertEqual(len(g_vecs[0]), 16418) self.assertAlmostEqual(recip_summation[0], 2.8946556e-15) self.assertEqual(len(r_vecs[0]), 16299) self.assertAlmostEqual(real_summation[0], 0.00679361)
def test_generate_R_and_G_vecs(self): gamma = 0.19357221 prec = 28 lattice = Lattice( [[ 4.692882, -8.12831 , 0.], [ 4.692882, 8.12831 , 0.], [ 0., 0., 10.03391 ]]) epsilon = 10. * np.identity(3) g_vecs, recip_summation, r_vecs, real_summation = generate_R_and_G_vecs( gamma, prec, lattice, epsilon) self.assertEqual(len(g_vecs[0]), 16418) self.assertAlmostEqual(recip_summation[0], 2.8946556e-15) self.assertEqual(len(r_vecs[0]), 16299) self.assertAlmostEqual(real_summation[0], 0.00679361)
def get_correction(self, entry): """ Gets the Kumagai correction for a defect entry Args: entry (DefectEntry): defect entry to compute Kumagai correction on. Requires following parameters in the DefectEntry to exist: 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 Returns: KumagaiCorrection values as a dictionary """ bulk_atomic_site_averages = entry.parameters[ "bulk_atomic_site_averages"] defect_atomic_site_averages = entry.parameters[ "defect_atomic_site_averages"] site_matching_indices = entry.parameters["site_matching_indices"] defect_sc_structure = entry.parameters["initial_defect_structure"] defect_frac_sc_coords = entry.parameters["defect_frac_sc_coords"] lattice = defect_sc_structure.lattice volume = lattice.volume q = entry.defect.charge if not self.metadata["gamma"]: self.metadata["gamma"] = tune_for_gamma(lattice, self.dielectric) prec_set = [25, 28] g_vecs, recip_summation, r_vecs, real_summation = generate_R_and_G_vecs( self.metadata["gamma"], prec_set, lattice, self.dielectric) pot_shift = self.get_potential_shift(self.metadata["gamma"], volume) si = self.get_self_interaction(self.metadata["gamma"]) es_corr = [ (real_summation[ind] + recip_summation[ind] + pot_shift + si) for ind in range(2) ] # increase precision if correction is not converged yet # TODO: allow for larger prec_set to be tried if this fails if abs(es_corr[0] - es_corr[1]) > 0.0001: logger.debug( "Es_corr summation not converged! ({} vs. {})\nTrying a larger prec_set..." .format(es_corr[0], es_corr[1])) prec_set = [30, 35] g_vecs, recip_summation, r_vecs, real_summation = generate_R_and_G_vecs( self.metadata["gamma"], prec_set, lattice, self.dielectric) es_corr = [ (real_summation[ind] + recip_summation[ind] + pot_shift + si) for ind in range(2) ] if abs(es_corr[0] - es_corr[1]) < 0.0001: raise ValueError( "Correction still not converged after trying prec_sets up to 35... serious error." ) es_corr = es_corr[0] * -(q**2.) * kumagai_to_V / 2. # [eV] # if no sampling radius specified for pot align, then assuming Wigner-Seitz radius: if not self.metadata["sampling_radius"]: wz = lattice.get_wigner_seitz_cell() dist = [] for facet in wz: midpt = np.mean(np.array(facet), axis=0) dist.append(np.linalg.norm(midpt)) self.metadata["sampling_radius"] = min(dist) # assemble site_list based on matching indices # [[defect_site object, Vqb for site], .. repeat for all non defective sites] site_list = [] for bs_ind, ds_ind in site_matching_indices: Vqb = -(defect_atomic_site_averages[int(ds_ind)] - bulk_atomic_site_averages[int(bs_ind)]) site_list.append([defect_sc_structure[int(ds_ind)], Vqb]) pot_corr = self.perform_pot_corr(defect_sc_structure, defect_frac_sc_coords, site_list, self.metadata["sampling_radius"], q, r_vecs[0], g_vecs[0], self.metadata["gamma"]) entry.parameters["kumagai_meta"] = dict(self.metadata) entry.parameters["potalign"] = pot_corr / (-q) if q else 0. return { "kumagai_electrostatic": es_corr, "kumagai_potential_alignment": pot_corr }
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_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']))