Skip to content

Commit 09cf748

Browse files
authored
Element/Species: order full_electron_structure by energy (#3944)
1 parent 27d90b7 commit 09cf748

File tree

4 files changed

+85
-15
lines changed

4 files changed

+85
-15
lines changed

src/pymatgen/analysis/magnetism/jahnteller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def _get_number_of_d_electrons(species: Species) -> float:
345345

346346
# taken from get_crystal_field_spin
347347
elec = species.element.full_electronic_structure
348-
if len(elec) < 4 or elec[-1][1] != "s" or elec[-2][1] != "d":
348+
if len(elec) < 4 or elec[-2][1] != "s" or elec[-1][1] != "d":
349349
raise AttributeError(f"Invalid element {species.symbol} for crystal field calculation.")
350350
n_electrons = int(elec[-1][2] + elec[-2][2] - species.oxi_state) # type: ignore[operator]
351351
if n_electrons < 0 or n_electrons > 10:

src/pymatgen/core/periodic_table.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@
3333

3434
_pt_row_sizes = (2, 8, 8, 18, 18, 32, 32)
3535

36+
_madelung = [
37+
(1, "s"),
38+
(2, "s"),
39+
(2, "p"),
40+
(3, "s"),
41+
(3, "p"),
42+
(4, "s"),
43+
(3, "d"),
44+
(4, "p"),
45+
(5, "s"),
46+
(4, "d"),
47+
(5, "p"),
48+
(6, "s"),
49+
(4, "f"),
50+
(5, "d"),
51+
(6, "p"),
52+
(7, "s"),
53+
(5, "f"),
54+
(6, "d"),
55+
(7, "p"),
56+
]
57+
3658

3759
@functools.total_ordering
3860
@unique
@@ -422,11 +444,12 @@ def icsd_oxidation_states(self) -> tuple[int, ...]:
422444
@property
423445
def full_electronic_structure(self) -> list[tuple[int, str, int]]:
424446
"""Full electronic structure as list of tuples, in order of increasing
425-
principal (n) and angular momentum (l) quantum numbers.
447+
energy level (according to the Madelung rule). Therefore, the final
448+
element in the list gives the electronic structure of the valence shell.
426449
427450
For example, the electronic structure for Fe is represented as:
428451
[(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
429-
(3, "d", 6), (4, "s", 2)].
452+
(4, "s", 2), (3, "d", 6)].
430453
431454
References:
432455
Kramida, A., Ralchenko, Yu., Reader, J., and NIST ASD Team (2023). NIST
@@ -445,7 +468,13 @@ def parse_orbital(orb_str):
445468
if data[0][0] == "[":
446469
sym = data[0].replace("[", "").replace("]", "")
447470
data = list(Element(sym).full_electronic_structure) + data[1:]
448-
return data
471+
# sort the final electronic structure by increasing energy level
472+
return sorted(data, key=lambda x: _madelung.index((x[0], x[1])))
473+
474+
@property
475+
def n_electrons(self) -> int:
476+
"""Total number of electrons in the Element."""
477+
return sum([t[-1] for t in self.full_electronic_structure])
449478

450479
@property
451480
def valence(self) -> tuple[int | np.nan, int]:
@@ -1117,7 +1146,8 @@ def electronic_structure(self) -> str:
11171146
@property
11181147
def full_electronic_structure(self) -> list[tuple[int, str, int]]:
11191148
"""Full electronic structure as list of tuples, in order of increasing
1120-
principal (n) and angular momentum (l) quantum numbers.
1149+
energy level (according to the Madelung rule). Therefore, the final
1150+
element in the list gives the electronic structure of the valence shell.
11211151
11221152
For example, the electronic structure for Fe+2 is represented as:
11231153
[(1, "s", 2), (2, "s", 2), (2, "p", 6), (3, "s", 2), (3, "p", 6),
@@ -1140,7 +1170,15 @@ def parse_orbital(orb_str):
11401170
if data[0][0] == "[":
11411171
sym = data[0].replace("[", "").replace("]", "")
11421172
data = list(Element(sym).full_electronic_structure) + data[1:]
1143-
return data
1173+
# sort the final electronic structure by increasing energy level
1174+
return sorted(data, key=lambda x: _madelung.index((x[0], x[1])))
1175+
1176+
# NOTE - copied exactly from Element. Refactoring / inheritance may improve
1177+
# robustness
1178+
@property
1179+
def n_electrons(self) -> int:
1180+
"""Total number of electrons in the Species."""
1181+
return sum([t[-1] for t in self.full_electronic_structure])
11441182

11451183
# NOTE - copied exactly from Element. Refactoring / inheritance may improve
11461184
# robustness
@@ -1319,7 +1357,7 @@ def get_crystal_field_spin(
13191357
raise ValueError("Invalid coordination or spin config")
13201358

13211359
elec = self.element.full_electronic_structure
1322-
if len(elec) < 4 or elec[-1][1] != "s" or elec[-2][1] != "d":
1360+
if len(elec) < 4 or elec[-2][1] != "s" or elec[-1][1] != "d":
13231361
raise AttributeError(f"Invalid element {self.symbol} for crystal field calculation")
13241362

13251363
assert self.oxi_state is not None

tests/core/test_periodic_table.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ def test_full_electronic_structure(self):
7474
(2, "p", 6),
7575
(3, "s", 2),
7676
(3, "p", 6),
77-
(3, "d", 6),
7877
(4, "s", 2),
78+
(3, "d", 6),
7979
],
8080
"Li": [(1, "s", 2), (2, "s", 1)],
8181
"U": [
@@ -84,19 +84,19 @@ def test_full_electronic_structure(self):
8484
(2, "p", 6),
8585
(3, "s", 2),
8686
(3, "p", 6),
87-
(3, "d", 10),
8887
(4, "s", 2),
88+
(3, "d", 10),
8989
(4, "p", 6),
90-
(4, "d", 10),
9190
(5, "s", 2),
91+
(4, "d", 10),
9292
(5, "p", 6),
93+
(6, "s", 2),
9394
(4, "f", 14),
9495
(5, "d", 10),
95-
(6, "s", 2),
9696
(6, "p", 6),
97+
(7, "s", 2),
9798
(5, "f", 3),
9899
(6, "d", 1),
99-
(7, "s", 2),
100100
],
101101
}
102102
for k, v in cases.items():
@@ -169,6 +169,11 @@ def test_from_row_and_group(self):
169169
for k, v in cases.items():
170170
assert ElementBase.from_row_and_group(v[0], v[1]) == Element(k)
171171

172+
def test_n_electrons(self):
173+
cases = {"O": 8, "Fe": 26, "Li": 3, "Be": 4}
174+
for k, v in cases.items():
175+
assert Element(k).n_electrons == v
176+
172177
def test_valence(self):
173178
cases = {"O": (1, 4), "Fe": (2, 6), "Li": (0, 1), "Be": (0, 2)}
174179
for k, v in cases.items():
@@ -602,18 +607,20 @@ def test_sort(self):
602607

603608
def test_species_electronic_structure(self):
604609
assert Species("Fe", 0).electronic_structure == "[Ar].3d6.4s2"
610+
assert Species("Fe", 0).n_electrons == 26
605611
assert Species("Fe", 0).full_electronic_structure == [
606612
(1, "s", 2),
607613
(2, "s", 2),
608614
(2, "p", 6),
609615
(3, "s", 2),
610616
(3, "p", 6),
611-
(3, "d", 6),
612617
(4, "s", 2),
618+
(3, "d", 6),
613619
]
614620
assert Species("Fe", 0).valence == (2, 6)
615621

616622
assert Species("Fe", 2).electronic_structure == "[Ar].3d6"
623+
assert Species("Fe", 2).n_electrons == 24
617624
assert Species("Fe", 2).full_electronic_structure == [
618625
(1, "s", 2),
619626
(2, "s", 2),
@@ -625,6 +632,7 @@ def test_species_electronic_structure(self):
625632
assert Species("Fe", 2).valence == (2, 6)
626633

627634
assert Species("Fe", 3).electronic_structure == "[Ar].3d5"
635+
assert Species("Fe", 3).n_electrons == 23
628636
assert Species("Fe", 3).full_electronic_structure == [
629637
(1, "s", 2),
630638
(2, "s", 2),
@@ -635,12 +643,36 @@ def test_species_electronic_structure(self):
635643
]
636644
assert Species("Fe", 3).valence == (2, 5)
637645

646+
assert Species("Th", 4).electronic_structure == "[Hg].6p6"
647+
assert Species("Th", 4).full_electronic_structure == [
648+
(1, "s", 2),
649+
(2, "s", 2),
650+
(2, "p", 6),
651+
(3, "s", 2),
652+
(3, "p", 6),
653+
(4, "s", 2),
654+
(3, "d", 10),
655+
(4, "p", 6),
656+
(5, "s", 2),
657+
(4, "d", 10),
658+
(5, "p", 6),
659+
(6, "s", 2),
660+
(4, "f", 14),
661+
(5, "d", 10),
662+
(6, "p", 6),
663+
]
664+
assert Species("Th", 4).valence == (1, 6)
665+
638666
assert Species("Li", 1).electronic_structure == "1s2"
667+
assert Species("Li", 1).n_electrons == 2
639668
# alkali metals, all p
640669
for el in ["Na", "K", "Rb", "Cs"]:
641670
assert Species(el, 1).electronic_structure.split(".")[-1][1::] == "p6", f"Failure for {el} +1"
642671
for el in ["Ca", "Mg", "Ba", "Sr"]:
643672
assert Species(el, 2).electronic_structure.split(".")[-1][1::] == "p6", f"Failure for {el} +2"
673+
# valence shell should be f (l=3) for all lanthanide ions except La+3 and Lu+3
674+
for el in ["Ce", "Nd", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu"]:
675+
assert Species(el, 3).valence[0] == 3, f"Failure for {el} +3"
644676

645677
for el in Element:
646678
for ox in el.common_oxidation_states:

tests/io/vasp/test_inputs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,8 +1099,8 @@ def test_nelectrons(self):
10991099
assert self.psingle_Fe.nelectrons == 8
11001100

11011101
def test_electron_config(self):
1102-
assert self.psingle_Mn_pv.electron_configuration == [(4, "s", 2), (3, "d", 5), (3, "p", 6)]
1103-
assert self.psingle_Fe.electron_configuration == [(4, "s", 2), (3, "d", 6)]
1102+
assert self.psingle_Mn_pv.electron_configuration == [(3, "d", 5), (4, "s", 2), (3, "p", 6)]
1103+
assert self.psingle_Fe.electron_configuration == [(3, "d", 6), (4, "s", 2)]
11041104

11051105
def test_attributes(self):
11061106
for key, val in self.Mn_pv_attrs.items():

0 commit comments

Comments
 (0)