LCOV - code coverage report
Current view: top level - tests - test_green_tensor_interpolator.py (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 60 60
Test Date: 2026-11-02 09:44:19 Functions: 100.0 % 3 3

            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 : from typing import TYPE_CHECKING
       7              : 
       8            1 : import numpy as np
       9            1 : import pytest
      10            1 : from pairinteraction.units import ureg
      11              : 
      12              : if TYPE_CHECKING:
      13              :     from pairinteraction.green_tensor.green_tensor_interpolator import GreenTensorInterpolator
      14              :     from pairinteraction.units import PintFloat
      15              : 
      16              :     from .utils import PairinteractionModule
      17              : 
      18              : 
      19            1 : @pytest.mark.parametrize("distance_mum", [1, 2, 11])
      20            1 : def test_static_green_tensor_interpolator(
      21              :     pi_module: PairinteractionModule,
      22              :     green_tensor_interpolator_class: type[GreenTensorInterpolator],
      23              :     distance_mum: float,
      24              : ) -> None:
      25              :     """Test calculating a pair potential using a user-defined static green tensor interpolator."""
      26              :     # Create a single-atom system
      27            1 :     basis = pi_module.BasisAtom("Rb", n=(58, 62), l=(0, 2))
      28            1 :     system = pi_module.SystemAtom(basis)
      29              : 
      30              :     # Create two-atom basis
      31            1 :     ket = pi_module.KetAtom("Rb", n=60, l=0, m=0.5)
      32            1 :     delta_energy = 3  # GHz
      33            1 :     min_energy = 2 * ket.get_energy(unit="GHz") - delta_energy
      34            1 :     max_energy = 2 * ket.get_energy(unit="GHz") + delta_energy
      35            1 :     basis_pair = pi_module.BasisPair([system, system], energy=(min_energy, max_energy), energy_unit="GHz", m=(1, 1))
      36              : 
      37              :     # Create a system using a user-defined green tensor interpolator for dipole-dipole interaction
      38            1 :     gti = green_tensor_interpolator_class()
      39            1 :     omega = ureg.Quantity(1, "Hz")
      40            1 :     distance_au = ureg.Quantity(distance_mum, "micrometer").to("bohr").m
      41              : 
      42            1 :     tensor = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -2]]) / (distance_au**3)
      43            1 :     tensor_unit = "hartree / (e * bohr)^2"
      44            1 :     gti.set_constant(1, 1, tensor, tensor_unit)
      45              : 
      46            1 :     tensor_spherical = gti.get(1, 1, omega, unit=tensor_unit, scaled=True, coordinates="spherical")
      47            1 :     tensor_spherical_ref = np.array([[1, 0, 0], [0, -2, 0], [0, 0, 1]]) / distance_au**3
      48            1 :     np.testing.assert_allclose(tensor_spherical, tensor_spherical_ref)
      49              : 
      50            1 :     tensor_spherical = gti.get(1, 1, ureg.Quantity(2.5, "GHz"), unit=tensor_unit, scaled=True, coordinates="spherical")
      51            1 :     np.testing.assert_allclose(tensor_spherical, tensor_spherical_ref)
      52              : 
      53            1 :     system_pairs = pi_module.SystemPair(basis_pair)
      54            1 :     system_pairs._cpp.set_green_tensor_interpolator(gti._cpp)
      55              : 
      56              :     # Create a reference system using the build in dipole-dipole interaction
      57            1 :     system_pairs_reference = (
      58              :         pi_module.SystemPair(basis_pair).set_interaction_order(3).set_distance(distance_mum, unit="micrometer")
      59              :     )
      60              : 
      61              :     # Check that the two systems are equivalent. Couplings that vanish analytically might be stored with rounding
      62              :     # errors in one system but not the other, so dense matrices are compared with an absolute tolerance.
      63            1 :     np.testing.assert_allclose(
      64              :         system_pairs.get_hamiltonian(unit="GHz").toarray(),
      65              :         system_pairs_reference.get_hamiltonian(unit="GHz").toarray(),
      66              :         atol=1e-8,
      67              :     )
      68              : 
      69              : 
      70            1 : @pytest.mark.parametrize("distance_mum", [1, 2, 11])
      71            1 : def test_omega_dependent_green_tensor_interpolator(
      72              :     green_tensor_interpolator_class: type[GreenTensorInterpolator],
      73              :     distance_mum: float,
      74              : ) -> None:
      75              :     """Test the interpolation for different values of the transition energy."""
      76              :     # Define an simple linear omega-dependent green tensor interpolator
      77              :     # note that at least four entries are needed for the applied spline interpolation.
      78            1 :     gti = green_tensor_interpolator_class()
      79            1 :     transition_energies = [ureg.Quantity(i, "planck_constant * GHz") for i in np.linspace(1, 5, 20)]
      80            1 :     tensors = [
      81              :         np.array([[1, 0, 0], [0, 1, 0], [0, 0, -2]]) * i / (freq.m**2 * distance_mum**3)
      82              :         for (i, freq) in enumerate(transition_energies, start=1)
      83              :     ]
      84            1 :     tensors_pint = [ureg.Quantity(tensor, "1/micrometer") for tensor in tensors]
      85            1 :     gti.set_list(1, 1, tensors_pint, transition_energies)
      86              : 
      87              :     # Check the interpolation
      88            1 :     tensors_spherical = [
      89              :         np.array([[1, 0, 0], [0, -2, 0], [0, 0, 1]]) * i / (freq.m**2 * distance_mum**3)
      90              :         for (i, freq) in enumerate(transition_energies, start=1)
      91              :     ]
      92              : 
      93              :     # the interpolation near the edges is bad, so we only check the middle
      94            1 :     for idx in range(3, len(transition_energies) - 3):
      95            1 :         tensor = gti.get(1, 1, transition_energies[idx], unit="1/micrometer", coordinates="spherical")
      96            1 :         np.testing.assert_allclose(tensor, tensors_spherical[idx])
      97              : 
      98            1 :     for ind in range(3, len(transition_energies) - 5):
      99            1 :         ind1, ind2 = ind, ind + 1
     100            1 :         transition_energy: PintFloat = (transition_energies[ind1] + transition_energies[ind2]) / 2.0
     101            1 :         reference_tensor = (tensors_spherical[ind1] + tensors_spherical[ind2]) / 2
     102              : 
     103            1 :         tensor = gti.get(1, 1, transition_energy, unit="1/micrometer", coordinates="spherical")
     104            1 :         np.testing.assert_allclose(tensor, reference_tensor, rtol=2e-2)
     105              : 
     106              : 
     107            1 : def test_system_atom_green_tensor_interpolator(
     108              :     pi_module: PairinteractionModule,
     109              :     green_tensor_interpolator_class: type[GreenTensorInterpolator],
     110              : ) -> None:
     111              :     """Test self interaction in a single-atom system from a user-defined Green tensor interpolator."""
     112            1 :     ket1 = pi_module.KetAtom("Rb", n=60, l=0, j=0.5, m=0.5)
     113            1 :     ket2 = pi_module.KetAtom("Rb", n=60, l=1, j=0.5, m=0.5)
     114              : 
     115            1 :     basis = pi_module.BasisAtom("Rb", n=(0, 0), additional_kets=[ket1, ket2])
     116            1 :     system = pi_module.SystemAtom(basis)
     117            1 :     reference_hamiltonian = system.get_hamiltonian("hartree").toarray()
     118              : 
     119            1 :     dipole = ket1.get_matrix_element(ket2, "electric_dipole", q=0, unit="e * a0")
     120            1 :     green_tensor_zz = 2.5e-12
     121              : 
     122            1 :     gti = green_tensor_interpolator_class()
     123            1 :     gti.set_constant(
     124              :         1,
     125              :         1,
     126              :         np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, green_tensor_zz]]),
     127              :         "hartree / (e * a0)^2",
     128              :     )
     129              : 
     130            1 :     system._cpp.set_green_tensor_interpolator(gti._cpp)
     131            1 :     hamiltonian = system.get_hamiltonian("hartree").toarray()
     132            1 :     expected_shift = green_tensor_zz * abs(dipole) ** 2 / 2
     133            1 :     np.testing.assert_allclose(hamiltonian, reference_hamiltonian + expected_shift * np.eye(2))
        

Generated by: LCOV version 2.0-1