def get_freysoldt_correction(defect_type, defect_specie, path_to_defect_locpot,path_to_pure_locpot,charge, dielectric_constant,defect_site_coordinates,energy_cutoff=500,get_plot=False): ''' Function to perform charge corrections according to the method proposed py Freysoldt If this correction is used, please reference Freysoldt's original paper. doi: 10.1103/PhysRevLett.102.016402 Args: defect_type: 'vacancy' or 'interstitial' defect_specie: string with element occupying the defect site path_to_defect_locpot: path to LOCPOT file of defect structure path_to_pure_locpot: path to LOCPOT file of Pure structure charge: Charge of the defected system dielectric_constant: Dielectric constant defect_site_coordinates: numpy array with fractional coordinates of defect site energy_cutoff: Cut-off of plane wave expansion get_plot: return also Matplotlib object with plot Returns: Freysoldt corrections values as a dictionary ''' # acquiring data from LOCPOT files locpot_pure = Locpot.from_file(path_to_pure_locpot) vol_data_pure = VolumetricData(locpot_pure.structure,locpot_pure.data) locpot_defect = Locpot.from_file(path_to_defect_locpot) vol_data_defect = VolumetricData(locpot_defect.structure,locpot_defect.data) parameters = {} parameters['axis_grid'] = [] parameters['bulk_planar_averages'] = [] parameters['defect_planar_averages'] = [] for i in range(0,3): parameters['axis_grid'].append(vol_data_pure.get_axis_grid(i)) parameters['bulk_planar_averages'].append(vol_data_pure.get_average_along_axis(i)) parameters['defect_planar_averages'].append(vol_data_defect.get_average_along_axis(i)) parameters['initial_defect_structure'] = locpot_defect.structure parameters['defect_frac_sc_coords'] = defect_site_coordinates structure_bulk = locpot_pure.structure defect_site = PeriodicSite(defect_specie, coords=defect_site_coordinates, lattice = locpot_pure.structure.lattice) module = importlib.import_module("pymatgen.analysis.defects.core") defect_class = getattr(module,defect_type) defect = defect_class(structure_bulk, defect_site, charge=charge, multiplicity=None) defect_entry = DefectEntry(defect,None,corrections=None,parameters=parameters) freysoldt_class = FreysoldtCorrection(dielectric_constant,energy_cutoff=energy_cutoff) freysoldt_corrections = freysoldt_class.get_correction(defect_entry) if get_plot: plt = freysoldt_class.plot(1) return freysoldt_corrections , plt else: return freysoldt_corrections
def test_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_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_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 get_correction_freysoldt(defect_entry, epsilon, title=None, partflag='All', axis=None): """ Function to compute the isotropic freysoldt correction for each defect. If this correction is used, please reference Freysoldt's original paper. doi: 10.1103/PhysRevLett.102.016402 Args: defect_entry: DefectEntry object with the following keys stored in defect.parameters: required: axis_grid (3 x NGX where NGX is the length of the NGX grid in the x,y and z axis directions. Same length as planar average lists): A list of 3 numpy arrays which contain the cartesian axis values (in angstroms) that correspond to each planar avg potential supplied. bulk_planar_averages (3 x NGX where NGX is the length of the NGX grid in the x,y and z axis directions.): A list of 3 numpy arrays which contain the planar averaged electrostatic potential for the bulk supercell. defect_planar_averages (3 x NGX where NGX is the length of the NGX grid in the x,y and z axis directions.): A list of 3 numpy arrays which contain the planar averaged electrostatic potential for the defective supercell. bulk_sc_structure (Structure) bulk structure corresponding to defect supercell structure (uses Lattice for charge correction) defect_frac_sc_coords (3 x 1 array) Fracitional co-ordinates of defect location in supercell structure optional: 'encut' : energy cutoff desired for Freysoldt correction 'madetol' : madelung tolerance for Freysoldt correction 'q_model' : Charge Model for Freysoldt correction 'q_model' : Charge Model for Freysoldt correction 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]) axis (int or None): if integer, then freysoldt correction is performed on the single axis. If it is None, then averaging of the corrections for the three axes is used for the correction. Returns Correction """ if partflag not in ['All', 'AllSplit', 'pc', 'potalign']: print( '{} is incorrect potalign type. Must be "All", "AllSplit", "pc", or ' '"potalign".'.format(partflag)) return q_model = defect_entry.parameters.get('q_model', None) encut = defect_entry.parameters.get('encut', 520) madetol = defect_entry.parameters.get('madetol', 0.0001) if not defect_entry.charge: print('charge is zero so charge correction is zero') return 0. template_defect = defect_entry.copy() corr_class = FreysoldtCorrection(epsilon, q_model=q_model, energy_cutoff=encut, madetol=madetol, axis=axis) f_corr_summ = corr_class.get_correction(template_defect) if title: if axis is None: ax_list = [[k, "axis" + str(k)] for k in corr_class.metadata["pot_plot_data"].keys()] else: ax_list = [[axis, "axis" + str(axis + 1)]] for ax_key, ax_title in ax_list: p = corr_class.plot(ax_key, title=ax_title, saved=False) p.savefig(title + '_' + ax_title + '_freysoldtplot.pdf', bbox_inches='tight') if partflag in ['AllSplit', 'All']: freyval = np.sum(list(f_corr_summ.values())) elif partflag == 'pc': freyval = f_corr_summ['freysoldt_electrostatic'] elif partflag == 'potalign': freyval = f_corr_summ['freysoldt_potential_alignment'] print('\n Final Freysoldt correction is {}'.format(freyval)) if partflag == 'AllSplit': freyval = [ f_corr_summ['freysoldt_electrostatic'], f_corr_summ['freysoldt_potential_alignment'], freyval ] return freyval