def apply_transformation(self, structure):
     species_map = {}
     for k, v in self._species_map.items():
         if isinstance(v, dict):
             value = {smart_element_or_specie(x): y for x, y in v.items()}
         else:
             value = smart_element_or_specie(v)
         species_map[smart_element_or_specie(k)] = value
     editor = StructureEditor(structure)
     editor.replace_species(species_map)
     return editor.modified_structure
class StructureEditorTest(unittest.TestCase):

    def setUp(self):

        self.si = Element("Si")
        self.fe = Element("Fe")
        self.ge = Element("Ge")
        coords = list()
        coords.append(np.array([0, 0, 0]))
        coords.append(np.array([0.75, 0.5, 0.75]))
        lattice = Lattice.cubic(10)
        s = Structure(lattice, [self.si, self.fe], coords)
        self.modifier = StructureEditor(s)

    def test_translate_sites(self):
        self.modifier.translate_sites([0, 1], [0.5, 0.5, 0.5], frac_coords=True)
        self.assertTrue(np.array_equal(self.modifier.modified_structure.frac_coords[0], np.array([ 0.5, 0.5, 0.5])))

        self.modifier.translate_sites([0], [0.5, 0.5, 0.5], frac_coords=False)
        self.assertTrue(np.array_equal(self.modifier.modified_structure.cart_coords[0], np.array([ 5.5, 5.5, 5.5])))

    def test_append_site(self):
        self.modifier.append_site(self.si, [0, 0.5, 0])
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si2", "Wrong formula!")
        self.assertRaises(ValueError, self.modifier.append_site, self.si, np.array([0, 0.5, 0]))

    def test_modified_structure(self):
        self.modifier.insert_site(1, self.si, [0, 0.25, 0])
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si2", "Wrong formula!")

        self.modifier.delete_site(0)
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si1", "Wrong formula!")

        self.modifier.replace_site(0, self.ge)
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Ge1", "Wrong formula!")

        self.modifier.append_site(self.si, [0, 0.75, 0])
        self.modifier.replace_species({self.si: self.ge})
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Ge2", "Wrong formula!")

        self.modifier.replace_species({self.ge: {self.ge:0.5, self.si:0.5}})
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si1 Ge1", "Wrong formula!")

        #this should change the .5Si .5Ge sites to .75Si .25Ge
        self.modifier.replace_species({self.ge: {self.ge:0.5, self.si:0.5}})
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si1.5 Ge0.5", "Wrong formula!")

        d = 0.1
        pre_perturbation_sites = self.modifier.modified_structure.sites
        self.modifier.perturb_structure(distance=d)
        post_perturbation_sites = self.modifier.modified_structure.sites

        for i, x in enumerate(pre_perturbation_sites):
            self.assertAlmostEqual(x.distance(post_perturbation_sites[i]), d, 3, "Bad perturbation distance")

    def test_add_site_property(self):
        self.modifier.add_site_property("charge", [4.1, 5])
        s = self.modifier.modified_structure
        self.assertEqual(s[0].charge, 4.1)
        self.assertEqual(s[1].charge, 5)

        #test adding multiple properties.
        mod2 = StructureEditor(s)
        mod2.add_site_property("magmom", [3, 2])
        s = mod2.modified_structure
        self.assertEqual(s[0].charge, 4.1)
        self.assertEqual(s[0].magmom, 3)
