From 779a5282a75df89119a49bd1465b30910d0366c5 Mon Sep 17 00:00:00 2001 From: Sean Kavanagh Date: Wed, 22 Nov 2023 12:27:11 +0000 Subject: [PATCH] Add `group_order_from_schoenflies`, ready to auto-convert from relaxed structure -> point symmetry -> orientational degeneracy --- doped/generation.py | 85 +++-------------------------- doped/plotting.py | 2 +- doped/utils/symmetry.py | 115 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 78 deletions(-) diff --git a/doped/generation.py b/doped/generation.py index c45a1b61..f3341d78 100644 --- a/doped/generation.py +++ b/doped/generation.py @@ -55,6 +55,7 @@ get_primitive_structure, get_wyckoff, get_wyckoff_label_and_equiv_coord_list, + schoenflies_from_hermann, ) _dummy_species = DummySpecies("X") # Dummy species used to keep track of defect coords in the supercell @@ -258,7 +259,7 @@ def get_defect_name_from_defect(defect, element_list=None, symm_ops=None, sympre symm_dataset, _unique_sites = _get_symm_dataset_of_struc_with_all_equiv_sites( defect.site.frac_coords, defect.structure, symm_ops=symm_ops, symprec=symprec ) - spglib_point_group_symbol = herm2sch(symm_dataset["site_symmetry_symbols"][-1]) + spglib_point_group_symbol = schoenflies_from_hermann(symm_dataset["site_symmetry_symbols"][-1]) if spglib_point_group_symbol is not None: point_group_symbol = spglib_point_group_symbol else: # symm_ops approach failed, just use diagonal defect supercell approach: @@ -268,7 +269,7 @@ def get_defect_name_from_defect(defect, element_list=None, symm_ops=None, sympre ) # create defect supercell, which is a diagonal expansion of the unit cell so that the defect # periodic image retains the unit cell symmetry, in order not to affect the point group symmetry sga = _get_sga(defect_diagonal_supercell, symprec=symprec) - point_group_symbol = herm2sch(sga.get_point_group_symbol()) + point_group_symbol = schoenflies_from_hermann(sga.get_point_group_symbol()) return f"{defect.name}_{point_group_symbol}_{closest_site_info(defect, element_list=element_list)}" @@ -292,7 +293,7 @@ def _check_unrelaxed_defect_symmetry_determination( symm_ops=symm_ops, symprec=symprec, ) - unrelaxed_spglib_point_group_symbol = herm2sch(symm_dataset["pointgroup"]) + unrelaxed_spglib_point_group_symbol = schoenflies_from_hermann(symm_dataset["pointgroup"]) symm_dataset, _unique_sites = _get_symm_dataset_of_struc_with_all_equiv_sites( defect_entry.defect_supercell_site.frac_coords, @@ -300,7 +301,7 @@ def _check_unrelaxed_defect_symmetry_determination( symm_ops=symm_ops, symprec=symprec, ) - bulk_spglib_point_group_symbol = herm2sch(symm_dataset["pointgroup"]) + bulk_spglib_point_group_symbol = schoenflies_from_hermann(symm_dataset["pointgroup"]) if bulk_spglib_point_group_symbol != unrelaxed_spglib_point_group_symbol: if verbose: @@ -442,14 +443,14 @@ def get_defect_name_from_entry( # site_symmetry_symbols[-1] works better for unrelaxed defects (as sometimes with the equivalent # sites population it can change the overall point group symbol (but site symmetry symbol is # still correct)) - spglib_point_group_symbol = herm2sch(symm_dataset["site_symmetry_symbols"][-1]) + spglib_point_group_symbol = schoenflies_from_hermann(symm_dataset["site_symmetry_symbols"][-1]) else: # For relaxed defects the "defect supercell site" is not necessarily the true centre of mass of # the defect (e.g. for split-interstitials, split-vacancies, swapped vacancies etc), # so use 'pointgroup' output (in this case the reduced symmetry avoids the symmetry-upgrade # possibility with the equivalent sites, as when unrelaxed=True) - spglib_point_group_symbol = herm2sch(symm_dataset["pointgroup"]) + spglib_point_group_symbol = schoenflies_from_hermann(symm_dataset["pointgroup"]) if spglib_point_group_symbol is not None: point_group_symbol = spglib_point_group_symbol @@ -467,7 +468,7 @@ def get_defect_name_from_entry( ) # create defect supercell, which is a diagonal expansion of the unit cell so that the defect # periodic image retains the unit cell symmetry, in order not to affect the point group symmetry sga = _get_sga(defect_diagonal_supercell, symprec=symprec) - point_group_symbol = herm2sch(sga.get_point_group_symbol()) + point_group_symbol = schoenflies_from_hermann(sga.get_point_group_symbol()) return ( f"{defect_entry.defect.name}_{point_group_symbol}" @@ -697,32 +698,6 @@ def handle_repeated_name(defect_naming_dict, full_defect_name): return defect_naming_dict -def herm2sch(herm_symbol): - """ - Convert from Hermann-Mauguin to Schoenflies. - """ - herm_symbol = herm_symbol.replace(".", "") - schoenflies = _HERM2SCH.get(herm_symbol, None) - if schoenflies is None: - # try rearranging, symbols in spglib can be rearranged vs _HERM2SCH dict - # get _HERM2SCH key that has the same characters as herm_symbol - # (i.e. same characters, but possibly in a different order) - from collections import Counter - - def find_matching_key(input_str, input_dict): - input_str_counter = Counter(input_str) - for key in input_dict: - if Counter(key) == input_str_counter: - return key - return None - - herm_key = find_matching_key(herm_symbol, _HERM2SCH) - if herm_key is not None: - schoenflies = _HERM2SCH[herm_key] - - return schoenflies - - def get_oxi_probabilities(element_symbol: str) -> dict: """ Get a dictionary of oxidation states and their probabilities for an @@ -2339,47 +2314,3 @@ def _get_interstitial_candidate_sites(args): """ interstitial_generator, structure = args return [*interstitial_generator._get_candidate_sites(structure)] - - -# Schoenflies, Hermann-Mauguin, spgid dict: (Taken from the excellent Abipy with GNU GPL License) -_PTG_IDS = [ - ("C1", "1", 1), - ("Ci", "-1", 2), - ("C2", "2", 3), - ("Cs", "m", 6), - ("C2h", "2/m", 10), - ("D2", "222", 16), - ("C2v", "mm2", 25), - ("D2h", "mmm", 47), - ("C4", "4", 75), - ("S4", "-4", 81), - ("C4h", "4/m", 83), - ("D4", "422", 89), - ("C4v", "4mm", 99), - ("D2d", "-42m", 111), - ("D4h", "4/mmm", 123), - ("C3", "3", 143), - ("C3i", "-3", 147), - ("D3", "32", 149), - ("C3v", "3m", 156), - ("D3d", "-3m", 162), - ("C6", "6", 168), - ("C3h", "-6", 174), - ("C6h", "6/m", 175), - ("D6", "622", 177), - ("C6v", "6mm", 183), - ("D3h", "-6m2", 189), - ("D6h", "6/mmm", 191), - ("T", "23", 195), - ("Th", "m-3", 200), - ("O", "432", 207), - ("Td", "-43m", 215), - ("Oh", "m-3m", 221), -] - -_SCH2HERM = {t[0]: t[1] for t in _PTG_IDS} -_HERM2SCH = {t[1]: t[0] for t in _PTG_IDS} -_SPGID2SCH = {t[2]: t[0] for t in _PTG_IDS} -_SCH2SPGID = {t[0]: t[2] for t in _PTG_IDS} - -sch_symbols = list(_SCH2HERM.keys()) diff --git a/doped/plotting.py b/doped/plotting.py index 7b9d03b1..f9dd724d 100644 --- a/doped/plotting.py +++ b/doped/plotting.py @@ -18,7 +18,7 @@ from pymatgen.core.periodic_table import Element from pymatgen.util.string import latexify -from doped.generation import sch_symbols # point group symbols +from doped.utils.symmetry import sch_symbols # point group symbols # TODO: Make a specific tutorial in docs for editing return Matplotlib figures, or with rcParams, diff --git a/doped/utils/symmetry.py b/doped/utils/symmetry.py index 96519f28..3a1e14ff 100644 --- a/doped/utils/symmetry.py +++ b/doped/utils/symmetry.py @@ -837,3 +837,118 @@ def _skip_to_spacegroup(f, spacegroup, setting=None): if line.startswith(name): break return line + + +# Schoenflies, Hermann-Mauguin, spgid dict: (Taken from the excellent Abipy with GNU GPL License) +_PTG_IDS = [ + ("C1", "1", 1), + ("Ci", "-1", 2), + ("C2", "2", 3), + ("Cs", "m", 6), + ("C2h", "2/m", 10), + ("D2", "222", 16), + ("C2v", "mm2", 25), + ("D2h", "mmm", 47), + ("C4", "4", 75), + ("S4", "-4", 81), + ("C4h", "4/m", 83), + ("D4", "422", 89), + ("C4v", "4mm", 99), + ("D2d", "-42m", 111), + ("D4h", "4/mmm", 123), + ("C3", "3", 143), + ("C3i", "-3", 147), + ("D3", "32", 149), + ("C3v", "3m", 156), + ("D3d", "-3m", 162), + ("C6", "6", 168), + ("C3h", "-6", 174), + ("C6h", "6/m", 175), + ("D6", "622", 177), + ("C6v", "6mm", 183), + ("D3h", "-6m2", 189), + ("D6h", "6/mmm", 191), + ("T", "23", 195), + ("Th", "m-3", 200), + ("O", "432", 207), + ("Td", "-43m", 215), + ("Oh", "m-3m", 221), +] + +_SCH_to_HERM = {t[0]: t[1] for t in _PTG_IDS} +_HERM_to_SCH = {t[1]: t[0] for t in _PTG_IDS} +_SPGID_to_SCH = {t[2]: t[0] for t in _PTG_IDS} +_SCH_to_SPGID = {t[0]: t[2] for t in _PTG_IDS} + +sch_symbols = list(_SCH_to_HERM.keys()) + + +def schoenflies_from_hermann(herm_symbol): + """ + Convert from Hermann-Mauguin to Schoenflies. + """ + herm_symbol = herm_symbol.replace(".", "") + schoenflies = _HERM_to_SCH.get(herm_symbol) + if schoenflies is None: + # try rearranging, symbols in spglib can be rearranged vs _HERM_to_SCH dict + # get _HERM_to_SCH key that has the same characters as herm_symbol + # (i.e. same characters, but possibly in a different order) + from collections import Counter + + def find_matching_key(input_str, input_dict): + input_str_counter = Counter(input_str) + for key in input_dict: + if Counter(key) == input_str_counter: + return key + return None + + herm_key = find_matching_key(herm_symbol, _HERM_to_SCH) + if herm_key is not None: + schoenflies = _HERM_to_SCH[herm_key] + + return schoenflies + + +_point_group_order = { + "C1": 1, + "Ci": 2, # aka. S2, -1 in Hermann-Mauguin + "C2": 2, + "Cs": 2, # aka. C1h (m in Hermann-Mauguin) + "C3": 3, + "C4": 4, + "S4": 4, # C4 with improper rotation + "C2h": 4, # 2/m in Hermann-Mauguin + "D2": 4, # 222 in Hermann-Mauguin + "C2v": 4, # mm2 in Hermann-Mauguin + "C3i": 6, # aka. S6, -3 in Hermann-Mauguin + "C6": 6, + "C3h": 6, + "D3": 6, # 32 in Hermann-Mauguin + "C3v": 6, # 3m in Hermann-Mauguin + "D2h": 8, # mmm in Hermann-Mauguin + "C4h": 8, # 4/m in Hermann-Mauguin + "D4": 8, # 422 in Hermann-Mauguin + "C4v": 8, # 4mm in Hermann-Mauguin + "D2d": 8, # 42m in Hermann-Mauguin + "C6h": 12, # 6/m in Hermann-Mauguin + "T": 12, # 23 in Hermann-Mauguin + "D3d": 12, # 3m1 in Hermann-Mauguin + "D6": 12, # 622 in Hermann-Mauguin + "C6v": 12, # 6mm in Hermann-Mauguin + "D3h": 12, # 6m2 in Hermann-Mauguin + "D4h": 16, # 4/mmm in Hermann-Mauguin + "D6h": 24, # 6/mmm in Hermann-Mauguin + "Th": 24, # m3 in Hermann-Mauguin + "O": 24, # 432 in Hermann-Mauguin + "Td": 24, # 43m in Hermann-Mauguin + "Oh": 48, # m3m in Hermann-Mauguin +} + + +def group_order_from_schoenflies(sch_symbol): + """ + Return the order of the point group from the Schoenflies symbol. + + Useful for symmetry and orientational degeneracy analysis. + """ + return _point_group_order[sch_symbol]