def test_no_scaling(self): sm = StructureMatcher(ltol=0.1, stol=0.1, angle_tol=2, scale=False, comparator=ElementComparator()) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) self.assertTrue(sm.get_rms_dist(self.struct_list[0], self.struct_list[1])[0] < 0.0008)
def get_framework_rms_plot(self, plt=None, granularity=200, matching_s=None): """ Get the plot of rms framework displacement vs time. Useful for checking for melting, especially if framework atoms can move via paddle-wheel or similar mechanism (which would show up in max framework displacement but doesn't constitute melting). Args: plt (matplotlib.pyplot): If plt is supplied, changes will be made to an existing plot. Otherwise, a new plot will be created. granularity (int): Number of structures to match matching_s (Structure): Optionally match to a disordered structure instead of the first structure in the analyzer. Required when a secondary mobile ion is present. Notes: The method doesn't apply to NPT-AIMD simulation analysis. """ from pymatgen.util.plotting import pretty_plot if self.lattices is not None and len(self.lattices) > 1: warnings.warn( "Note the method doesn't apply to NPT-AIMD simulation analysis!" ) plt = pretty_plot(12, 8, plt=plt) step = (self.corrected_displacements.shape[1] - 1) // (granularity - 1) f = (matching_s or self.structure).copy() f.remove_species([self.specie]) sm = StructureMatcher( primitive_cell=False, stol=0.6, comparator=OrderDisorderElementComparator(), allow_subset=True, ) rms = [] for s in self.get_drift_corrected_structures(step=step): s.remove_species([self.specie]) d = sm.get_rms_dist(f, s) if d: rms.append(d) else: rms.append((1, 1)) max_dt = (len(rms) - 1) * step * self.step_skip * self.time_step if max_dt > 100000: plot_dt = np.linspace(0, max_dt / 1000, len(rms)) unit = "ps" else: plot_dt = np.linspace(0, max_dt, len(rms)) unit = "fs" rms = np.array(rms) plt.plot(plot_dt, rms[:, 0], label="RMS") plt.plot(plot_dt, rms[:, 1], label="max") plt.legend(loc="best") plt.xlabel(f"Timestep ({unit})") plt.ylabel("normalized distance") plt.tight_layout() return plt
def get_framework_rms_plot(self, plt=None, granularity=200, matching_s=None): """ Get the plot of rms framework displacement vs time. Useful for checking for melting, especially if framework atoms can move via paddle-wheel or similar mechanism (which would show up in max framework displacement but doesn't constitute melting). Args: plt (matplotlib.pyplot): If plt is supplied, changes will be made to an existing plot. Otherwise, a new plot will be created. granularity (int): Number of structures to match matching_s (Structure): Optionally match to a disordered structure instead of the first structure in the analyzer. Required when a secondary mobile ion is present. Notes: The method doesn't apply to NPT-AIMD simulation analysis. """ from pymatgen.util.plotting import pretty_plot if self.lattices is not None and len(self.lattices) > 1: warnings.warn("Note the method doesn't apply to NPT-AIMD " "simulation analysis!") plt = pretty_plot(12, 8, plt=plt) step = (self.corrected_displacements.shape[1] - 1) // (granularity - 1) f = (matching_s or self.structure).copy() f.remove_species([self.specie]) sm = StructureMatcher(primitive_cell=False, stol=0.6, comparator=OrderDisorderElementComparator(), allow_subset=True) rms = [] for s in self.get_drift_corrected_structures(step=step): s.remove_species([self.specie]) d = sm.get_rms_dist(f, s) if d: rms.append(d) else: rms.append((1, 1)) max_dt = (len(rms) - 1) * step * self.step_skip * self.time_step if max_dt > 100000: plot_dt = np.linspace(0, max_dt / 1000, len(rms)) unit = 'ps' else: plot_dt = np.linspace(0, max_dt, len(rms)) unit = 'fs' rms = np.array(rms) plt.plot(plot_dt, rms[:, 0], label='RMS') plt.plot(plot_dt, rms[:, 1], label='max') plt.legend(loc='best') plt.xlabel("Timestep ({})".format(unit)) plt.ylabel("normalized distance") plt.tight_layout() return plt
def test_rms_vs_minimax(self): # This tests that structures with adjusted RMS less than stol, but minimax # greater than stol are treated properly sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False) l = Lattice.orthorhombic(1, 2, 12) sp = ["Si", "Si", "Al"] s1 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.5]]) s2 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.6]]) self.assertArrayAlmostEqual(sm.get_rms_dist(s1, s2), (0.32 ** 0.5 / 2, 0.4)) self.assertEqual(sm.fit(s1, s2), False) self.assertEqual(sm.fit_anonymous(s1, s2), False) self.assertEqual(sm.get_mapping(s1, s2), None)
def get_framework_rms_plot(self, plt=None, granularity=200, matching_s=None): """ Get the plot of rms framework displacement vs time. Useful for checking for melting, especially if framework atoms can move via paddle-wheel or similar mechanism (which would show up in max framework displacement but doesn't constitute melting). Args: granularity (int): Number of structures to match matching_s (Structure): Optionally match to a disordered structure instead of the first structure in the analyzer. Required when a secondary mobile ion is present. """ from pymatgen.util.plotting_utils import get_publication_quality_plot plt = get_publication_quality_plot(12, 8, plt=plt) step = (self.corrected_displacements.shape[1] - 1) // (granularity - 1) f = (matching_s or self.structure).copy() f.remove_species([self.specie]) sm = StructureMatcher(primitive_cell=False, stol=0.6, comparator=OrderDisorderElementComparator(), allow_subset=True) rms = [] for s in self.get_drift_corrected_structures(step=step): s.remove_species([self.specie]) d = sm.get_rms_dist(f, s) if d: rms.append(d) else: rms.append((1, 1)) max_dt = (len(rms) - 1) * step * self.step_skip * self.time_step if max_dt > 100000: plot_dt = np.linspace(0, max_dt / 1000, len(rms)) unit = 'ps' else: plot_dt = np.linspace(0, max_dt, len(rms)) unit = 'fs' rms = np.array(rms) plt.plot(plot_dt, rms[:, 0], label='RMS') plt.plot(plot_dt, rms[:, 1], label='max') plt.legend(loc='best') plt.xlabel("Timestep ({})".format(unit)) plt.ylabel("normalized distance") plt.tight_layout() return plt
def test_rms_vs_minimax(self): # This tests that structures with adjusted RMS less than stol, but minimax # greater than stol are treated properly # stol=0.3 gives exactly an ftol of 0.1 on the c axis sm = StructureMatcher(ltol=0.2, stol=0.301, angle_tol=1, primitive_cell=False) l = Lattice.orthorhombic(1, 2, 12) sp = ["Si", "Si", "Al"] s1 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.5]]) s2 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.6]]) self.assertArrayAlmostEqual(sm.get_rms_dist(s1, s2), (0.32 ** 0.5 / 2, 0.4)) self.assertEqual(sm.fit(s1, s2), False) self.assertEqual(sm.fit_anonymous(s1, s2), False) self.assertEqual(sm.get_mapping(s1, s2), None)
def post_process(self, docs): s1 = Structure.from_dict(self.structure) m = StructureMatcher( ltol=self.ltol, stol=self.stol, angle_tol=self.angle_tol, primitive_cell=True, scale=True, attempt_supercell=False, comparator=ElementComparator(), ) matches = [] for doc in docs: s2 = Structure.from_dict(doc["structure"]) matched = m.fit(s1, s2) if matched: rms = m.get_rms_dist(s1, s2) matches.append({ "material_id": doc["material_id"], "normalized_rms_displacement": rms[0], "max_distance_paired_sites": rms[1], }) response = sorted( matches[:self.limit], key=lambda x: ( x["normalized_rms_displacement"], x["max_distance_paired_sites"], ), ) return response
def test_supercell_subsets(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size='volume') sm_no_s = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=False, supercell_size='volume') l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Ag', 'Si', 'Si'], [[.7,.4,.5],[0,0,0.1],[0,0,0.2]]) s1.make_supercell([2,1,1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0.1,-0.95],[0,0.1,0],[-.7,.5,.375]]) shuffle = [0,2,1,3,4,5] s1 = Structure.from_sites([s1[i] for i in shuffle]) #test when s1 is exact supercell of s2 result = sm.get_s2_like_s1(s1, s2) for a, b in zip(s1, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2)) self.assertTrue(sm.fit(s2, s1)) self.assertTrue(sm_no_s.fit(s1, s2)) self.assertTrue(sm_no_s.fit(s2, s1)) rms = (0.048604032430991401, 0.059527539448807391) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms)) #test when the supercell is a subset of s2 subset_supercell = s1.copy() del subset_supercell[0] result = sm.get_s2_like_s1(subset_supercell, s2) self.assertEqual(len(result), 6) for a, b in zip(subset_supercell, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(subset_supercell, s2)) self.assertTrue(sm.fit(s2, subset_supercell)) self.assertFalse(sm_no_s.fit(subset_supercell, s2)) self.assertFalse(sm_no_s.fit(s2, subset_supercell)) rms = (0.053243049896333279, 0.059527539448807336) self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms)) #test when s2 (once made a supercell) is a subset of s1 s2_missing_site = s2.copy() del s2_missing_site[1] result = sm.get_s2_like_s1(s1, s2_missing_site) for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2_missing_site)) self.assertTrue(sm.fit(s2_missing_site, s1)) self.assertFalse(sm_no_s.fit(s1, s2_missing_site)) self.assertFalse(sm_no_s.fit(s2_missing_site, s1)) rms = (0.029763769724403633, 0.029763769724403987) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
def test_supercell_subsets(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size='volume') sm_no_s = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=False, supercell_size='volume') l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Ag', 'Si', 'Si'], [[.7, .4, .5], [0, 0, 0.1], [0, 0, 0.2]]) s1.make_supercell([2, 1, 1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0, 0.1, -0.95], [0, 0.1, 0], [-.7, .5, .375]]) shuffle = [0, 2, 1, 3, 4, 5] s1 = Structure.from_sites([s1[i] for i in shuffle]) #test when s1 is exact supercell of s2 result = sm.get_s2_like_s1(s1, s2) for a, b in zip(s1, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2)) self.assertTrue(sm.fit(s2, s1)) self.assertTrue(sm_no_s.fit(s1, s2)) self.assertTrue(sm_no_s.fit(s2, s1)) rms = (0.048604032430991401, 0.059527539448807391) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms)) #test when the supercell is a subset of s2 subset_supercell = s1.copy() del subset_supercell[0] result = sm.get_s2_like_s1(subset_supercell, s2) self.assertEqual(len(result), 6) for a, b in zip(subset_supercell, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(subset_supercell, s2)) self.assertTrue(sm.fit(s2, subset_supercell)) self.assertFalse(sm_no_s.fit(subset_supercell, s2)) self.assertFalse(sm_no_s.fit(s2, subset_supercell)) rms = (0.053243049896333279, 0.059527539448807336) self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms)) #test when s2 (once made a supercell) is a subset of s1 s2_missing_site = s2.copy() del s2_missing_site[1] result = sm.get_s2_like_s1(s1, s2_missing_site) for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2_missing_site)) self.assertTrue(sm.fit(s2_missing_site, s1)) self.assertFalse(sm_no_s.fit(s1, s2_missing_site)) self.assertFalse(sm_no_s.fit(s2_missing_site, s1)) rms = (0.029763769724403633, 0.029763769724403987) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
from pymatgen import Structure from pymatgen.analysis.structure_matcher import StructureMatcher client = pymongo.MongoClient() db = client.springer coll = db['pauling_file_unique_Parse'] if __name__ == '__main__': for doc in coll.find({'key': 'sd_1223808'}): struc1 = Structure.from_dict(doc['structure']) for doc in coll.find({'key': 'sd_0458111'}): struc2 = Structure.from_dict(doc['structure']) for doc in coll.find({'key': 'sd_1933177'}): struc3 = Structure.from_dict(doc['structure']) for doc in coll.find({'key': 'sd_1010018'}): struc4 = Structure.from_dict(doc['structure']) print struc4 print doc['metadata']['_Springer']['geninfo']['Phase Label(s)'] # print Structure.from_dict(doc['structure']) # print doc['structure'] for doc in coll.find({'key': 'sd_0529813'}): struc5 = Structure.from_dict(doc['structure']) matcher = StructureMatcher() print matcher.fit(struc1, struc2), '8.18, 8.26', matcher.get_rms_dist(struc1, struc2) print matcher.fit(struc2, struc3), '8.26 8.25', matcher.get_rms_dist(struc2, struc3) print matcher.fit(struc3, struc1), '8.25 8.18', matcher.get_rms_dist(struc3, struc1) print matcher.fit(struc1, struc4), matcher.get_rms_dist(struc1, struc4) print matcher.fit(struc1, struc5), matcher.get_rms_dist(struc1, struc5) print matcher.fit(struc2, struc5), matcher.get_rms_dist(struc2, struc5)
async def find_structure( structure: Structure = Body( ..., title="Pymatgen structure object to query with", ), ltol: float = Query( 0.2, title="Fractional length tolerance. Default is 0.2.", ), stol: float = Query( 0.3, title= "Site tolerance. Defined as the fraction of the average free \ length per atom := ( V / Nsites ) ** (1/3). Default is 0.3.", ), angle_tol: float = Query( 5, title="Angle tolerance in degrees. Default is 5 degrees.", ), limit: int = Query( 1, title= "Maximum number of matches to show. Defaults to 1, only showing the best match.", ), ): """ Obtains material structures that match a given input structure within some tolerance. Returns: A list of Material IDs for materials with matched structures alongside the associated RMS values """ try: s = PS.from_dict(structure.dict()) except Exception: raise HTTPException( status_code=404, detail= "Body cannot be converted to a pymatgen structure object.", ) m = StructureMatcher( ltol=ltol, stol=stol, angle_tol=angle_tol, primitive_cell=True, scale=True, attempt_supercell=False, comparator=ElementComparator(), ) crit = {"composition_reduced": dict(s.composition.to_reduced_dict)} self.store.connect() matches = [] for r in self.store.query(criteria=crit, properties=["structure", "task_id"]): s2 = PS.from_dict(r["structure"]) matched = m.fit(s, s2) if matched: rms = m.get_rms_dist(s, s2) matches.append({ "task_id": r["task_id"], "normalized_rms_displacement": rms[0], "max_distance_paired_sites": rms[1], }) response = { "data": sorted( matches[:limit], key=lambda x: ( x["normalized_rms_displacement"], x["max_distance_paired_sites"], ), ) } return response
import pymatgen as mg from pymatgen.analysis.structure_matcher import StructureMatcher struct1 = mg.Structure.from_file("1.cif") struct2 = mg.Structure.from_file("2.cif") matcher = StructureMatcher(attempt_supercell=True, primitive_cell=False) rms = matcher.get_rms_dist(struct1, struct2) print rms