LCOV - code coverage report
Current view: top level - src/pairinteraction_gui/calculate - calculate_two_atoms.py (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 73 0.0 %
Date: 2025-05-02 21:49:59 Functions: 0 6 0.0 %

          Line data    Source code
       1             : # SPDX-FileCopyrightText: 2025 Pairinteraction Developers
       2             : # SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4           0 : import logging
       5           0 : from typing import TYPE_CHECKING, Optional, Union
       6             : 
       7           0 : from attr import dataclass
       8             : 
       9           0 : from pairinteraction import (
      10             :     complex as pi_complex,
      11             :     real as pi_real,
      12             : )
      13           0 : from pairinteraction_gui.calculate.calculate_base import Parameters, Results
      14           0 : from pairinteraction_gui.worker import run_in_other_process
      15             : 
      16             : if TYPE_CHECKING:
      17             :     from typing_extensions import Self
      18             : 
      19             :     from pairinteraction_gui.page import TwoAtomsPage
      20             : 
      21           0 : logger = logging.getLogger(__name__)
      22             : 
      23             : 
      24           0 : @dataclass
      25           0 : class ParametersTwoAtoms(Parameters["TwoAtomsPage"]):
      26             :     """Parameters for the two atoms calculation."""
      27             : 
      28           0 :     pair_basis_energy_delta: float = 0
      29           0 :     order: int = 3
      30             : 
      31           0 :     @classmethod
      32           0 :     def from_page(cls, page: "TwoAtomsPage") -> "Self":
      33           0 :         obj = super().from_page(page)
      34           0 :         obj.pair_basis_energy_delta = page.basis_config.delta_pair_energy.value()
      35           0 :         obj.order = page.system_config.order.value()
      36           0 :         return obj
      37             : 
      38           0 :     def to_replacement_dict(self) -> dict[str, str]:
      39           0 :         replacements = super().to_replacement_dict()
      40           0 :         replacements["$MULTIPOLE_ORDER"] = str(self.order)
      41           0 :         replacements["$PAIR_ENERGY_DELTA"] = str(self.pair_basis_energy_delta)
      42           0 :         return replacements
      43             : 
      44             : 
      45           0 : @dataclass
      46           0 : class ResultsTwoAtoms(Results):
      47           0 :     basis_0_label: Optional[str] = None
      48             : 
      49             : 
      50           0 : @run_in_other_process
      51           0 : def calculate_two_atoms(parameters: ParametersTwoAtoms) -> ResultsTwoAtoms:
      52             :     """Calculate the energy plot for two atoms.
      53             : 
      54             :     This means, given a Paramaters object, do the pairinteraction calculations and return an ResultsTwoAtoms object.
      55             :     """
      56           0 :     pi = pi_real if parameters.is_real else pi_complex
      57           0 :     n_atoms = 2
      58             : 
      59           0 :     kets = tuple(pi.KetAtom(parameters.get_species(i), **parameters.get_quantum_numbers(i)) for i in range(n_atoms))
      60           0 :     bases = tuple(
      61             :         pi.BasisAtom(parameters.get_species(i), **parameters.get_quantum_number_restrictions(i)) for i in range(n_atoms)
      62             :     )
      63             : 
      64           0 :     fields = {k: v for k, v in parameters.ranges.items() if k in ["Ex", "Ey", "Ez", "Bx", "By", "Bz"]}
      65             : 
      66             :     basis_pair_list: Union[list[pi_real.BasisPair], list[pi_complex.BasisPair]]
      67           0 :     if all(v[0] == v[-1] for v in fields.values()):
      68             :         # If all fields are constant, we can only have to diagonalize one SystemAtom per atom
      69             :         # and can construct one BasisPair, which we can use for all steps
      70           0 :         systems = tuple(
      71             :             pi.SystemAtom(bases[i])
      72             :             .set_electric_field(parameters.get_efield(0), unit="V/cm")
      73             :             .set_magnetic_field(parameters.get_bfield(0), unit="G")
      74             :             for i in range(n_atoms)
      75             :         )
      76           0 :         logger.debug("Diagonalizing SystemAtoms...")
      77           0 :         pi.diagonalize(systems, **parameters.diagonalize_kwargs)
      78           0 :         logger.debug("Done diagonalizing SystemAtoms.")
      79           0 :         ket_pair_energy_0 = sum(systems[i].get_corresponding_energy(kets[i], "GHz") for i in range(n_atoms))
      80           0 :         delta_energy = parameters.pair_basis_energy_delta
      81           0 :         basis_pair = pi.BasisPair(
      82             :             systems,
      83             :             energy=(ket_pair_energy_0 - delta_energy, ket_pair_energy_0 + delta_energy),
      84             :             energy_unit="GHz",
      85             :         )
      86             :         # not very elegant, but works (note that importantly this does not copy the basis_pair objects)
      87           0 :         basis_pair_list = parameters.steps * [basis_pair]
      88             :     else:
      89             :         # Otherwise, we have to diagonalize one SystemAtom per atom and per step
      90             :         # and construct one BasisPair per step
      91           0 :         systems_list = []
      92           0 :         for step in range(parameters.steps):
      93           0 :             systems = tuple(
      94             :                 pi.SystemAtom(bases[i])
      95             :                 .set_electric_field(parameters.get_efield(step), unit="V/cm")
      96             :                 .set_magnetic_field(parameters.get_bfield(step), unit="G")
      97             :                 for i in range(n_atoms)
      98             :             )
      99           0 :             systems_list.append(systems)
     100           0 :         systems_flattened = [system for systems in systems_list for system in systems]
     101           0 :         logger.debug("Diagonalizing SystemAtoms...")
     102           0 :         pi.diagonalize(systems_flattened, **parameters.diagonalize_kwargs)
     103           0 :         logger.debug("Done diagonalizing SystemAtoms.")
     104           0 :         delta_energy = parameters.pair_basis_energy_delta
     105           0 :         basis_pair_list = []
     106           0 :         for step in range(parameters.steps):
     107           0 :             ket_pair_energy = sum(
     108             :                 systems_list[step][i].get_corresponding_energy(kets[i], "GHz") for i in range(n_atoms)
     109             :             )
     110           0 :             basis_pair = pi.BasisPair(
     111             :                 systems_list[step],
     112             :                 energy=(ket_pair_energy - delta_energy, ket_pair_energy + delta_energy),
     113             :                 energy_unit="GHz",
     114             :             )
     115           0 :             basis_pair_list.append(basis_pair)
     116           0 :         ket_pair_energy_0 = sum(systems_list[-1][i].get_corresponding_energy(kets[i], "GHz") for i in range(n_atoms))
     117             : 
     118           0 :     system_pair_list: Union[list[pi_real.SystemPair], list[pi_complex.SystemPair]] = []
     119           0 :     for step in range(parameters.steps):
     120           0 :         system = pi.SystemPair(basis_pair_list[step])
     121           0 :         system.set_interaction_order(parameters.order)
     122           0 :         if "Distance" in parameters.ranges:
     123           0 :             distance = parameters.ranges["Distance"][step]
     124           0 :             angle: float = 0
     125           0 :             if "Angle" in parameters.ranges:
     126           0 :                 angle = parameters.ranges["Angle"][step]
     127           0 :             system.set_distance(distance, angle, unit="micrometer")
     128           0 :         system_pair_list.append(system)
     129             : 
     130           0 :     logger.debug("Diagonalizing SystemPairs...")
     131           0 :     pi.diagonalize(
     132             :         system_pair_list,
     133             :         **parameters.diagonalize_kwargs,
     134             :         **parameters.get_diagonalize_energy_range(ket_pair_energy_0),
     135             :     )
     136           0 :     logger.debug("Done diagonalizing SystemPairs.")
     137             : 
     138           0 :     results = ResultsTwoAtoms.from_calculate(system_pair_list, kets, ket_pair_energy_0)
     139           0 :     results.basis_0_label = (
     140             :         str(basis_pair_list[-1]) + f"\n  ⇒ Basis consists of {basis_pair_list[-1].number_of_kets} kets"
     141             :     )
     142             : 
     143           0 :     return results

Generated by: LCOV version 1.16