Skip to content

Commit

Permalink
Add CompoundLSCoupling state class
Browse files Browse the repository at this point in the history
  • Loading branch information
xnx committed Apr 30, 2024
1 parent 99499ce commit bd319ed
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 1 deletion.
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setup(
name="pyvalem",
version="2.5.16",
version="2.6",
description="A package for managing simple chemical species and states",
long_description=long_description,
long_description_content_type="text/x-rst",
Expand All @@ -28,6 +28,7 @@
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",
"Operating System :: OS Independent",
],
Expand Down
2 changes: 2 additions & 0 deletions src/pyvalem/states/_state_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .racah_symbol import RacahSymbol
from .rotational_state import RotationalState
from .vibrational_state import VibrationalState
from .compound_LS_coupling import CompoundLSCoupling

# the following has two purposes: keys determine the order in which the
# states are parsed, and the values determine the sorting order of states
Expand All @@ -22,6 +23,7 @@
[
(GenericExcitedState, 0),
(AtomicConfiguration, 1),
(CompoundLSCoupling, 1),
(AtomicTermSymbol, 2),
(DiatomicMolecularConfiguration, 1),
(MolecularTermSymbol, 2),
Expand Down
9 changes: 9 additions & 0 deletions src/pyvalem/states/atomic_term_symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@
integer + pp.Optional(pp.Suppress("/") + "2") + pp.StringEnd()
).setResultsName("Jstr")
atom_parity = pp.Literal("o").setResultsName("parity")
seniority = pp.Suppress("{") + integer.setResultsName("seniority") + pp.Suppress("}")
atom_term = (
pp.Optional(moore_label)
+ atom_Smult
+ atom_Lletter
+ pp.Optional(atom_parity)
+ pp.Optional(seniority)
+ pp.Optional(pp.Suppress("_") + atom_Jstr)
+ pp.StringEnd()
)
Expand All @@ -48,6 +50,7 @@ def __init__(self, state_str):
self.parity = None
self.J = None
self.moore_label = ""
self.seniority = None
self._parse_state(state_str)

def _parse_state(self, state_str):
Expand All @@ -62,6 +65,8 @@ def _parse_state(self, state_str):
self.Lletter = components.Lletter
self.L = atom_L_symbols.index(components.Lletter)
self.parity = components.get("parity")
if components.get("seniority"):
self.seniority = int(components.get("seniority"))
self.moore_label = components.get("moore_label", "")
try:
self.J = parse_fraction(components.Jstr)
Expand Down Expand Up @@ -92,6 +97,8 @@ def html(self):
]
if self.parity:
html_chunks.append("<sup>o</sup>")
if self.seniority:
html_chunks.append(str(self.seniority))
if self.J is not None:
j_str = float_to_fraction(self.J)
html_chunks.append("<sub>{0:s}</sub>".format(j_str))
Expand All @@ -106,6 +113,8 @@ def latex(self):
]
if self.parity:
latex_chunks.append("^o")
if self.seniority:
latex_chunks.append(str(self.seniority))
if self.J is not None:
j_str = float_to_fraction(self.J)
latex_chunks.append("_{{{}}}".format(j_str))
Expand Down
82 changes: 82 additions & 0 deletions src/pyvalem/states/compound_LS_coupling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
The CompoundLSCoupling class, representing a complex atomic state with
multiple configurations coupling within the LS formulism to several
intermediate terms, e.g. "4f(2Fo)5d2(1G)6s(2G)". The final term should
be given as its own AtomicTermSymbol. The full CompoundLSCoupling state
should contain no spaces; NB the different interaction strength cases
detailed in Martin et al. (sec. 11.8.1) are not yet implemented.
W. C. Martin, W. Wiese, A. Kramida, "Atomic Spectroscopy" in "Springer
Handbook of Atomic, Molecular and Optical Physics", G. W. F. Drake (ed.),
https://doi.org/10.1007/978-3-030-73893-8_11
Includes methods for parsing a string into quantum numbers and labels,
creating an HTML representation of the term symbol, etc.
"""

import re

from pyvalem.states._base_state import State, StateParseError
from .atomic_term_symbol import AtomicTermSymbol, AtomicTermSymbolError
from .atomic_configuration import AtomicConfiguration, AtomicConfigurationError

term_patt = r"\((.*?)\)"


class CompoundLSCouplingError(StateParseError):
pass


class CompoundLSCoupling(State):
def __init__(self, state_str):
self.state_str = state_str
self.atomic_configurations = []
self.term_symbols = []
self._parse_state(self.state_str)

def _parse_state(self, state_str):
terms = re.findall(term_patt, state_str)
configs = state_str.split("(")

if len(configs) == 0 or len(terms) == 0:
raise CompoundLSCouplingError

try:
for i, config in enumerate(configs[1:], start=1):
configs[i] = configs[i][len(terms[i - 1]) + 1 :]
except IndexError:
raise CompoundLSCouplingError

if not configs[-1]:
configs = configs[:-1]

try:
self.terms = [AtomicTermSymbol(term) for term in terms]
self.atomic_configurations = [
AtomicConfiguration(config) for config in configs
]
except (AtomicTermSymbolError, AtomicConfigurationError):
raise CompoundLSCouplingError

def __repr__(self):
return self.state_str

@property
def html(self):
html_chunks = []
for config, term in zip(self.atomic_configurations, self.terms):
html_chunks.append(config.html)
html_chunks.append(f"({term.html})")
if len(self.atomic_configurations) > len(self.terms):
html_chunks.append(self.atomic_configurations[-1].html)
return "".join(html_chunks)

@property
def latex(self):
latex_chunks = []
for config, term in zip(self.atomic_configurations, self.terms):
latex_chunks.append(config.latex)
latex_chunks.append(f"({term.latex})")
if len(self.atomic_configurations) > len(self.terms):
latex_chunks.append(self.atomic_configurations[-1].latex)
return "".join(latex_chunks)
15 changes: 15 additions & 0 deletions tests/test_atomic_term_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ def test_moore_label(self):
self.assertEqual(a1.latex, r"z{}^{3}\mathrm{P}^o")
self.assertRaises(AtomicTermSymbolError, AtomicTermSymbol, "A5D")

def test_seniority_label(self):
a0 = AtomicTermSymbol("2D{1}_3/2")
a1 = AtomicTermSymbol("2D{2}_3/2")
a2 = AtomicTermSymbol("4I{3}")
self.assertEqual(a0.seniority, 1)
self.assertEqual(a1.seniority, 2)
self.assertEqual(a2.seniority, 3)

self.assertEqual(a0.html, "<sup>2</sup>D1<sub>3/2</sub>")
self.assertEqual(a0.latex, r"{}^{2}\mathrm{D}1_{3/2}")
self.assertEqual(a1.html, "<sup>2</sup>D2<sub>3/2</sub>")
self.assertEqual(a1.latex, r"{}^{2}\mathrm{D}2_{3/2}")
self.assertEqual(a2.html, "<sup>4</sup>I3")
self.assertEqual(a2.latex, r"{}^{4}\mathrm{I}3")


if __name__ == "__main__":
unittest.main()
56 changes: 56 additions & 0 deletions tests/test_compound_LS_coupling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Unit tests for the Compound LS Coupling module of PyValem
"""

