Skip to content

Schrödinger equation minimally coupled to an electromagnetic potential

A Gaussian wave packet of a charge in a prescribed electromagnetic potential, after Schwichtenberg 4.3.

  • Name: charged_schrodinger
  • Field: physics/waves
  • Formalism: ordinary differential equations
  • References: Schwichtenberg 2020

Factory

@register_model(
    field='physics/waves',
    tags=('quantum', 'gauge field', 'electromagnetism', 'wave packet', 'spectral', 'pde'),
    references=('Schwichtenberg 2020',),
    defaults={'until': 8.0, 'dt': 0.1},
)
def charged_schrodinger(
    n_sites: int = 256,
    *,
    length: float = 80.0,
    D: float = 0.5,
    q: float = 1.0,
    momentum: float = 0.0,
    width: float = 2.0,
    A_x: float = 0.0,
    A_t: float = 0.0,
    E: float = 1.0,
    duration: float = 2.0,
) -> System:
    """Return a Gaussian wave packet of a charge in a prescribed electromagnetic potential, after Schwichtenberg 4.3.

    The wave function follows the Schrödinger equation with the covariant derivatives `d - i q A`,
    `(d_t - i q A_t) psi = i D (d_x - i q A_x)^2 psi`, on a periodic line with spectral derivatives. A mode of wave
    number `k` turns at the frequency `D (k - q A_x)^2 - q A_t`, so the packet moves at the group velocity
    `2 D (k - q A_x)`. A constant `A_x` is a pure gauge that only shifts the canonical momentum, while the electric
    field `E` switched on for `duration` changes `A_x` and accelerates the packet at the rate `2 D q E`.

    Args:
        n_sites:
            The number of sites of the periodic lattice.

        length:
            The length of the periodic line, centered on 0.

        D:
            The coefficient of the Laplacian, `ħ / (2 m)`.

        q:
            The electric charge.

        momentum:
            The mean wave number `k` of the packet.

        width:
            The width of the packet.

        A_x:
            The vector potential at time 0.

        A_t:
            The scalar potential.

        E:
            The electric field.

        duration:
            The time the electric field is on.

    Returns:
        system:
            The model, with the real part `packet.u` and the imaginary part `packet.w` of the wave function.

    Examples:
        >>> from skcomplex.models.physics.waves import charged_schrodinger
        >>> charged_schrodinger(64, E=0.5).elements[0].values['E']
        0.5
    """
    spacing = length / n_sites
    positions = spacing * np.arange(n_sites) - length / 2
    envelope = np.exp(-(positions**2) / (4 * width**2)) / (2 * np.pi * width**2) ** 0.25
    psi = envelope * np.exp(1j * momentum * positions)
    values = {
        'u': psi.real,
        'w': psi.imag,
        'D': D,
        'q': q,
        'A_x': A_x,
        'A_t': A_t,
        'E': E,
        'duration': duration,
    }
    space = LatticeSpace(n_sites, spacing=spacing)
    return System(
        elements=[
            FieldElement(
                'packet',
                element=ChargedWaveFunctionElement,
                space=space,
                values=values,
                discretization='spectral',
            ),
        ],
    )

Components

class ChargedWaveFunctionElement(Element):
    """Wave function `psi = u + i w` of a charge `q` in a uniform electromagnetic potential.

    The wave function follows `(d_t - i q A_t) psi = i D (d_x - i q A_x)^2 psi`. The vector potential
    `A_x(t) = A_x - E min(t, duration)` holds a uniform electric field `E` from time 0 to `duration`, and the scalar
    potential `A_t` is constant. Expanded, the equation is
    `psi_t = i D psi_xx + 2 D q A_x psi_x + i (q A_t - D q^2 A_x^2) psi`.

    Args:
        label:
            The label of the site or the field.

        visual:
            How the element is drawn, or `None` for `default_visual_`.

        u:
            The initial real part.

        w:
            The initial imaginary part.

        D:
            The coefficient of the Laplacian, `ħ / (2 m)`.

        q:
            The electric charge.

        A_x:
            The vector potential at time 0.

        A_t:
            The scalar potential.

        E:
            The electric field.

        duration:
            The time the electric field is on.

    Examples:
        >>> from skcomplex.models.physics.waves import ChargedWaveFunctionElement
        >>> ChargedWaveFunctionElement('site').q
        1.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        u: Annotated[float, StateSpec()] = 0.0,
        w: Annotated[float, StateSpec()] = 0.0,
        D: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 0.5,
        q: Annotated[float, ParameterSpec()] = 1.0,
        A_x: Annotated[float, ParameterSpec()] = 0.0,
        A_t: Annotated[float, ParameterSpec()] = 0.0,
        E: Annotated[float, ParameterSpec()] = 0.0,
        duration: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.u = u
        self.w = w
        self.D = D
        self.q = q
        self.A_x = A_x
        self.A_t = A_t
        self.E = E
        self.duration = duration

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the minimally coupled Schrödinger equation for the real and imaginary parts.

        Args:
            s:
                The namespace of the symbols the laws can use.

        Returns:
            laws:
                The laws of the component.
        """
        potential = s.A_x - s.E * ramp(s.t, 0, s.duration, s.duration)
        drift = 2 * s.D * s.q * potential
        phase = s.q * s.A_t - s.D * s.q**2 * potential**2
        return [
            RateLaw(s.u, -s.D * laplacian(s.w) + drift * gradient(s.u, 1)[0] - phase * s.w),
            RateLaw(s.w, s.D * laplacian(s.u) + drift * gradient(s.w, 1)[0] + phase * s.u),
        ]

Equations

packet.u[i]: diff(u_packet(t), t) == -2*(E_packet*where(t < 0, 0, where(t < duration_packet, t, duration_packet)) - A_x_packet)*D_packet*q_packet*partial(u_packet(t), 0) - D_packet*laplacian(w_packet(t)) + ((E_packet*where(t < 0, 0, where(t < duration_packet, t, duration_packet)) - A_x_packet)^2*D_packet*q_packet^2 - A_t_packet*q_packet)*w_packet(t)
packet.w[i]: diff(w_packet(t), t) == -2*(E_packet*where(t < 0, 0, where(t < duration_packet, t, duration_packet)) - A_x_packet)*D_packet*q_packet*partial(w_packet(t), 0) + D_packet*laplacian(u_packet(t)) - ((E_packet*where(t < 0, 0, where(t < duration_packet, t, duration_packet)) - A_x_packet)^2*D_packet*q_packet^2 - A_t_packet*q_packet)*u_packet(t)