Skip to content

Commit 57842a7

Browse files
kaueltzenjanosh
andauthored
Modified CifParser.check() as one possible solution for issue materialsproject#3626 (materialsproject#3628)
* Modified check method of CifParser as one possible way to fix issue 3626. * Reincluded all CifParser check() checks but for relative stoichiometry in case of missing formula keys. * Added CifParser test for skipping relative stoichiometry check in case of missing formula keys and for appending a warning to self.warnings in this case. * Refactor CifParser.check * tighter warning test --------- Co-authored-by: Janosh Riebesell <[email protected]>
1 parent 05e8abb commit 57842a7

File tree

2 files changed

+33
-17
lines changed

2 files changed

+33
-17
lines changed

pymatgen/io/cif.py

+24-17
Original file line numberDiff line numberDiff line change
@@ -1313,13 +1313,6 @@ def has_errors(self):
13131313
def check(self, structure: Structure) -> str | None:
13141314
"""Check whether a structure constructed from CIF passes sanity checks.
13151315
1316-
Args:
1317-
structure (Structure) : structure created from CIF
1318-
1319-
Returns:
1320-
str | None: If any check fails, on output, returns a human-readable str for the
1321-
reason why (e.g., which elements are missing). Returns None if all checks pass.
1322-
13231316
Checks:
13241317
- Composition from CIF is valid
13251318
- CIF composition contains only valid elements
@@ -1329,19 +1322,30 @@ def check(self, structure: Structure) -> str | None:
13291322
- CIF and structure have same relative stoichiometry. Thus
13301323
if CIF reports stoichiometry LiFeO, and the structure has
13311324
composition (LiFeO)4, this check passes.
1325+
1326+
Args:
1327+
structure (Structure) : structure created from CIF
1328+
1329+
Returns:
1330+
str | None: If any check fails, on output, returns a human-readable str for the
1331+
reason why (e.g., which elements are missing). Returns None if all checks pass.
13321332
"""
13331333
failure_reason = None
13341334

13351335
cif_as_dict = self.as_dict()
13361336
head_key = next(iter(cif_as_dict))
13371337

13381338
cif_formula = None
1339+
check_stoichiometry = True
13391340
for key in ("_chemical_formula_sum", "_chemical_formula_structural"):
13401341
if cif_as_dict[head_key].get(key):
13411342
cif_formula = cif_as_dict[head_key][key]
13421343
break
13431344

1345+
# In case of missing CIF formula keys, get non-stoichiometric formula from
1346+
# unique sites and skip relative stoichiometry check (added in gh-3628)
13441347
if cif_formula is None and cif_as_dict[head_key].get("_atom_site_type_symbol"):
1348+
check_stoichiometry = False
13451349
cif_formula = " ".join(cif_as_dict[head_key]["_atom_site_type_symbol"])
13461350

13471351
try:
@@ -1370,17 +1374,20 @@ def check(self, structure: Structure) -> str | None:
13701374
failure_reason = f"Missing elements {missing_str} {addendum}"
13711375

13721376
elif not all(struct_comp[elt] - orig_comp[elt] == 0 for elt in orig_comp):
1373-
# Check that stoichiometry is same, i.e., same relative ratios of elements
1374-
ratios = {elt: struct_comp[elt] / orig_comp[elt] for elt in orig_comp_elts}
1375-
1376-
same_stoich = all(
1377-
abs(ratios[elt_a] - ratios[elt_b]) < self.comp_tol
1378-
for elt_a in orig_comp_elts
1379-
for elt_b in orig_comp_elts
1380-
)
1377+
if check_stoichiometry:
1378+
# Check that CIF/PMG stoichiometry has same relative ratios of elements
1379+
ratios = {elt: struct_comp[elt] / orig_comp[elt] for elt in orig_comp_elts}
1380+
1381+
same_stoich = all(
1382+
abs(ratios[elt_a] - ratios[elt_b]) < self.comp_tol
1383+
for elt_a in orig_comp_elts
1384+
for elt_b in orig_comp_elts
1385+
)
13811386

1382-
if not same_stoich:
1383-
failure_reason = f"Incorrect stoichiometry:\n CIF={orig_comp}\n PMG={struct_comp}\n {ratios=}"
1387+
if not same_stoich:
1388+
failure_reason = f"Incorrect stoichiometry:\n CIF={orig_comp}\n PMG={struct_comp}\n {ratios=}"
1389+
else:
1390+
self.warnings += ["Skipping relative stoichiometry check because CIF does not contain formula keys."]
13841391

13851392
return failure_reason
13861393

tests/io/test_cif.py

+9
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ def test_cif_parser(self):
193193

194194
with open(f"{TEST_FILES_DIR}/FePO4.cif") as cif_file:
195195
cif_str = cif_file.read()
196+
196197
parser = CifParser.from_str(cif_str)
197198
struct = parser.parse_structures(primitive=False)[0]
198199
assert struct.formula == "Fe4 P4 O16"
@@ -933,6 +934,14 @@ def test_invalid_cif_composition(self):
933934
failure_reason = cif.check(Structure.from_file(f"{TEST_FILES_DIR}/LiFePO4.cif"))
934935
assert failure_reason == "'X' is not a valid Element"
935936

937+
def test_skipping_relative_stoichiometry_check(self):
938+
cif = CifParser(f"{TEST_FILES_DIR}/Li10GeP2S12.cif")
939+
struct = cif.parse_structures()[0]
940+
failure_reason = cif.check(struct)
941+
assert failure_reason is None
942+
assert len(cif.warnings) == 2
943+
assert cif.warnings[-1] == "Skipping relative stoichiometry check because CIF does not contain formula keys."
944+
936945
def test_cif_writer_site_properties(self):
937946
# check CifWriter(write_site_properties=True) adds Structure site properties to
938947
# CIF with _atom_site_ prefix

0 commit comments

Comments
 (0)