Skip to content

Commit 9604d75

Browse files
author
Shyue Ping Ong
committed
Allow square brackets in composition. Fixes #3583.
1 parent c0c40dd commit 9604d75

File tree

2 files changed

+54
-10
lines changed

2 files changed

+54
-10
lines changed

pymatgen/core/composition.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,8 @@ def _parse_formula(self, formula: str, strict: bool = True) -> dict[str, float]:
544544
raise ValueError(f"Invalid {formula=}")
545545
# for Metallofullerene like "Y3N@C80"
546546
formula = formula.replace("@", "")
547+
formula = formula.replace("[", "(")
548+
formula = formula.replace("]", ")")
547549

548550
def get_sym_dict(form: str, factor: float) -> dict[str, float]:
549551
sym_dict: dict[str, float] = collections.defaultdict(float)
@@ -1009,7 +1011,9 @@ def _get_oxi_state_guesses(self, all_oxi_states, max_sites, oxi_states_override,
10091011
*(
10101012
(y, x)
10111013
for (z, y, x) in sorted(
1012-
zip(all_scores, all_sols, all_oxid_combo), key=lambda pair: pair[0], reverse=True
1014+
zip(all_scores, all_sols, all_oxid_combo),
1015+
key=lambda pair: pair[0],
1016+
reverse=True,
10131017
)
10141018
)
10151019
)

tests/core/test_composition.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,15 @@ def test_init(self):
143143

144144
def test_str_and_repr(self):
145145
test_cases = [
146-
({"Li+": 2, "Mn3+": 2, "O2-": 4}, {"str": "Li+2 Mn3+2 O2-4", "repr": "Composition('Li+:2 Mn3+:2 O2-:4')"}),
146+
(
147+
{"Li+": 2, "Mn3+": 2, "O2-": 4},
148+
{"str": "Li+2 Mn3+2 O2-4", "repr": "Composition('Li+:2 Mn3+:2 O2-:4')"},
149+
),
147150
("H2O", {"str": "H2 O1", "repr": "Composition('H2 O1')"}),
148-
({"Fe3+": 2, "O2-": 3}, {"str": "Fe3+2 O2-3", "repr": "Composition('Fe3+:2 O2-:3')"}),
151+
(
152+
{"Fe3+": 2, "O2-": 3},
153+
{"str": "Fe3+2 O2-3", "repr": "Composition('Fe3+:2 O2-:3')"},
154+
),
149155
("C6H6", {"str": "C6 H6", "repr": "Composition('C6 H6')"}),
150156
]
151157

@@ -154,7 +160,16 @@ def test_str_and_repr(self):
154160
assert repr(Composition(comp)) == expected["repr"]
155161

156162
def test_average_electroneg(self):
157-
electro_negs = (2.7224999999999997, 2.4160000000000004, 2.5485714285714285, 2.21, 2.718, 3.08, 1.21, 2.43)
163+
electro_negs = (
164+
2.7224999999999997,
165+
2.4160000000000004,
166+
2.5485714285714285,
167+
2.21,
168+
2.718,
169+
3.08,
170+
1.21,
171+
2.43,
172+
)
158173
for elem, val in zip(self.comps, electro_negs):
159174
assert elem.average_electroneg == approx(val)
160175

@@ -197,7 +212,10 @@ def test_formula(self):
197212
assert Composition("(C)((C)0.9(B)0.1)") == Composition("C1.9 B0.1")
198213

199214
assert Composition("NaN").reduced_formula == "NaN"
200-
with pytest.raises(ValueError, match=r"float\('NaN'\) is not a valid Composition, did you mean str\('NaN'\)\?"):
215+
with pytest.raises(
216+
ValueError,
217+
match=r"float\('NaN'\) is not a valid Composition, did you mean str\('NaN'\)\?",
218+
):
201219
Composition(float("NaN"))
202220

