Line data Source code
1 : # SPDX-FileCopyrightText: 2024 PairInteraction Developers 2 : # SPDX-License-Identifier: LGPL-3.0-or-later 3 : 4 1 : from __future__ import annotations 5 : 6 1 : import contextlib 7 1 : import logging 8 1 : from pathlib import Path 9 1 : from typing import TYPE_CHECKING, Protocol 10 : 11 1 : import numpy as np 12 1 : import pytest 13 : 14 : if TYPE_CHECKING: 15 : from collections.abc import Callable, Iterator 16 : 17 : import pairinteraction as pi 18 : import pint 19 : from pairinteraction.units import NDArray 20 : 21 : 22 1 : REFERENCE_PATHS = { 23 : "stark_map": Path(__file__).parent.parent / "data" / "reference_stark_map", 24 : "pair_potential": Path(__file__).parent.parent / "data" / "reference_pair_potential", 25 : } 26 : 27 1 : SHRUNK_DATABASE_PATH = (Path(__file__).parent.parent / "data" / "database").resolve() 28 : 29 : 30 1 : def is_shrunk_database_used() -> bool: 31 : """Check whether the shrunk database that comes with the repository is used. 32 : 33 : The shrunk database only contains states within a narrow range of the effective principal quantum number, a 34 : few low-lying states, and only small values of the quantum number l_ryd. Thus, results that depend on the 35 : completeness of the database, like lifetimes, are far off and must not be checked against reference values. 36 : To check these values as well, run the tests with `pytest --database-dir "" --download-missing`. 37 : """ 38 1 : from pairinteraction import Database 39 : 40 1 : return Path(Database.get_global_database().database_dir).resolve() == SHRUNK_DATABASE_PATH 41 : 42 : 43 1 : def skip_value_check_if_shrunk_database() -> None: 44 : """Skip the rest of the current test if the results cannot be checked against reference values. 45 : 46 : Call this directly before the checks of the resulting values, see :func:`is_shrunk_database_used`. The code 47 : before the call is still executed, only the checks are skipped and the test is reported as skipped. 48 : """ 49 1 : if is_shrunk_database_used(): 50 1 : pytest.skip( 51 : "the shrunk database is used, run with `pytest --database-dir '' --download-missing` to include all checks" 52 : ) 53 : 54 : 55 1 : def compare_eigensystem_to_reference( 56 : reference_path: Path, 57 : eigenenergies: NDArray, 58 : overlaps: NDArray | None = None, 59 : eigenvectors: NDArray | None = None, 60 : kets: list[str] | None = None, 61 : ) -> None: 62 1 : n_systems, n_kets = eigenenergies.shape 63 1 : np.testing.assert_allclose(eigenenergies, np.loadtxt(reference_path / "eigenenergies.txt")) 64 : 65 1 : if overlaps is not None: 66 : # Ensure that the overlaps sum up to one 67 1 : np.testing.assert_allclose(np.sum(overlaps, axis=1), np.ones(n_systems)) 68 1 : np.testing.assert_allclose(overlaps, np.loadtxt(reference_path / "overlaps.txt"), atol=1e-7) 69 : 70 1 : if kets is not None: 71 1 : np.testing.assert_equal(kets, np.loadtxt(reference_path / "kets.txt", dtype=str, delimiter="\t")) 72 : 73 1 : if eigenvectors is not None: 74 : # Because of degeneracies, checking the eigenvectors against reference data is complicated. 75 : # Thus, we only check their normalization and orthogonality. 76 1 : cumulative_norm = (np.array(eigenvectors) * np.array(eigenvectors).conj()).sum(axis=1) 77 1 : np.testing.assert_allclose(cumulative_norm, n_kets * np.ones(n_systems)) 78 : 79 : 80 1 : @contextlib.contextmanager 81 1 : def no_log_propagation(logger: logging.Logger | str) -> Iterator[None]: 82 : """Context manager to temporarily disable log propagation for a given logger.""" 83 1 : if isinstance(logger, str): 84 1 : logger = logging.getLogger(logger) 85 1 : old_value = logger.propagate 86 1 : try: 87 1 : logger.propagate = False 88 1 : yield 89 : finally: 90 1 : logger.propagate = old_value 91 : 92 : 93 1 : class PairinteractionModule(Protocol): 94 1 : ureg: pint.UnitRegistry 95 1 : Database: type[pi.Database] 96 1 : KetAtom: type[pi.KetAtom] 97 1 : StateAtom: type[pi.StateAtom] 98 1 : BasisAtom: type[pi.BasisAtom] 99 1 : SystemAtom: type[pi.SystemAtom] 100 1 : KetPair: type[pi.KetPair] 101 1 : BasisPair: type[pi.BasisPair] 102 1 : SystemPair: type[pi.SystemPair] 103 1 : EffectiveSystemPair: type[pi.EffectiveSystemPair] 104 1 : C3: type[pi.C3] 105 1 : C6: type[pi.C6] 106 1 : diagonalize: Callable[..., None]