import unittest

from pyvalem.states.atomic_configuration import AtomicConfiguration
from pyvalem.states.atomic_term_symbol import AtomicTermSymbol
from pyvalem.states.compound_LS_coupling import CompoundLSCoupling


class CompoundLSCouplingTest(unittest.TestCase):
def test_compound_LS_coupling(self):
s0 = CompoundLSCoupling("3d10.4f7(8So)4p6.5d(9Do)6s(8Do)7s")
a0 = AtomicConfiguration("3d10.4f7")
a1 = AtomicConfiguration("4p6.5d")
a2 = AtomicConfiguration("6s")
a3 = AtomicConfiguration("7s")
for i, ac in enumerate((a0, a1, a2, a3)):
self.assertEqual(ac, s0.atomic_configurations[i])
self.assertEqual(
s0.html,
"""3d<sup>10</sup>4f<sup>7</sup>(<sup>8</sup>S<sup>o</sup>)4p<sup>6</sup>5d(<sup>9</sup>D<sup>o</sup>)6s(<sup>8</sup>D<sup>o</sup>)7s""",
)
self.assertEqual(
s0.latex,
r"3d^{10}4f^{7}({}^{8}\mathrm{S}^o)4p^{6}5d({}^{9}\mathrm{D}^o)6s({}^{8}\mathrm{D}^o)7s",
)

s1 = CompoundLSCoupling("4f10(3K{2})6s.6p(1Po)")
t0 = AtomicTermSymbol("3K{2}")
t1 = AtomicTermSymbol("1Po")
for i, ts in enumerate((t0, t1)):
self.assertEqual(ts, s1.terms[i])
self.assertEqual(s1.terms[0].seniority, 2)
self.assertEqual(
s1.html,
"""4f<sup>10</sup>(<sup>3</sup>K2)6s6p(<sup>1</sup>P<sup>o</sup>)""",
)
self.assertEqual(
s1.latex, r"4f^{10}({}^{3}\mathrm{K}2)6s6p({}^{1}\mathrm{P}^o)"
)

s2 = CompoundLSCoupling("4d9(2P)5d2(3Fo)")
self.assertEqual(
s2.html,
"""4d<sup>9</sup>(<sup>2</sup>P)5d<sup>2</sup>(<sup>3</sup>F<sup>o</sup>)""",
)
self.assertEqual(
s2.latex, r"4d^{9}({}^{2}\mathrm{P})5d^{2}({}^{3}\mathrm{F}^o)"
)

s3 = CompoundLSCoupling("5p5(2Po_3/2)6d")
self.assertEqual(
s3.html, """5p<sup>5</sup>(<sup>2</sup>P<sup>o</sup><sub>3/2</sub>)6d"""
)
8 changes: 8 additions & 0 deletions tests/test_stateful_species.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ def test_atomic_term_symbols(self):

self.assertRaises(StateParseError, StatefulSpecies, "Ti +2 A5D")

def test_compoundLScoupling_species(self):
ss1 = StatefulSpecies("N 2s2.2p2(3P)6d 4F")
self.assertEqual(ss1.formula.formula, "N")
scl = ss1.states[0]
sat = ss1.states[1]
self.assertEqual(scl.html, "2s<sup>2</sup>2p<sup>2</sup>(<sup>3</sup>P)6d")
self.assertEqual(sat.html, "<sup>4</sup>F")


if __name__ == "__main__":
unittest.main()

0 comments on commit bd319ed

Please sign in to comment.