Line data Source code
1 : # SPDX-FileCopyrightText: 2025 PairInteraction Developers 2 : # SPDX-License-Identifier: LGPL-3.0-or-later 3 : 4 1 : from __future__ import annotations 5 : 6 1 : from typing import TYPE_CHECKING 7 : 8 1 : import numpy as np 9 1 : import pytest 10 1 : from pairinteraction import ureg 11 1 : from scipy.optimize import curve_fit 12 : 13 : if TYPE_CHECKING: 14 : from pairinteraction.units import NDArray 15 : 16 : from .utils import PairinteractionModule 17 : 18 1 : from .utils import no_log_propagation 19 : 20 : 21 1 : @pytest.mark.skip("The Yb174_mqdt database currently does not have low lying states.") 22 1 : def test_lifetime(pi_module: PairinteractionModule) -> None: 23 : """Test calculating the lifetime of a state. 24 : 25 : Note that obtaining a reasonable value for the lifetime requires the full database and is not possible with the 26 : reduced database that is included in the repository! 27 : """ 28 0 : ket = pi_module.KetAtom("Yb174_mqdt", n=64, l=0, j=0, m=0) 29 : 30 : # We use n=64 here, because this corresponds to nu~60 and we include 31 : # states with 50<nu<70 in the limited database that comes with the repository. In 32 : # addition, states with nu<20 are included so that the decay to the ground state 33 : # is also captured. Note that the relative error of the calculated 34 : # lifetime versus the actual lifetime is still quite large because of the limited 35 : # number of states. The full database is used and accurate results can be obtained 36 : # if the test is run with `pytest --database-dir "" --download-missing`. 37 : 38 0 : lifetime1 = ket.get_lifetime(temperature=300, temperature_unit="K", unit="us") 39 0 : lifetime2 = ket.get_lifetime(temperature=300 * ureg.K, unit="us") 40 0 : lifetime3 = ket.get_lifetime(temperature=300 * ureg.K) 41 0 : lifetime4 = ket.get_lifetime(unit="us") 42 : 43 0 : assert lifetime1 == lifetime2 == lifetime3.to(ureg.us).magnitude < lifetime4 44 0 : assert pytest.approx(lifetime1, rel=0.15) == 142.04845576112646 # NOSONAR 45 0 : assert pytest.approx(lifetime4, rel=0.15) == 494.1653414977515 # NOSONAR 46 : 47 : 48 1 : def test_lifetime_scaling(pi_module: PairinteractionModule) -> None: 49 : """Test the scaling of the lifetime with the principal quantum number.""" 50 : 51 1 : def fit_function(x: NDArray, a: float, b: float) -> NDArray: 52 1 : return a * x + b 53 : 54 1 : n_list = list(range(60, 70, 1)) 55 : 56 : # S states 57 1 : kets = [pi_module.KetAtom("Rb", n=n, l=0, j=0.5, m=0.5) for n in n_list] 58 1 : nu = [ket.nu for ket in kets] 59 1 : with no_log_propagation("cpp"): # surpress low n state warnings from lifetime calculation 60 1 : lifetimes = [ket.get_lifetime(unit="us") for ket in kets] 61 1 : popt, _ = curve_fit(fit_function, np.log(nu), np.log(lifetimes)) 62 1 : assert np.isclose(popt[0], 3, atol=0.02) 63 : 64 : # Circular states 65 1 : try: 66 1 : kets = [pi_module.KetAtom("Rb", n=n, l=n - 1, j=n - 0.5, m=n - 0.5) for n in n_list] 67 1 : except ValueError as err: 68 : # If the limited database which comes with the repository is used, creating the 69 : # kets will fail because the circular states are not included in the database. 70 : # This is expected. 71 1 : if "No state found" not in str(err): 72 0 : raise 73 : else: 74 0 : nu = [ket.nu for ket in kets] 75 0 : with no_log_propagation("cpp"): # surpress low n state warnings from lifetime calculation 76 0 : lifetimes = [ket.get_lifetime(unit="us") for ket in kets] 77 0 : popt, _ = curve_fit(fit_function, np.log(nu), np.log(lifetimes)) 78 0 : assert np.isclose(popt[0], 5, atol=0.02) 79 : 80 : 81 1 : def test_transition_rates(pi_module: PairinteractionModule) -> None: 82 : """Test calculating transition rates to other states. 83 : 84 : Note that obtaining a reasonable value for the transition rates requires the full database and is not possible 85 : with the reduced database that is included in the repository! 86 : """ 87 1 : ket = pi_module.KetAtom("Yb174_mqdt", n=60, l=0, j=0, m=0) 88 : 89 1 : with no_log_propagation("cpp"): # surpress low n state warnings from lifetime calculation 90 1 : lifetime = ket.get_lifetime(temperature=300, temperature_unit="K", unit="us") 91 1 : kets_sp, rates_sp = ket.get_spontaneous_transition_rates(unit="MHz") 92 1 : kets_bbr, rates_bbr = ket.get_black_body_transition_rates(temperature=300, temperature_unit="K", unit="MHz") 93 : 94 1 : assert len(rates_sp) == len(kets_sp) 95 1 : assert len(rates_bbr) == len(kets_bbr) 96 1 : assert np.isclose(1 / (sum(rates_sp) + sum(rates_bbr)), lifetime)