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")
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.")