Skip to content
Merged
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
19 changes: 4 additions & 15 deletions src/rydstate/angular/angular_ket.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,21 +292,10 @@ def get_qn(self, qn: AngularMomentumQuantumNumbers, *, allow_unknown: bool | Non
if qn in self.quantum_number_names:
qn_value = getattr(self, qn)
else:
for coupled_quantum_numbers in (
self.coupled_quantum_numbers,
AngularKetLS.coupled_quantum_numbers,
AngularKetJJ.coupled_quantum_numbers,
AngularKetFJ.coupled_quantum_numbers,
):
if qn not in coupled_quantum_numbers:
continue

qn_1, qn_2 = coupled_quantum_numbers[qn]
qn_1_value = self.get_qn(qn_1, allow_unknown=True)
qn_2_value = self.get_qn(qn_2, allow_unknown=True)
qn_value = try_trivial_spin_addition(qn_1_value, qn_2_value, None)
if not is_unknown(qn_value):
break
coupling_scheme = get_coupling_scheme_for_quantum_number(qn)
state = self.to_state(coupling_scheme)
if state.calc_std_qn(qn) == 0:
qn_value = state.calc_exp_qn(qn)

allow_unknown = self._allow_unknown if allow_unknown is None else allow_unknown
if not allow_unknown and is_unknown(qn_value):
Expand Down
9 changes: 5 additions & 4 deletions src/rydstate/rydberg_state/rydberg_ket.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def core_state(self) -> RydbergStateSQDT[Any] | None:

ion_species = f"{self.species}_ion"
try:
ion_sqdt = get_sqdt(ion_species)
core_sqdt = get_sqdt(ion_species)
except ValueError:
logger.warning(
"No SQDT data available for the ion species of %s, "
Expand All @@ -198,14 +198,15 @@ def core_state(self) -> RydbergStateSQDT[Any] | None:
if is_unknown(l_c) or is_unknown(j_c) or is_unknown(f_c):
return None

angular_ket = AngularKetLS(l_r=l_c, j_tot=j_c, f_tot=f_c, species=ion_species)
# The core electron of the neutral atom is the valence electron of the ion, with total angular momentum j_c.
core_angular_ket = AngularKetLS(l_r=l_c, j_tot=j_c, f_tot=f_c, species=ion_species)

# TODO: we should probably also store n_c for the core angular ket in the future
# for now, it is correct to assume that the core electron is
# in the lowest allowed shell of the ion for the given l_c
for n_c in range(l_c + 1, l_c + 15):
if ion_sqdt.is_allowed_shell(n_c, l_c, 0.5):
return RydbergStateSQDT(ion_species, n_c, angular_ket=angular_ket, sqdt=ion_sqdt)
if core_sqdt.is_allowed_shell(n_c, l_c, 0.5):
return RydbergStateSQDT(ion_species, n_c, angular_ket=core_angular_ket, sqdt=core_sqdt)

raise ValueError(f"No allowed shell found for ion species {ion_species} with l_c={l_c}.")

Expand Down
10 changes: 5 additions & 5 deletions src/rydstate/species/strontium_ion/potential_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ class _PotentialMarinescu1994StrontiumIonAbstract(PotentialMarinescu1994):
# Note that the potential there is defined with Marinescu a_j = Greene \alpha_i as follows:
# a_1 = \alpha_1
# a_2 = \alpha_3
# a_3 = \alpha_2
# a_3 = -\alpha_2 (Note the minus sign!)
# and a_4 = 0
alpha_c_marinescu_1994 = 7.5
r_c_dict_marinescu_1994: ClassVar = {0: 1.7965, 1: 1.3960, 2: 1.6820, 3: 1.0057}
model_potential_parameter_marinescu_1994: ClassVar = {
0: (3.4187, 1.5915, 4.7332, 0),
1: (3.3235, 1.5712, 2.2539, 0),
2: (3.2533, 1.5996, 3.2330, 0),
3: (5.3540, 5.6624, 7.9517, 0),
0: (3.4187, 1.5915, -4.7332, 0),
1: (3.3235, 1.5712, -2.2539, 0),
2: (3.2533, 1.5996, -3.2330, 0),
3: (5.3540, 5.6624, -7.9517, 0),
}


Expand Down
38 changes: 38 additions & 0 deletions tests/test_ion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import logging

import pytest
from rydstate import RydbergStateSQDT
from rydstate.species import get_element_properties, get_potential_class, get_sqdt
from rydstate.species.element_properties import ElementProperties
from rydstate.species.utils import calc_energy_from_nu, calc_nu_from_energy, get_all_subclasses
Expand Down Expand Up @@ -42,3 +45,38 @@ def test_ion_nist_data_is_loaded(species: str) -> None:
assert (5, 2, 1.5, 0.5) in levels # 4f14.5d 2D3/2
# only doublets (s_tot = 1/2) since Yb+ is alkali-like (closed-shell 4f14 core)
assert all(s_tot == 0.5 for (_n, _l, _j, s_tot) in levels)


# Low-lying Sr+ states (n, l_r) whose energies come from the NIST data (no quantum defects are defined
# for the ion, so nu is taken from the measured levels and the radial shape from the model potential).
# The expected number of radial nodes of a state is n - l - 1.
STRONTIUM_ION_LOW_LYING_STATES = [
(5, 0), # 5s, ground state of Sr+
(6, 0), # 6s
(7, 0), # 7s
(5, 1), # 5p
(6, 1), # 6p
(4, 2), # 4d
(5, 2), # 5d
(4, 3), # 4f
]


@pytest.mark.parametrize(("n", "l_r"), STRONTIUM_ION_LOW_LYING_STATES)
def test_strontium_ion_radial_wavefunction(n: int, l_r: int, caplog: pytest.LogCaptureFixture) -> None:
"""The integrated Sr+ wavefunction is normalized, has n - l - 1 nodes and raises no warnings."""
j_tot = l_r + 0.5
state = RydbergStateSQDT("Sr88_ion", n=n, l_r=l_r, j_tot=j_tot, f_tot=j_tot)
radial = state.radial

with caplog.at_level(logging.WARNING):
radial.integrate_wavefunction()

# No sanity-check (or any other) warning should be emitted during the integration.
warnings = [record.getMessage() for record in caplog.records if record.levelno >= logging.WARNING]
assert warnings == [], f"Unexpected warnings for Sr88_ion n={n} l={l_r}:\n" + "\n".join(warnings)

# The wavefunction is correctly integrated and normalized ...
assert radial.norm == pytest.approx(1.0)
# ... and has exactly n - l - 1 nodes with the (sign-corrected) model potential.
assert radial.nodes == n - l_r - 1
Loading