From 9523e3c16f58971e0588bb89590f69e557caa2a0 Mon Sep 17 00:00:00 2001 From: Rhys Goodall Date: Thu, 6 Mar 2025 21:49:48 -0500 Subject: [PATCH] Add method to get the Pearson symbol to SpaceGroupAnalyzer (#4281) * fea: add method to get the pearson symbol * test: test the pearson symbols * clean: clean and comment sga tests * fix: LiPo4 actually FePO4 * fix linting and supress warning. --------- Signed-off-by: Matthew Horton Co-authored-by: Matthew Horton Co-authored-by: Shyue Ping Ong --- src/pymatgen/analysis/local_env.py | 21 ++++++++++++--------- src/pymatgen/symmetry/analyzer.py | 23 +++++++++++++++++++++++ tests/symmetry/test_analyzer.py | 22 ++++++++++++++++------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/pymatgen/analysis/local_env.py b/src/pymatgen/analysis/local_env.py index ab8c74a4753..26c5a443f74 100644 --- a/src/pymatgen/analysis/local_env.py +++ b/src/pymatgen/analysis/local_env.py @@ -3745,25 +3745,28 @@ def get_nn_info(self, structure: Structure, n: int): if self.use_fictive_radius: # calculate fictive ionic radii - fict_ionic_radii = [_get_fictive_ionic_radius(site, neighbor) for neighbor in neighbors] + fictive_ionic_radii = [_get_fictive_ionic_radius(site, neighbor) for neighbor in neighbors] else: # just use the bond distance - fict_ionic_radii = [neighbor.nn_distance for neighbor in neighbors] + fictive_ionic_radii = [neighbor.nn_distance for neighbor in neighbors] # calculate mean fictive ionic radius - mefir = _get_mean_fictive_ionic_radius(fict_ionic_radii) + mean_fictive_ionic_radius = _get_mean_fictive_ionic_radius(fictive_ionic_radii) # iteratively solve MEFIR; follows equation 4 in Hoppe's EconN paper - prev_mefir = float("inf") - while abs(prev_mefir - mefir) > 1e-4: + prev_mean_fictive_ionic_radius = float("inf") + while abs(prev_mean_fictive_ionic_radius - mean_fictive_ionic_radius) > 1e-4: # this is guaranteed to converge - prev_mefir = mefir - mefir = _get_mean_fictive_ionic_radius(fict_ionic_radii, minimum_fir=mefir) + prev_mean_fictive_ionic_radius = mean_fictive_ionic_radius + mean_fictive_ionic_radius = _get_mean_fictive_ionic_radius( + fictive_ionic_radii, + minimum_fir=mean_fictive_ionic_radius, + ) siw = [] - for nn, fir in zip(neighbors, fict_ionic_radii, strict=True): + for nn, fictive_ionic_radius in zip(neighbors, fictive_ionic_radii, strict=True): if nn.nn_distance < self.cutoff: - w = math.exp(1 - (fir / mefir) ** 6) + w = math.exp(1 - (fictive_ionic_radius / mean_fictive_ionic_radius) ** 6) if w > self.tol: bonded_site = { "site": nn, diff --git a/src/pymatgen/symmetry/analyzer.py b/src/pymatgen/symmetry/analyzer.py index cd54afaab22..e2242879cbf 100644 --- a/src/pymatgen/symmetry/analyzer.py +++ b/src/pymatgen/symmetry/analyzer.py @@ -246,6 +246,29 @@ def get_lattice_type(self) -> LatticeType: return "rhombohedral" return "hexagonal" if system == "trigonal" else system + def get_pearson_symbol(self) -> str: + """Get the Pearson symbol for the structure. + + Returns: + str: Pearson symbol for structure. + """ + cry_sys = self.get_crystal_system() + spg_sym = self.get_space_group_symbol() + centering = "C" if spg_sym[0] in ("A", "B", "C", "S") else spg_sym[0] + + CRYSTAL_FAMILY_SYMBOLS = { + "triclinic": "a", + "monoclinic": "m", + "orthorhombic": "o", + "tetragonal": "t", + "trigonal": "h", + "hexagonal": "h", + "cubic": "c", + } + + num_sites_conventional = len(self._space_group_data.std_types) + return f"{CRYSTAL_FAMILY_SYMBOLS[cry_sys]}{centering}{num_sites_conventional}" + def get_symmetry_dataset(self) -> SpglibDataset: """Get the symmetry dataset as a SpglibDataset. diff --git a/tests/symmetry/test_analyzer.py b/tests/symmetry/test_analyzer.py index 1c8aceb42aa..8b0fc7f9488 100644 --- a/tests/symmetry/test_analyzer.py +++ b/tests/symmetry/test_analyzer.py @@ -26,19 +26,25 @@ class TestSpacegroupAnalyzer(PymatgenTest): def setUp(self): + # FePO4 self.structure = Structure.from_file(f"{VASP_IN_DIR}/POSCAR") self.sg = SpacegroupAnalyzer(self.structure, 0.001) + + # Li10GeP2S12 self.disordered_structure = self.get_structure("Li10GeP2S12") self.disordered_sg = SpacegroupAnalyzer(self.disordered_structure, 0.001) + + # FePO4 with order of sites changed so the atoms aren't grouped by element. struct = self.structure.copy() site = struct[0] del struct[0] struct.append(site.species, site.frac_coords) self.sg3 = SpacegroupAnalyzer(struct, 0.001) - graphite = self.get_structure("Graphite") - graphite.add_site_property("magmom", [0.1] * len(graphite)) - self.sg4 = SpacegroupAnalyzer(graphite, 0.001) - self.structure4 = graphite + + # Graphite + self.structure4 = self.get_structure("Graphite") + self.structure4.add_site_property("magmom", [0.1] * len(self.structure4)) + self.sg4 = SpacegroupAnalyzer(self.structure4, 0.001) def test_primitive(self): struct = Structure.from_spacegroup("Fm-3m", np.eye(3) * 3, ["Cu"], [[0, 0, 0]]) @@ -49,9 +55,7 @@ def test_primitive(self): def test_is_laue(self): struct = Structure.from_spacegroup("Fm-3m", np.eye(3) * 3, ["Cu"], [[0, 0, 0]]) assert SpacegroupAnalyzer(struct).is_laue() - assert self.sg.is_laue() - assert self.disordered_sg.is_laue() def test_magnetic(self): @@ -86,6 +90,12 @@ def test_get_pointgroup(self): assert self.sg.get_point_group_symbol() == "mmm" assert self.disordered_sg.get_point_group_symbol() == "4/mmm" + def test_get_pearson_symbol(self): + assert self.sg.get_pearson_symbol() == "oP24" + assert self.disordered_sg.get_pearson_symbol() == "tP58" + assert self.sg3.get_pearson_symbol() == "oP24" + assert self.sg4.get_pearson_symbol() == "hP4" + def test_get_point_group_operations(self): sg: SpacegroupAnalyzer rng = np.random.default_rng()