3333
3434 from pymatgen .util .typing import SpeciesLike
3535
36- # Load element data from JSON file
36+ # Load element data (periodic table) from JSON file
3737with open (Path (__file__ ).absolute ().parent / "periodic_table.json" , encoding = "utf-8" ) as ptable_json :
38- _pt_data = json .load (ptable_json )
38+ _PT_DATA : dict = json .load (ptable_json )
3939
40- _pt_row_sizes = (2 , 8 , 8 , 18 , 18 , 32 , 32 )
40+ _PT_ROW_SIZES : tuple [ int , ...] = (2 , 8 , 8 , 18 , 18 , 32 , 32 )
4141
42- _madelung = [
42+ # Madelung energy ordering rule (lower to higher energy)
43+ _MADELUNG : list [tuple [int , str ]] = [
4344 (1 , "s" ),
4445 (2 , "s" ),
4546 (2 , "p" ),
@@ -137,21 +138,21 @@ def __init__(self, symbol: SpeciesLike) -> None:
137138 Solid State Communications, 1984.
138139 """
139140 self .symbol = str (symbol )
140- data = _pt_data [symbol ]
141+ data = _PT_DATA [symbol ]
141142
142143 # Store key variables for quick access
143144 self .Z = data ["Atomic no" ]
144145
145146 self ._is_named_isotope = data .get ("Is named isotope" , False )
146147 if self ._is_named_isotope :
147- for sym in _pt_data :
148- if _pt_data [ sym ][ "Atomic no" ] == self .Z and not _pt_data [ sym ] .get ("Is named isotope" , False ):
148+ for sym , info in _PT_DATA . items () :
149+ if info [ "Atomic no" ] == self .Z and not info .get ("Is named isotope" , False ):
149150 self .symbol = sym
150151 break
151152 # For specified/named isotopes, treat the same as named element
152153 # (the most common isotope). Then we pad the data block with the
153154 # entries for the named element.
154- data = {** _pt_data [self .symbol ], ** data }
155+ data = {** _PT_DATA [self .symbol ], ** data }
155156
156157 at_r = data .get ("Atomic radius" , "no data" )
157158 if str (at_r ).startswith ("no data" ):
@@ -452,33 +453,48 @@ def icsd_oxidation_states(self) -> tuple[int, ...]:
452453
453454 @property
454455 def full_electronic_structure (self ) -> list [tuple [int , str , int ]]:
455- """Full electronic structure as list of tuples, in order of increasing
456+ """Full electronic structure in order of increasing
456457 energy level (according to the Madelung rule). Therefore, the final
457458 element in the list gives the electronic structure of the valence shell.
458459
459- For example, the electronic structure for Fe is represented as :
460- [(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
461- (4, "s", 2), (3, "d", 6)].
460+ For example, the full electronic structure for Fe is:
461+ [(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
462+ (4, "s", 2), (3, "d", 6)].
462463
463464 References:
464465 Kramida, A., Ralchenko, Yu., Reader, J., and NIST ASD Team (2023). NIST
465466 Atomic Spectra Database (ver. 5.11). https://physics.nist.gov/asd [2024,
466467 June 3]. National Institute of Standards and Technology, Gaithersburg,
467468 MD. DOI: https://doi.org/10.18434/T4W30F
469+
470+ Returns:
471+ list[tuple[int, str, int]]: A list of tuples representing each subshell,
472+ where each tuple contains:
473+ - `n` (int): Principal quantum number.
474+ - `orbital_type` (str): Orbital type (e.g., "s", "p", "d", "f").
475+ - `electron_count` (int): Number of electrons in the subshell.
468476 """
469- e_str = self .electronic_structure
477+ e_str : str = self .electronic_structure
470478
471- def parse_orbital (orb_str ):
479+ def parse_orbital (orb_str : str ) -> str | tuple [int , str , int ]:
480+ """Parse orbital information from split electron configuration string."""
481+ # Parse valence subshell notation (e.g., "3d6" -> (3, "d", 6))
472482 if match := re .match (r"(\d+)([spdfg]+)(\d+)" , orb_str ):
473483 return int (match [1 ]), match [2 ], int (match [3 ])
484+
485+ # Return core-electron configuration as-is (e.g. "[Ar]")
474486 return orb_str
475487
476- data = [parse_orbital (s ) for s in e_str .split ("." )]
477- if data [0 ][0 ] == "[" :
478- sym = data [0 ].replace ("[" , "" ).replace ("]" , "" )
488+ # Split e_str (e.g. for Fe "[Ar].3d6.4s2" into ["[Ar]", "3d6", "4s2"])
489+ data : list = [parse_orbital (s ) for s in e_str .split ("." )]
490+
491+ # Fully expand core-electron configuration (replace noble gas notation string)
492+ if isinstance (data [0 ], str ):
493+ sym : str = data [0 ].replace ("[" , "" ).replace ("]" , "" )
479494 data = list (Element (sym ).full_electronic_structure ) + data [1 :]
480- # sort the final electronic structure by increasing energy level
481- return sorted (data , key = lambda x : _madelung .index ((x [0 ], x [1 ])))
495+
496+ # Sort the final electronic structure by increasing energy level
497+ return sorted (data , key = lambda x : _MADELUNG .index ((x [0 ], x [1 ])))
482498
483499 @property
484500 def n_electrons (self ) -> int :
@@ -563,7 +579,7 @@ def ground_state_term_symbol(self) -> str:
563579 L_symbols = "SPDFGHIKLMNOQRTUVWXYZ"
564580
565581 term_symbols = self .term_symbols
566- term_symbol_flat = { # type: ignore[var-annotated]
582+ term_symbol_flat : dict = {
567583 term : {
568584 "multiplicity" : int (term [0 ]),
569585 "L" : L_symbols .index (term [1 ]),
@@ -595,7 +611,7 @@ def from_Z(Z: int, A: int | None = None) -> Element:
595611 Returns:
596612 Element with atomic number Z.
597613 """
598- for sym , data in _pt_data .items ():
614+ for sym , data in _PT_DATA .items ():
599615 atomic_mass_num = data .get ("Atomic mass no" ) if A else None
600616 if data ["Atomic no" ] == Z and atomic_mass_num == A :
601617 return Element (sym )
@@ -616,7 +632,7 @@ def from_name(name: str) -> Element:
616632 uk_to_us = {"aluminium" : "aluminum" , "caesium" : "cesium" }
617633 name = uk_to_us .get (name .lower (), name )
618634
619- for sym , data in _pt_data .items ():
635+ for sym , data in _PT_DATA .items ():
620636 if data ["Name" ] == name .capitalize ():
621637 return Element (sym )
622638
@@ -643,7 +659,7 @@ def from_row_and_group(row: int, group: int) -> Element:
643659 Note:
644660 The 18 group number system is used, i.e. noble gases are group 18.
645661 """
646- for sym in _pt_data :
662+ for sym in _PT_DATA :
647663 el = Element (sym )
648664 if 57 <= el .Z <= 71 :
649665 el_pseudo_row = 8
@@ -683,7 +699,7 @@ def row(self) -> int:
683699 return 6
684700 if 89 <= z <= 103 :
685701 return 7
686- for idx , size in enumerate (_pt_row_sizes , start = 1 ):
702+ for idx , size in enumerate (_PT_ROW_SIZES , start = 1 ):
687703 total += size
688704 if total >= z :
689705 return idx
@@ -1161,33 +1177,45 @@ def electronic_structure(self) -> str:
11611177 # robustness
11621178 @property
11631179 def full_electronic_structure (self ) -> list [tuple [int , str , int ]]:
1164- """Full electronic structure as list of tuples, in order of increasing
1180+ """Full electronic structure in order of increasing
11651181 energy level (according to the Madelung rule). Therefore, the final
11661182 element in the list gives the electronic structure of the valence shell.
11671183
1168- For example, the electronic structure for Fe+2 is represented as :
1169- [(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
1170- (3, "d", 6)].
1184+ For example, the full electronic structure for Fe is:
1185+ [(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
1186+ (4, "s", 2), (3, "d", 6)].
11711187
11721188 References:
11731189 Kramida, A., Ralchenko, Yu., Reader, J., and NIST ASD Team (2023). NIST
11741190 Atomic Spectra Database (ver. 5.11). https://physics.nist.gov/asd [2024,
11751191 June 3]. National Institute of Standards and Technology, Gaithersburg,
11761192 MD. DOI: https://doi.org/10.18434/T4W30F
1193+
1194+ Returns:
1195+ list[tuple[int, str, int]]: A list of tuples representing each subshell,
1196+ where each tuple contains:
1197+ - `n` (int): Principal quantum number.
1198+ - `orbital_type` (str): Orbital type (e.g., "s", "p", "d", "f").
1199+ - `electron_count` (int): Number of electrons in the subshell.
11771200 """
1178- e_str = self .electronic_structure
1201+ e_str : str = self .electronic_structure
11791202
1180- def parse_orbital (orb_str ):
1203+ def parse_orbital (orb_str : str ) -> str | tuple [int , str , int ]:
1204+ """Parse orbital information from split electron configuration string."""
1205+ # Parse valence subshell notation (e.g., "3d6" -> (3, "d", 6))
11811206 if match := re .match (r"(\d+)([spdfg]+)(\d+)" , orb_str ):
11821207 return int (match [1 ]), match [2 ], int (match [3 ])
1208+
1209+ # Return core-electron configuration as-is (e.g. "[Ar]")
11831210 return orb_str
11841211
1185- data = [parse_orbital (s ) for s in e_str .split ("." )]
1186- if data [0 ][ 0 ] == "[" :
1212+ data : list = [parse_orbital (s ) for s in e_str .split ("." )]
1213+ if isinstance ( data [0 ], str ) :
11871214 sym = data [0 ].replace ("[" , "" ).replace ("]" , "" )
11881215 data = list (Element (sym ).full_electronic_structure ) + data [1 :]
1189- # sort the final electronic structure by increasing energy level
1190- return sorted (data , key = lambda x : _madelung .index ((x [0 ], x [1 ])))
1216+
1217+ # Sort the final electronic structure by increasing energy level
1218+ return sorted (data , key = lambda x : _MADELUNG .index ((x [0 ], x [1 ])))
11911219
11921220 # NOTE - copied exactly from Element. Refactoring / inheritance may improve
11931221 # robustness
0 commit comments