Source code for fdtdx.objects.boundaries.pec

import jax
from typing_extensions import override

from fdtdx.colors import XKCD_RED, Color
from fdtdx.core.jax.pytrees import autoinit, frozen_field
from fdtdx.objects.boundaries.boundary import BaseBoundary


[docs] @autoinit class PerfectElectricConductor(BaseBoundary): """Implements perfect electric conductor (PEC) boundary conditions. PEC enforces E_tangential = 0 at the boundary wall. Zero-padding provides the correct ghost cell values for H (and for E_normal), but the curl_H computation at the boundary produces nonzero updates for tangential E components. This class explicitly zeros them after each E update. Component zeroing per axis: - PEC on x-face: zero Ey, Ez (tangential to x) - PEC on y-face: zero Ex, Ez (tangential to y) - PEC on z-face: zero Ex, Ey (tangential to z) Note on dispersive media: the tangential polarization ``P`` at a PEC-adjacent cell can be left one step out of sync with the freshly zeroed tangential ``E`` because ``P`` is updated from ``E_prev``. This converges on the next E-update — since ``E_tangential`` is clamped to zero the next ``P`` relaxes back toward zero, which is the physically correct behavior at a perfect conductor. """ #: RGB color tuple for visualization. Defaults to red. color: Color | None = frozen_field(default=XKCD_RED) @property @override def descriptive_name(self) -> str: """Gets a human-readable name describing this PEC boundary's location. Returns: str: Description like "min_x" or "max_z" indicating position """ axis_str = "x" if self.axis == 0 else "y" if self.axis == 1 else "z" direction_str = "min" if self.direction == "-" else "max" return f"{direction_str}_{axis_str}" @property @override def thickness(self) -> int: """Gets the thickness of the PEC boundary layer in grid points. Returns: int: Number of grid points in the boundary layer (always 1 for PEC) """ return 1 @property def tangential_components(self) -> tuple[int, int]: """Gets the indices of E field components tangential to this boundary. Returns: tuple[int, int]: Indices of the two tangential components (0=Ex, 1=Ey, 2=Ez) """ if self.axis == 0: return (1, 2) # Ey, Ez tangential to x-face elif self.axis == 1: return (0, 2) # Ex, Ez tangential to y-face else: return (0, 1) # Ex, Ey tangential to z-face
[docs] @override def apply_post_E_update(self, E: jax.Array) -> jax.Array: """Zeros tangential E components at this PEC boundary face.""" comp1, comp2 = self.tangential_components sx, sy, sz = self.grid_slice E = E.at[comp1, sx, sy, sz].set(0) E = E.at[comp2, sx, sy, sz].set(0) return E