class StructureEditorTest(unittest.TestCase):

    def setUp(self):
        self.si = Element("Si")
        self.fe = Element("Fe")
        self.ge = Element("Ge")
        coords = list()
        coords.append(np.array([0, 0, 0]))
        coords.append(np.array([0.75, 0.5, 0.75]))
        lattice = Lattice.cubic(10)
        s = Structure(lattice, ["Si", "Fe"], coords)
        self.modifier = StructureEditor(s)

    def test_to_unit_cell(self):
        self.modifier.append_site(self.fe, [1.75, 0.5, 0.75],
                                  validate_proximity=False)
        self.modifier.to_unit_cell()
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si1",
                         "Wrong formula!")

    def test_to_unit_cell(self):
        self.modifier.apply_strain(0.01)
        self.assertEqual(self.modifier.modified_structure.lattice.abc,
                         (10.1, 10.1, 10.1))

    def test_translate_sites(self):
        self.modifier.translate_sites([0, 1], [0.5, 0.5, 0.5],
                                      frac_coords=True)
        self.assertTrue(np.array_equal(self.modifier.modified_structure
                                       .frac_coords[0],
                                       np.array([0.5, 0.5, 0.5])))

        self.modifier.translate_sites([0], [0.5, 0.5, 0.5], frac_coords=False)
        self.assertTrue(np.array_equal(self.modifier.modified_structure
                                       .cart_coords[0],
                                       np.array([5.5, 5.5, 5.5])))

    def test_append_site(self):
        self.modifier.append_site(self.si, [0, 0.5, 0])
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si2",
                         "Wrong formula!")
        self.assertRaises(ValueError, self.modifier.append_site, self.si,
                          np.array([0, 0.5, 0]))

    def test_modified_structure(self):
        self.modifier.insert_site(1, self.si, [0, 0.25, 0])
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si2",
                         "Wrong formula!")

        self.modifier.delete_site(0)
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Si1",
                         "Wrong formula!")

        self.modifier.replace_site(0, self.ge)
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Ge1",
                         "Wrong formula!")

        self.modifier.append_site(self.si, [0, 0.75, 0])
        self.modifier.replace_species({self.si: self.ge})
        self.assertEqual(self.modifier.modified_structure.formula, "Fe1 Ge2",
                         "Wrong formula!")

        self.modifier.replace_species({self.ge: {self.ge: 0.5, self.si: 0.5}})
        self.assertEqual(self.modifier.modified_structure.formula,
                         "Fe1 Si1 Ge1", "Wrong formula!")

        #this should change the .5Si .5Ge sites to .75Si .25Ge
        self.modifier.replace_species({self.ge: {self.ge: 0.5, self.si: 0.5}})
        self.assertEqual(self.modifier.modified_structure.formula,
                         "Fe1 Si1.5 Ge0.5", "Wrong formula!")

        d = 0.1
        pre_perturbation_sites = self.modifier.modified_structure.sites
        self.modifier.perturb_structure(distance=d)
        post_perturbation_sites = self.modifier.modified_structure.sites

        for i, x in enumerate(pre_perturbation_sites):
            self.assertAlmostEqual(x.distance(post_perturbation_sites[i]), d,
                                   3, "Bad perturbation distance")

    def test_add_site_property(self):
        self.modifier.add_site_property("charge", [4.1, 5])
        s = self.modifier.modified_structure
        self.assertEqual(s[0].charge, 4.1)
        self.assertEqual(s[1].charge, 5)

        #test adding multiple properties.
        mod2 = StructureEditor(s)
        mod2.add_site_property("magmom", [3, 2])
        s = mod2.modified_structure
        self.assertEqual(s[0].charge, 4.1)
        self.assertEqual(s[0].magmom, 3)

    def test_add_oxidation_states(self):
        si = Element("Si")
        fe = Element("Fe")
        coords = list()
        coords.append([0, 0, 0])
        coords.append([0.75, 0.5, 0.75])
        lattice = Lattice.cubic(10)
        s = Structure(lattice, [si, fe], coords)
        oxidation_states = {"Fe": 2, "Si": -4}
        mod = StructureEditor(s)
        mod.add_oxidation_state_by_element(oxidation_states)
        mod_s = mod.modified_structure
        for site in mod_s:
            for k in site.species_and_occu.keys():
                self.assertEqual(k.oxi_state, oxidation_states[k.symbol],
                                 "Wrong oxidation state assigned!")
        oxidation_states = {"Fe": 2}
        self.assertRaises(ValueError, mod.add_oxidation_state_by_element,
                          oxidation_states)
        mod.add_oxidation_state_by_site([2, -4])
        mod_s = mod.modified_structure
        self.assertEqual(mod_s[0].specie.oxi_state, 2)
        self.assertRaises(ValueError, mod.add_oxidation_state_by_site,
                          [1])

    def test_remove_oxidation_states(self):
        co_elem = Element("Co")
        o_elem = Element("O")
        co_specie = Specie("Co", 2)
        o_specie = Specie("O", -2)
        coords = list()
        coords.append([0, 0, 0])
        coords.append([0.75, 0.5, 0.75])
        lattice = Lattice.cubic(10)
        s_elem = Structure(lattice, [co_elem, o_elem], coords)
        s_specie = Structure(lattice, [co_specie, o_specie], coords)
        mod = StructureEditor(s_specie)
        mod.remove_oxidation_states()
        mod_s = mod.modified_structure
        self.assertEqual(s_elem, mod_s, "Oxidation state remover failed")
