Source code for pairinteraction.green_tensor.green_tensor_cavity

# SPDX-FileCopyrightText: 2024 PairInteraction Developers
# SPDX-License-Identifier: LGPL-3.0-or-later

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np
import scipy.constants as const
from typing_extensions import override

from pairinteraction.green_tensor.dynamic_green_tensor import dynamic_green_tensor_total
from pairinteraction.green_tensor.green_tensor_base import GreenTensorBase, evaluate_relative_permittivity
from pairinteraction.units import QuantityScalar, ureg

if TYPE_CHECKING:
    from typing_extensions import Self

    from pairinteraction.green_tensor.green_tensor_base import PermittivityLike
    from pairinteraction.units import (
        ArrayLike,
        NDArray,
        PintArray,  # noqa: F401  # required for sphinx
        PintArrayLike,
        PintFloat,
    )


[docs] class GreenTensorCavity(GreenTensorBase): """Green tensor for two atoms in a cavity (between two infinite planar surfaces). Examples: >>> from pairinteraction.green_tensor import GreenTensorCavity >>> gt = GreenTensorCavity([0, 0, 0], [10, 0, 0], z1=-5, z2=5, unit="micrometer") >>> transition_energy = 2 # h * GHz >>> gt_dipole_dipole = gt.get(1, 1, transition_energy, "planck_constant * GHz") >>> print(f"{gt_dipole_dipole[0, 0]:.2f}") -3.84 / bohr """
[docs] def __init__( self, pos1: ArrayLike | PintArrayLike, pos2: ArrayLike | PintArrayLike, z1: float | PintFloat, z2: float | PintFloat, unit: str | None = None, static_limit: bool = False, interaction_order: int = 3, ) -> None: """Create a Green tensor for two atoms inside a planar cavity formed by two infinite surfaces. The two surfaces of the cavity are assumed to be infinite in the x-y plane. If not specified otherwise (see `set_relative_permittivities`), the surfaces are treated as perfect mirrors. Args: pos1: Position of the first atom in the given unit. pos2: Position of the second atom in the given unit. z1: The z-position of the first surface in the given unit. z2: The z-position of the second surface in the given unit. unit: The unit of the distance, e.g. "micrometer". Default None expects a `pint.Quantity`. static_limit: If True, the static limit is used. Default False. interaction_order: The order of interaction, e.g., 3 for dipole-dipole. Defaults to 3. """ super().__init__(pos1, pos2, unit, static_limit, interaction_order) self.surface1_z_au = QuantityScalar.convert_user_to_au(z1, unit, "distance") self.surface2_z_au = QuantityScalar.convert_user_to_au(z2, unit, "distance") # Almost perfect mirrors # TODO make utils be able to handle inf self.surface1_epsilon: PermittivityLike = 1e9 self.surface2_epsilon: PermittivityLike = 1e9
[docs] def set_relative_permittivities( self, epsilon: PermittivityLike, epsilon1: PermittivityLike, epsilon2: PermittivityLike ) -> Self: """Set the relative permittivities of the system. Args: epsilon: The relative permittivity (dimensionless) of the medium inside the cavity. epsilon1: The relative permittivity (dimensionless) of the first surface. epsilon2: The relative permittivity (dimensionless) of the second surface. """ self.epsilon = epsilon self.surface1_epsilon = epsilon1 self.surface2_epsilon = epsilon2 return self
@override def _get_scaled_au(self, kappa1: int, kappa2: int, transition_energy_au: float) -> NDArray: if kappa1 == 1 and kappa2 == 1: return self._get_scaled_dipole_dipole_au(transition_energy_au) raise NotImplementedError("Only dipole-dipole Green tensors are currently implemented.") def _get_scaled_dipole_dipole_au(self, transition_energy_au: float) -> NDArray: """Calculate the dipole dipole Green tensor in cartesian coordinates for a cavity in atomic units. Args: transition_energy_au: The transition energy in atomic units at which to evaluate the Green tensor. Returns: The dipole dipole Green tensor in cartesian coordinates as a 3x3 array in atomic units (i.e. 1/bohr). """ au_to_meter: float = ureg.Quantity(1, "atomic_unit_of_length").to("meter").magnitude pos1_m = np.array(self.pos1_au) * au_to_meter pos2_m = np.array(self.pos2_au) * au_to_meter epsilon = evaluate_relative_permittivity(self.epsilon, transition_energy_au, "hartree") omega_hz = ureg.Quantity(transition_energy_au, "hartree").to("hbar Hz").magnitude z1_m = self.surface1_z_au * au_to_meter z2_m = self.surface2_z_au * au_to_meter epsilon1 = evaluate_relative_permittivity(self.surface1_epsilon, transition_energy_au, "hartree") epsilon2 = evaluate_relative_permittivity(self.surface2_epsilon, transition_energy_au, "hartree") # unit: # m^(-3) [hbar]^(-1) [epsilon_0]^(-1) gt = dynamic_green_tensor_total( pos1_m, pos2_m, z1_m, z2_m, omega_hz, epsilon, epsilon1, epsilon2, only_real_part=True ) to_au = au_to_meter ** (-3) * ((4 * np.pi) ** (-1)) / (const.epsilon_0 * const.hbar) # hbar * epsilon_0 = (4*np.pi)**(-1) in atomic units return np.real(gt) / to_au