Example #1
0
    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
Example #2
0
        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 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)
Example #4
0
    def __init__(self, structure, element):
        """
        Initializes an Interstitial generator using structure motifs
        Args:
            structure (Structure): pymatgen structure object
            element (str or Element or Specie): 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
Example #5
0
    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
Example #6
0
    def __init__(self, structure, element):
        """
        Initializes an Interstitial generator using Voronoi sites
        Args:
            structure (Structure): pymatgen structure object
            element (str or Element or Specie): 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
Example #7
0
    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_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))