diff --git a/src/pymatgen/core/ion.py b/src/pymatgen/core/ion.py index 7ba9ceabaff..03055da8095 100644 --- a/src/pymatgen/core/ion.py +++ b/src/pymatgen/core/ion.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING from monty.json import MSONable + from pymatgen.core.composition import Composition, reduce_formula from pymatgen.util.string import Stringify, charge_string, formula_double_format @@ -173,10 +174,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) - # 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")): + # 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" @@ -189,12 +190,12 @@ def get_reduced_formula_and_factor( formula = "CH3COOH" factor /= 2 # phosphoric acid system - elif formula == "PH3O4(aq)": - formula = "H3PO4(aq)" - elif formula == "PHO4[-2]": - formula = "HPO4[-2]" - elif formula == "P(HO2)2[-1]": - formula = "H2PO4[-1]" + 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" @@ -213,6 +214,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 52aaa199b05..a3b4003a7fb 100644 --- a/tests/core/test_ion.py +++ b/tests/core/test_ion.py @@ -4,6 +4,7 @@ from unittest import TestCase import pytest + from pymatgen.core import Composition, Element from pymatgen.core.ion import Ion @@ -46,6 +47,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,6 +61,7 @@ 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]"), @@ -66,13 +69,17 @@ def test_special_formulas(self): ("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]