From 660ba7a584fae5b5d8bb1095614f0104c9d89049 Mon Sep 17 00:00:00 2001 From: Ryan Kingsbury Date: Wed, 31 Jul 2024 11:56:21 -0400 Subject: [PATCH] Fix Ion formula parsing bug; add more special formulas (#3942) * fix Ion parsing bug introduced by commit https://github.com/materialsproject/pymatgen/commit/b4a70eef7666743459a6e242a0efc18762ff3b49 * Ion: special handling of CH4, NH4, N3-, SCN-, formate, oxalate * pre-commit auto-fixes * Element / Species - add n_electrons * Revert "Element / Species - add n_electrons" This reverts commit 97ea1807302fb1dbaa34f012ef07d509dcc541ce. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/pymatgen/core/ion.py | 35 +++++++++++++++++++++++++++++++++-- tests/core/test_ion.py | 9 ++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/pymatgen/core/ion.py b/src/pymatgen/core/ion.py index c3d7cf3271c..52408713463 100644 --- a/src/pymatgen/core/ion.py +++ b/src/pymatgen/core/ion.py @@ -173,9 +173,10 @@ def get_reduced_formula_and_factor( el_amt_dict = {k: int(round(v)) for k, v in comp.get_el_amt_dict().items()} formula, factor = reduce_formula(el_amt_dict, iupac_ordering=iupac_ordering) - if (self.composition.get("H") and self.composition.get("O")) is not None: + # This line checks specifically that the contains an equal amount of O and H. When that is the case, + # they should be displayed as "OH" rather than "HO". + if self.composition.get("H") == self.composition.get("O"): formula = formula.replace("HO", "OH") - if nH2O > 0: formula += f".{nH2O}H2O" @@ -187,6 +188,13 @@ def get_reduced_formula_and_factor( elif formula == "H2CO": formula = "CH3COOH" factor /= 2 + # phosphoric acid system + elif formula == "PH3O4": + formula = "H3PO4" + elif formula == "PHO4": + formula = "HPO4" + elif formula == "P(HO2)2": + formula = "H2PO4" # acetate elif formula == "H3(CO)2": formula = "CH3COO" @@ -205,6 +213,29 @@ def get_reduced_formula_and_factor( elif formula == "O" and factor % 3 == 0: formula = "O3" factor /= 3 + # ammonia + elif formula == "H4N": + formula = "NH4" + elif formula == "H3N": + formula = "NH3" + # methane + elif formula == "H4C": + formula = "CH4" + # thiocyanate + elif formula == "CSN": + formula = "SCN" + # triiodide, nitride, an phosphide + elif formula in ["I", "N", "P"] and self.charge == -1: + formula += "3" + factor /= 3 + # formate # codespell:ignore + elif formula == "HCOO": + formula = "HCO2" + # oxalate + elif formula == "CO2": + formula = "C2O4" + factor *= 2 + # diatomic gases elif formula in {"O", "N", "F", "Cl", "H"} and factor % 2 == 0: formula += "2" factor /= 2 diff --git a/tests/core/test_ion.py b/tests/core/test_ion.py index f501287ed6d..5e5f5482dfb 100644 --- a/tests/core/test_ion.py +++ b/tests/core/test_ion.py @@ -46,6 +46,7 @@ def test_charge_from_formula(self): assert Ion.from_formula("SO42-").charge == -1 assert Ion.from_formula("SO4--").charge == -2 assert Ion.from_formula("SO4[--]").charge == -2 + assert Ion.from_formula("N3-").charge == -1 assert Ion.from_formula("Na[+-+]").charge == 1 @@ -59,19 +60,25 @@ def test_special_formulas(self): ("O3", "O3(aq)"), ("O2", "O2(aq)"), ("N2", "N2(aq)"), + ("NaOH", "NaOH(aq)"), ("H4O4", "H2O2(aq)"), ("OH-", "OH[-1]"), + ("H2PO4-", "H2PO4[-1]"), ("CH3COO-", "CH3COO[-1]"), ("CH3COOH", "CH3COOH(aq)"), ("CH3OH", "CH3OH(aq)"), ("H4CO", "CH3OH(aq)"), + ("CH4", "CH4(aq)"), + ("NH4+", "NH4[+1]"), + ("NH3", "NH3(aq)"), + ("N3-", "N3[-1]"), + ("HCOO-", "HCO2[-1]"), ("C2H6O", "C2H5OH(aq)"), ("C3H8O", "C3H7OH(aq)"), ("C4H10O", "C4H9OH(aq)"), ("Fe(OH)4+", "Fe(OH)4[+1]"), ("Zr(OH)4", "Zr(OH)4(aq)"), ] - for tup in special_formulas: assert Ion.from_formula(tup[0]).reduced_formula == tup[1]