203221
# test bad formulas raise ValueError
@@ -313,7 +331,10 @@ def test_integer_formula(self):
313331
]
314332
all_formulas = [c.get_integer_formula_and_factor()[0] for c in self.comps]
315333
assert all_formulas == correct_reduced_formulas
316-
assert Composition("Li0.5O0.25").get_integer_formula_and_factor() == ("Li2O", 0.25)
334+
assert Composition("Li0.5O0.25").get_integer_formula_and_factor() == (
335+
"Li2O",
336+
0.25,
337+
)
317338
assert Composition("O0.25").get_integer_formula_and_factor() == ("O2", 0.125)
318339
formula, factor = Composition("Li0.16666667B1.0H1.0").get_integer_formula_and_factor()
319340
assert formula == "Li(BH)6"
@@ -365,7 +386,12 @@ def test_anonymized_formula(self):
365386
assert comp.anonymized_formula == expected_formulas[idx]
366387

367388
def test_get_wt_fraction(self):
368-
correct_wt_frac = {"Li": 0.0498841610868, "Fe": 0.267567687258, "P": 0.222604831158, "O": 0.459943320496}
389+
correct_wt_frac = {
390+
"Li": 0.0498841610868,
391+
"Fe": 0.267567687258,
392+
"P": 0.222604831158,
393+
"O": 0.459943320496,
394+
}
369395
for el in correct_wt_frac:
370396
assert correct_wt_frac[el] == approx(self.comps[0].get_wt_fraction(el)), "Wrong computed weight fraction"
371397
assert self.comps[0].get_wt_fraction(Element("S")) == 0, "Wrong computed weight fractions"
@@ -378,7 +404,11 @@ def test_from_dict(self):
378404
assert comp == comp2
379405

380406
def test_from_weight_dict(self):
381-
weight_dict_list = [{"Ti": 90, "V": 6, "Al": 4}, {"Ni": 60, "Ti": 40}, {"H": 0.1119, "O": 0.8881}]
407+
weight_dict_list = [
408+
{"Ti": 90, "V": 6, "Al": 4},
409+
{"Ni": 60, "Ti": 40},
410+
{"H": 0.1119, "O": 0.8881},
411+
]
382412
formula_list = ["Ti87.6 V5.5 Al6.9", "Ti44.98 Ni55.02", "H2O"]
383413

384414
for weight_dict, formula in zip(weight_dict_list, formula_list):
@@ -492,7 +522,10 @@ def test_comparisons(self):
492522
Fe = Element("Fe")
493523
assert c1 != Fe, NotImplemented
494524
assert c1 != Fe
495-
with pytest.raises(TypeError, match="'<' not supported between instances of 'Composition' and 'Element'"):
525+
with pytest.raises(
526+
TypeError,
527+
match="'<' not supported between instances of 'Composition' and 'Element'",
528+
):
496529
c1 < Fe # noqa: B015
497530

498531
def test_almost_equals(self):
@@ -615,7 +648,10 @@ def test_oxi_state_guesses(self):
615648
# to under the abs(max_sites) number of sites. Will also timeout if
616649
# incorrect.
617650
assert Composition("Sb10000O10000F10000").oxi_state_guesses(max_sites=-3)[0] == {"Sb": 3, "O": -2, "F": -1}
618-
with pytest.raises(ValueError, match="Composition Li1 O1 F1 cannot accommodate max_sites setting"):
651+
with pytest.raises(
652+
ValueError,
653+
match="Composition Li1 O1 F1 cannot accommodate max_sites setting",
654+
):
619655
Composition("LiOF").oxi_state_guesses(max_sites=-2)
620656

621657
with pytest.raises(ValueError, match="Composition V2 O3 cannot accommodate max_sites setting"):
@@ -800,3 +836,7 @@ def test_math(self):
800836
assert pots_x2 - pots == pots
801837
assert fe_pot + o_pot == pots
802838
assert fe_pot - o_pot == pots - o_pot - o_pot
839+
840+
def test_square_brackets(self):
841+
c = Composition("(NH4)2[FeCl5(H2O)]")
842+
assert str(c) == "N2 H10 Fe1 Cl5 O1"

0 commit comments

Comments
 (0)