Skip to content

Commit

Permalink
[Breaking/Fix] Skip isotopes when iterating through core.Element (#…
Browse files Browse the repository at this point in the history
…4180)

* try to overwrite iter

* add test

* enhance module documentation, remove ptable class

* also check full members

* also check full members

* inherit from EnumType

* avoid hard-coding isotopes

* revert to enummeta

* add named_isotopes property

* fix incorrect merge

---------

Co-authored-by: Shyue Ping Ong <[email protected]>
  • Loading branch information
DanielYang59 and shyuep authored Jan 9, 2025
1 parent 777a6b2 commit 27230b1
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 3 deletions.
24 changes: 21 additions & 3 deletions src/pymatgen/core/periodic_table.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
"""Classes representing Element, Species (Element + oxidation state) and PeriodicTable."""
"""Classes representing:
- Element: Element in the periodic table.
- Species: Element with optional oxidation state and spin.
- DummySpecies: Non-traditional Elements/Species (vacancies/...).
- ElementType: element types (metal/noble_gas/halogen/s_block/...).
"""

from __future__ import annotations

Expand All @@ -8,7 +13,7 @@
import re
import warnings
from collections import Counter
from enum import Enum, unique
from enum import Enum, EnumMeta, unique
from itertools import combinations, product
from pathlib import Path
from typing import TYPE_CHECKING, overload
Expand Down Expand Up @@ -864,7 +869,20 @@ def print_periodic_table(filter_function: Callable | None = None) -> None:
print(" ".join(row_str))


class Element(ElementBase):
class _ElementMeta(EnumMeta):
"""Override Element to handle isotopes."""

def __iter__(cls):
"""Skip named isotopes when iterating."""
return (elem for elem in super().__iter__() if not elem._is_named_isotope)

@property
def named_isotopes(cls):
"""Get all named isotopes."""
return tuple(elem for elem in super().__iter__() if elem._is_named_isotope)


class Element(ElementBase, metaclass=_ElementMeta):
"""Enum representing an element in the periodic table."""

# This name = value convention is redundant and dumb, but unfortunately is
Expand Down
21 changes: 21 additions & 0 deletions tests/core/test_periodic_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ def test_init(self):

assert id(Element("Fe")) == id(Element("Fe")) # Test caching

def test_iter(self):
# Make sure isotopes don't show during iteration
assert [elem.name for elem in Element] == (
"H,He,Li,Be,B,C,N,O,F,Ne,Na,Mg,Al,Si,P,S,Cl,Ar,K,Ca,Sc,Ti,V,Cr,"
"Mn,Fe,Co,Ni,Cu,Zn,Ga,Ge,As,Se,Br,Kr,Rb,Sr,Y,Zr,Nb,Mo,Tc,Ru,Rh,Pd,"
"Ag,Cd,In,Sn,Sb,Te,I,Xe,Cs,Ba,La,Ce,Pr,Nd,Pm,Sm,Eu,Gd,Tb,Dy,Ho,Er,Tm,"
"Yb,Lu,Hf,Ta,W,Re,Os,Ir,Pt,Au,Hg,Tl,Pb,Bi,Po,At,Rn,Fr,Ra,Ac,Th,Pa,U,Np,"
"Pu,Am,Cm,Bk,Cf,Es,Fm,Md,No,Lr,Rf,Db,Sg,Bh,Hs,Mt,Ds,Rg,Cn,Nh,Fl,Mc,Lv,Ts,Og"
).split(",")

# Make sure isotopes are still in members
assert list(Element.__members__) == (
"H,D,T,He,Li,Be,B,C,N,O,F,Ne,Na,Mg,Al,Si,P,S,Cl,Ar,K,Ca,Sc,Ti,V,Cr,"
"Mn,Fe,Co,Ni,Cu,Zn,Ga,Ge,As,Se,Br,Kr,Rb,Sr,Y,Zr,Nb,Mo,Tc,Ru,Rh,Pd,"
"Ag,Cd,In,Sn,Sb,Te,I,Xe,Cs,Ba,La,Ce,Pr,Nd,Pm,Sm,Eu,Gd,Tb,Dy,Ho,Er,Tm,"
"Yb,Lu,Hf,Ta,W,Re,Os,Ir,Pt,Au,Hg,Tl,Pb,Bi,Po,At,Rn,Fr,Ra,Ac,Th,Pa,U,Np,"
"Pu,Am,Cm,Bk,Cf,Es,Fm,Md,No,Lr,Rf,Db,Sg,Bh,Hs,Mt,Ds,Rg,Cn,Nh,Fl,Mc,Lv,Ts,Og"
).split(",")

def test_missing_and_confusing_data(self):
with pytest.warns(UserWarning, match="No data available"):
_ = Element.H.metallic_radius
Expand Down Expand Up @@ -391,6 +410,8 @@ def test_isotope(self):
3.0155007134,
]

assert Element.named_isotopes == (Element.D, Element.T)


class TestSpecies(PymatgenTest):
def setUp(self):
Expand Down

0 comments on commit 27230b1

Please sign in to comment.