LCOV - code coverage report
Current view: top level - tests - test_lifetime.py (source / functions) Hit Total Coverage
Test: coverage.info Lines: 32 46 69.6 %
Date: 2026-04-17 09:29:39 Functions: 3 4 75.0 %

          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)

Generated by: LCOV version 1.16