def test_ignore_species(self): s1 = Structure.from_file(os.path.join(test_dir, "LiFePO4.cif")) s2 = Structure.from_file(os.path.join(test_dir, "POSCAR")) m = StructureMatcher(ignored_species=["Li"], primitive_cell=False, attempt_supercell=True) self.assertTrue(m.fit(s1, s2)) self.assertTrue(m.fit_anonymous(s1, s2)) groups = m.group_structures([s1, s2]) self.assertEqual(len(groups), 1) s2.make_supercell((2, 1, 1)) ss1 = m.get_s2_like_s1(s2, s1, include_ignored_species=True) self.assertAlmostEqual(ss1.lattice.a, 20.820740000000001) self.assertEqual(ss1.composition.reduced_formula, "LiFePO4") self.assertEqual( { k.symbol: v.symbol for k, v in m.get_best_electronegativity_anonymous_mapping( s1, s2).items() }, { "Fe": "Fe", "P": "P", "O": "O" })
def apply_transformation(self, structure): """ Returns a copy of structure with lattice parameters and sites scaled to the same degree as the relaxed_structure. Arg: structure (Structure): A structurally similar structure in regards to crystal and site positions. """ if self.species_map is None: match = StructureMatcher() s_map = match.get_best_electronegativity_anonymous_mapping(self.unrelaxed_structure, structure) else: s_map = self.species_map params = list(structure.lattice.abc) params.extend(structure.lattice.angles) new_lattice = Lattice.from_parameters(*(p * self.params_percent_change[i] for i, p in enumerate(params))) species, frac_coords = [], [] for site in self.relaxed_structure: species.append(s_map[site.specie]) frac_coords.append(site.frac_coords) return Structure(new_lattice, species, frac_coords)
def apply_transformation(self, structure): """ Returns a copy of structure with lattice parameters and sites scaled to the same degree as the relaxed_structure. Arg: structure (Structure): A structurally similar structure in regards to crystal and site positions. """ if self.species_map == None: match = StructureMatcher() s_map = \ match.get_best_electronegativity_anonymous_mapping(self.unrelaxed_structure, structure) else: s_map = self.species_map params = list(structure.lattice.abc) params.extend(structure.lattice.angles) new_lattice = Lattice.from_parameters(*[p*self.params_percent_change[i] \ for i, p in enumerate(params)]) species, frac_coords = [], [] for site in self.relaxed_structure: species.append(s_map[site.specie]) frac_coords.append(site.frac_coords) return Structure(new_lattice, species, frac_coords)
def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PAsO4S4.json")) s2 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PNO4Se4.json")) self.assertEqual(sm.get_best_electronegativity_anonymous_mapping(s1, s2), {Element('S'): Element('Se'), Element('As'): Element('N'), Element('Fe'): Element('Fe'), Element('Na'): Element('Na'), Element('P'): Element('P'), Element('O'): Element('O'),}) self.assertEqual(len(sm.get_all_anonymous_mappings(s1, s2)), 2)
def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PAsO4S4.json")) s2 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PNO4Se4.json")) self.assertEqual(sm.get_best_electronegativity_anonymous_mapping(s1, s2), {Element('S'): Element('Se'), Element('As'): Element('N'), Element('Fe'): Element('Fe'), Element('Na'): Element('Na'), Element('P'): Element('P'), Element('O'): Element('O'),}) self.assertEqual(len(sm.get_all_anonymous_mappings(s1, s2)), 2)
def predict(self, structure, ref_structure, test_isostructural=True): """ Given a structure, returns back the predicted volume. Args: structure (Structure): structure w/unknown volume ref_structure (Structure): A reference structure with a similar structure but different species. test_isostructural (bool): Whether to test that the two structures are isostructural. This algo works best for isostructural compounds. Defaults to True. Returns: a float value of the predicted volume """ if not is_ox(structure): a = BVAnalyzer() structure = a.get_oxi_state_decorated_structure(structure) if not is_ox(ref_structure): a = BVAnalyzer() ref_structure = a.get_oxi_state_decorated_structure(ref_structure) if test_isostructural: m = StructureMatcher() mapping = m.get_best_electronegativity_anonymous_mapping( structure, ref_structure) if mapping is None: raise ValueError("Input structures do not match!") comp = structure.composition ref_comp = ref_structure.composition numerator = 0 denominator = 0 # Here, the 1/3 factor on the composition accounts for atomic # packing. We want the number per unit length. # TODO: AJ doesn't understand the (1/3). It would make sense to him # if you were doing atomic volume and not atomic radius for k, v in comp.items(): numerator += k.ionic_radius * v**(1 / 3) for k, v in ref_comp.items(): denominator += k.ionic_radius * v**(1 / 3) # The scaling factor is based on lengths. We apply a power of 3. return ref_structure.volume * (numerator / denominator)**3
def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PAsO4S4.json")) s2 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PNO4Se4.json")) self.assertEqual( sm.get_best_electronegativity_anonymous_mapping(s1, s2), { Element("S"): Element("Se"), Element("As"): Element("N"), Element("Fe"): Element("Fe"), Element("Na"): Element("Na"), Element("P"): Element("P"), Element("O"): Element("O"), }, ) self.assertEqual(len(sm.get_all_anonymous_mappings(s1, s2)), 2)
def test_ignore_species(self): s1 = Structure.from_file(os.path.join(test_dir, "LiFePO4.cif")) s2 = Structure.from_file(os.path.join(test_dir, "POSCAR")) m = StructureMatcher(ignored_species=["Li"], primitive_cell=False, attempt_supercell=True) self.assertTrue(m.fit(s1, s2)) self.assertTrue(m.fit_anonymous(s1, s2)) groups = m.group_structures([s1, s2]) self.assertEqual(len(groups), 1) s2.make_supercell((2, 1, 1)) ss1 = m.get_s2_like_s1(s2, s1, include_ignored_species=True) self.assertAlmostEqual(ss1.lattice.a, 20.820740000000001) self.assertEqual(ss1.composition.reduced_formula, "LiFePO4") self.assertEqual( {k.symbol: v.symbol for k, v in m.get_best_electronegativity_anonymous_mapping(s1, s2).items()}, {"Fe": "Fe", "P": "P", "O": "O"}, )
def predict(self, structure, ref_structure, test_isostructural=True): """ Given a structure, returns back the predicted volume. Args: structure (Structure): structure w/unknown volume ref_structure (Structure): A reference structure with a similar structure but different species. test_isostructural (bool): Whether to test that the two structures are isostructural. This algo works best for isostructural compounds. Defaults to True. Returns: a float value of the predicted volume """ if not is_ox(structure): a = BVAnalyzer() structure = a.get_oxi_state_decorated_structure(structure) if not is_ox(ref_structure): a = BVAnalyzer() ref_structure = a.get_oxi_state_decorated_structure(ref_structure) if test_isostructural: m = StructureMatcher() mapping = m.get_best_electronegativity_anonymous_mapping(structure, ref_structure) if mapping is None: raise ValueError("Input structures do not match!") comp = structure.composition ref_comp = ref_structure.composition numerator = 0 denominator = 0 # Here, the 1/3 factor on the composition accounts for atomic # packing. We want the number per unit length. # TODO: AJ doesn't understand the (1/3). It would make sense to him # if you were doing atomic volume and not atomic radius for k, v in comp.items(): numerator += k.ionic_radius * v ** (1 / 3) for k, v in ref_comp.items(): denominator += k.ionic_radius * v ** (1 / 3) # The scaling factor is based on lengths. We apply a power of 3. return ref_structure.volume * (numerator / denominator) ** 3
def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PAsO4S4.json")) s2 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PNO4Se4.json")) self.assertEqual(sm.get_best_electronegativity_anonymous_mapping(s1, s2), {Element('S'): Element('Se'), Element('As'): Element('N'), Element('Fe'): Element('Fe'), Element('Na'): Element('Na'), Element('P'): Element('P'), Element('O'): Element('O'),}) self.assertEqual(len(sm.get_all_anonymous_mappings(s1, s2)), 2) # test include_dist dists = {Element('N'): 0, Element('P'): 0.0010725064} for mapping, d in sm.get_all_anonymous_mappings(s1, s2, include_dist=True): self.assertAlmostEqual(dists[mapping[Element('As')]], d)
def test_electronegativity(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5) s1 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PAsO4S4.json")) s2 = Structure.from_file(os.path.join(test_dir, "Na2Fe2PNO4Se4.json")) self.assertEqual(sm.get_best_electronegativity_anonymous_mapping(s1, s2), {Element('S'): Element('Se'), Element('As'): Element('N'), Element('Fe'): Element('Fe'), Element('Na'): Element('Na'), Element('P'): Element('P'), Element('O'): Element('O'),}) self.assertEqual(len(sm.get_all_anonymous_mappings(s1, s2)), 2) # test include_dist dists = {Element('N'): 0, Element('P'): 0.0010725064} for mapping, d in sm.get_all_anonymous_mappings(s1, s2, include_dist=True): self.assertAlmostEqual(dists[mapping[Element('As')]], d)
def predict(self, structure, ref_structure): """ Given a structure, returns the predicted volume. Args: structure (Structure): structure w/unknown volume ref_structure (Structure): A reference structure with a similar structure but different species. Returns: a float value of the predicted volume """ if self.check_isostructural: m = StructureMatcher() mapping = m.get_best_electronegativity_anonymous_mapping(structure, ref_structure) if mapping is None: raise ValueError("Input structures do not match!") if "ionic" in self.radii_type: try: # Use BV analyzer to determine oxidation states only if the # oxidation states are not already specified in the structure # and use_bv is true. if (not _is_ox(structure)) and self.use_bv: a = BVAnalyzer() structure = a.get_oxi_state_decorated_structure(structure) if (not _is_ox(ref_structure)) and self.use_bv: a = BVAnalyzer() ref_structure = a.get_oxi_state_decorated_structure(ref_structure) comp = structure.composition ref_comp = ref_structure.composition # Check if all the associated ionic radii are available. if any(k.ionic_radius is None for k in list(comp.keys())) or any( k.ionic_radius is None for k in list(ref_comp.keys()) ): raise ValueError("Not all the ionic radii are available!") numerator = 0 denominator = 0 # Here, the 1/3 factor on the composition accounts for atomic # packing. We want the number per unit length. for k, v in comp.items(): numerator += k.ionic_radius * v ** (1 / 3) for k, v in ref_comp.items(): denominator += k.ionic_radius * v ** (1 / 3) return ref_structure.volume * (numerator / denominator) ** 3 except Exception: warnings.warn("Exception occured. Will attempt atomic radii.") # If error occurs during use of ionic radii scheme, pass # and see if we can resolve it using atomic radii. pass if "atomic" in self.radii_type: comp = structure.composition ref_comp = ref_structure.composition # Here, the 1/3 factor on the composition accounts for atomic # packing. We want the number per unit length. numerator = 0 denominator = 0 for k, v in comp.items(): numerator += k.atomic_radius * v ** (1 / 3) for k, v in ref_comp.items(): denominator += k.atomic_radius * v ** (1 / 3) return ref_structure.volume * (numerator / denominator) ** 3 raise ValueError("Cannot find volume scaling based on radii choices specified!")
def predict(self, structure, ref_structure): """ Given a structure, returns the predicted volume. Args: structure (Structure): structure w/unknown volume ref_structure (Structure): A reference structure with a similar structure but different species. Returns: a float value of the predicted volume """ if self.check_isostructural: m = StructureMatcher() mapping = m.get_best_electronegativity_anonymous_mapping( structure, ref_structure) if mapping is None: raise ValueError("Input structures do not match!") if "ionic" in self.radii_type: try: # Use BV analyzer to determine oxidation states only if the # oxidation states are not already specified in the structure # and use_bv is true. if (not is_ox(structure)) and self.use_bv: a = BVAnalyzer() structure = a.get_oxi_state_decorated_structure(structure) if (not is_ox(ref_structure)) and self.use_bv: a = BVAnalyzer() ref_structure = a.get_oxi_state_decorated_structure( ref_structure) comp = structure.composition ref_comp = ref_structure.composition # Check if all the associated ionic radii are available. if any([k.ionic_radius is None for k in list(comp.keys())]) or \ any([k.ionic_radius is None for k in list(ref_comp.keys())]): raise ValueError("Not all the ionic radii are available!") numerator = 0 denominator = 0 # Here, the 1/3 factor on the composition accounts for atomic # packing. We want the number per unit length. for k, v in comp.items(): numerator += k.ionic_radius * v ** (1 / 3) for k, v in ref_comp.items(): denominator += k.ionic_radius * v ** (1 / 3) return ref_structure.volume * (numerator / denominator) ** 3 except Exception as ex: warnings.warn("Exception occured. Will attempt atomic radii.") # If error occurs during use of ionic radii scheme, pass # and see if we can resolve it using atomic radii. pass if "atomic" in self.radii_type: comp = structure.composition ref_comp = ref_structure.composition # Here, the 1/3 factor on the composition accounts for atomic # packing. We want the number per unit length. numerator = 0 denominator = 0 for k, v in comp.items(): numerator += k.atomic_radius * v ** (1 / 3) for k, v in ref_comp.items(): denominator += k.atomic_radius * v ** (1 / 3) return ref_structure.volume * (numerator / denominator) ** 3 raise ValueError("Cannot find volume scaling based on radii choices " "specified!")