Beispiel #4
0
    def __init__(
        self,
        structure_a,
        structure_b,
        tolerance_cell_misfit=0.1,
        tolerance_atomic_misfit=1.0,
        supercells_allowed=True,
        anonymized=False,
        fitting_accuracy=FAST_FIT,
        use_symmetry=False,
    ):
        """
        Fits two structures. All fitting parameters have been set with defaults 
        that should work in most cases. To use, initialize the structure fitter
        with parameters.
        E.g.,
        fitter = StructureFitter(a, b)
        print fitter.fit_found
        
        Args:
            structure_a : 
                First structure
            structure_b : 
                Second structure to try to match with first structure
            tolerance_cell_misfit : 
                Tolerance for cell misfit. Default = 0.1
            tolerance_atomic_misfit : 
                Tolerance for atomic misfit. Default = 1.0.
            supercells_allowed : 
                Whether supercell structures are allowed.  Default = True.
            anonymized : 
                Whether to attempt matching of different species.  Setting this
                to true will allow any two structures with the same framework,
                but different species to match to each other. Default = False.
            fitting_accuracy : 
                An integer setting for the fitting accuracy.  Corresponds to
                the max number of candidate rotations considered.  Use the
                static variables, 
                StructureFitter.FAST_FIT
                StructureFitter.NORMAL_FIT
                StructureFitter.ACCURATE_FIT
                to set the tradeoff between accuracy and speed.  The default, 
                FAST_FIT, should work reasonably well in most instances.
            use_symmetry:
                Whether to use pymatgen.spacegroup to determine the spacegroup
                first. Eliminates most non-fits. Defaults to True.
        """

        self._tolerance_cell_misfit = tolerance_cell_misfit
        self._tolerance_atomic_misfit = tolerance_atomic_misfit
        self._supercells_allowed = supercells_allowed
        self._anonymized = anonymized
        self._max_rotations = fitting_accuracy
        # Sort structures first so that they have the same arrangement of species
        self._structure_a = structure_a.get_sorted_structure()
        self._structure_b = structure_b.get_sorted_structure()
        if use_symmetry:
            from pymatgen.symmetry.spglib_adaptor import SymmetryFinder

            finder_a = SymmetryFinder(self._structure_a, symprec=0.1)
            finder_b = SymmetryFinder(self._structure_b, symprec=0.1)
            same_sg = finder_a.get_spacegroup_number() == finder_b.get_spacegroup_number()

        if not use_symmetry or same_sg:
            self._mapping_op = None
            if not self._anonymized:
                self.fit(self._structure_a, self._structure_b)
                if self.fit_found:
                    self.el_mapping = {el: el for el in self._structure_a.composition.elements}
            else:
                comp_a = structure_a.composition
                comp_b = structure_b.composition
                if len(comp_a.elements) == len(comp_b.elements):
                    el_a = comp_a.elements
                    # Create permutations of the specie/elements in structure A
                    for p in itertools.permutations(el_a):
                        # Create mapping of the specie/elements in structure B to that of A.
                        # Then create a modified structure with those elements and try to fit it.
                        el_mapping = dict(zip(comp_b.elements, p))
                        logger.debug("Using specie mapping " + str(el_mapping))
                        mod = StructureEditor(self._structure_b)
                        mod.replace_species(el_mapping)
                        self.fit(self._structure_a, mod.modified_structure)
                        if self._mapping_op != None:
                            # Store successful element mapping
                            self.el_mapping = {el_a: el_b for el_b, el_a in el_mapping.items()}
                            break
                else:
                    logger.debug("No. of elements in structures are unequal.")
        else:
            self._mapping_op = None
            logger.debug("Symmetry is different.")