Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Breaking/Fix] Skip isotopes when iterating through core.Element #4180

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -878,7 +883,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
Loading