def similar_defects(entryset): """ Used for grouping similar defects of different charges Can distinguish identical defects even if they are not in same position """ pdc = PointDefectComparator(check_charge=False, check_primitive_cell=True, check_lattice_scale=False) grp_def_sets = [] grp_def_indices = [] for ent_ind, ent in enumerate(entryset): # TODO: more pythonic way of grouping entry sets with PointDefectComparator. # this is currently most time intensive part of DefectPhaseDiagram matched_ind = None for grp_ind, defgrp in enumerate(grp_def_sets): if pdc.are_equal(ent.defect, defgrp[0].defect): matched_ind = grp_ind break if matched_ind is not None: grp_def_sets[matched_ind].append(ent.copy()) grp_def_indices[matched_ind].append(ent_ind) else: grp_def_sets.append([ent.copy()]) grp_def_indices.append([ent_ind]) return zip(grp_def_sets, grp_def_indices)
def __init__(self, structure, element): """ Initializes an Interstitial generator using structure motifs Args: structure (Structure): pymatgen structure object element (str or Element or Species): element for the interstitial """ self.structure = structure self.element = element interstitial_finder = StructureMotifInterstitial( self.structure, self.element) self.unique_defect_seq = [] # eliminate sublattice equivalent defects which may # have slipped through interstitial finder pdc = PointDefectComparator() for poss_site in interstitial_finder.enumerate_defectsites(): now_defect = Interstitial(self.structure, poss_site) append_defect = True for unique_defect in self.unique_defect_seq: if pdc.are_equal(now_defect, unique_defect): append_defect = False if append_defect: self.unique_defect_seq.append(now_defect) self.count_def = 0 # for counting the index of the generated defect
def __init__(self, structure, element): """ Initializes an Interstitial generator using Voronoi sites Args: structure (Structure): pymatgen structure object element (str or Element or Species): element for the interstitial """ self.structure = structure self.element = element framework = list(self.structure.symbol_set) get_voronoi = TopographyAnalyzer(self.structure, framework, [], check_volume=False) get_voronoi.cluster_nodes() get_voronoi.remove_collisions() # trim equivalent nodes with symmetry analysis struct_to_trim = self.structure.copy() for poss_inter in get_voronoi.vnodes: struct_to_trim.append(self.element, poss_inter.frac_coords, coords_are_cartesian=False) symmetry_finder = SpacegroupAnalyzer(struct_to_trim, symprec=1e-1) equiv_sites_list = symmetry_finder.get_symmetrized_structure( ).equivalent_sites # do additional screening for sublattice equivalent # defects which may have slipped through pdc = PointDefectComparator() self.unique_defect_seq = [] for poss_site_list in equiv_sites_list: poss_site = poss_site_list[0] if poss_site not in self.structure: now_defect = Interstitial(self.structure, poss_site) append_defect = True for unique_defect in self.unique_defect_seq: if pdc.are_equal(now_defect, unique_defect): append_defect = False if append_defect: self.unique_defect_seq.append(now_defect) self.count_def = 0 # for counting the index of the generated defect
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))