Skip to content

Commit 75afbb1

Browse files
authored
Composition support formula strings with curly brackets (#4283)
* Composition support formula strings with curly brackets this PR extends the existing bracket normalization for square brackets to curly brackets important e.g. for bulk metallic glass compositions * link source paper for formulas in test doc str * link test from new lines * fix mypy error src/pymatgen/io/vasp/outputs.py:4724: error: Unsupported operand types for < ("int" and "None") [operator] src/pymatgen/io/vasp/outputs.py:4724: note: Right operand is of type "int | None" Found 1 error in 1 file (checked 295 source files)
1 parent 153ccb4 commit 75afbb1

File tree

3 files changed

+19
-3
lines changed

3 files changed

+19
-3
lines changed

src/pymatgen/core/composition.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,9 @@ def _parse_formula(formula: str, strict: bool = True) -> dict[str, float]:
619619
# Square brackets are used in formulas to denote coordination complexes (gh-3583)
620620
formula = formula.replace("[", "(")
621621
formula = formula.replace("]", ")")
622+
# next 2 lines covered by test_curly_bracket_deeply_nested_formulas
623+
formula = formula.replace("{", "(")
624+
formula = formula.replace("}", ")")
622625

623626
def get_sym_dict(form: str, factor: float) -> dict[str, float]:
624627
sym_dict: dict[str, float] = defaultdict(float)

src/pymatgen/io/vasp/outputs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4719,9 +4719,9 @@ def concatenate(
47194719
preamble.append(line)
47204720
elif line == "" or "Direct configuration=" in line:
47214721
poscar = Poscar.from_str("\n".join([*preamble, "Direct", *coords_str]))
4722-
if (
4723-
ionicstep_end is None and ionicstep_cnt >= ionicstep_start
4724-
) or ionicstep_start <= ionicstep_cnt < ionicstep_end:
4722+
if (ionicstep_end is None and ionicstep_cnt >= ionicstep_start) or (
4723+
ionicstep_end is not None and ionicstep_start <= ionicstep_cnt < ionicstep_end
4724+
):
47254725
structures.append(poscar.structure)
47264726
ionicstep_cnt += 1
47274727
coords_str = []

tests/core/test_composition.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,19 @@ def test_isotopes(self):
859859
assert composition.elements[0].oxi_state == 1
860860
assert "Deuterium" in [elem.long_name for elem in composition.elements]
861861

862+
def test_curly_bracket_deeply_nested_formulas(self):
863+
"""Test parsing of bulk metallic glass formulas with complex nested brackets collected in
864+
Ward et al. (2018) https://doi.org/10.1016/j.actamat.2018.08.002.
865+
"""
866+
for formula, expected in {
867+
"{[(Fe0.6Co0.4)0.75B0.2Si0.05]0.96Nb0.04}100": "Nb4 Fe43.2 Co28.8 Si4.8 B19.2",
868+
"{[(Fe0.6Co0.4)0.75B0.2Si0.05]0.96Nb0.04}99Cr1": "Nb3.96 Cr1 Fe42.768 Co28.512 Si4.752 B19.008",
869+
"{[(Fe0.6Co0.4)0.75B0.2Si0.05]0.96Nb0.04}98Cr2": "Nb3.92 Cr2 Fe42.336 Co28.224 Si4.704 B18.816",
870+
"{[(Fe0.6Co0.4)0.75B0.2Si0.05]0.96Nb0.04}97Cr3": "Nb3.88 Cr3 Fe41.904 Co27.936 Si4.656 B18.624",
871+
"{[(Fe0.6Co0.4)0.75B0.2Si0.05]0.96Nb0.04}96Cr4": "Nb3.84 Cr4 Fe41.472 Co27.648 Si4.608 B18.432",
872+
}.items():
873+
assert Composition(formula).formula == expected
874+
862875

863876
def test_reduce_formula():
864877
assert reduce_formula({"Li": 2, "Mn": 4, "O": 8}) == ("LiMn2O4", 2)

0 commit comments

Comments
 (0)