Discrete ports and lumped circuit elements#
Semi-implicit Thévenin discrete port#
The discrete (lumped) port drives a chain of grid edges with a Thévenin source \(V_s\) behind an internal impedance \(Z_0\), coupled into the E update semi-implicitly: the port current is solved together with the local field update,
which is unconditionally stable at the unchanged CFL limit
(ports/discrete/operator.py, DD-030/DD-075). Embedding lumped
resistive sources and loads into the FDTD grid in this
field-circuit-consistent way is the established lumped-element FDTD
technique of Sui et al. [27] and Piket-May,
Taflove and Baron [28] (the
semi-implicit averaging of the local field term is the standard
stabilisation in that literature; the specific multi-edge chain
formulation follows the in-repo derivation).
Trapezoidal RLC companion models#
General series/parallel RLC two-terminal elements are reduced per time
step to a Thévenin companion \((R_{\text{eq}}, V_{\text{hist}})\) using
the trapezoidal rule (circuit/companion.py, DD-077):
Companion models with trapezoidal (bilinear) integration are the classical workhorse of circuit simulators of the SPICE family; the canonical references are Nagel’s SPICE2 report [29] and the circuit-simulation textbook treatment of Chua and Lin [30]. The trapezoidal rule was chosen (over backward Euler) for its energy conservation on L/C — matching the non-dissipative leapfrog interior — which is a standard argument in both circuit and field simulation.
LumpedElementOperator (DD-079) unifies the discrete port and
general RLC elements under one operator; the classic resistive port
is the special case SeriesRLC(R=Z0) (bit-identical by
construction).
Excitation units follow the power-wave convention: a user waveform in \(\sqrt{\mathrm W}\) is realised as \(v_{\text{src}} = 2\sqrt{Z_0}\,a(t)\) (DD-078), consistent with Kurokawa power waves [26].
Lumped devices on symmetry planes#
A lumped port or passive element is always declared as the
full-model device — endpoints in full-model coordinates, Z0 and
R/L/C values of the whole element — even when a symmetry plane cuts
it. The builder relates the edge chain to every declared plane and
derives the half model itself:
A chain crossing an electric symmetry plane along the plane normal (a dipole feed on the mirror plane) must be mirror-symmetric about it; it is clipped to the meshed half, which carries half the device in series (\(Z_0/2\), \(R/2\), \(L/2\), \(2C\)). A chain crossing a magnetic plane is rejected: the mirrored current is anti-parallel, so no physical full-model element corresponds.
A chain lying in a magnetic symmetry plane is one of two parallel branches; the meshed half carries the doubled device (\(2Z_0\), \(2R\), \(2L\), \(C/2\)). A chain lying in an electric wall is rejected — its edges would be shorted.
With the as-built
ForceSymmetry…spelling the geometry is declared halved, so a chain ending on the plane is the crossing declaration; with the clipping spellings a terminal exactly on the plane is rejected with guidance, since the full-model reading of that shape is a mirror-twin pair sharing a node.
With the internally scaled device, recorded power waves and the excitation pick up the same \(\sqrt2\)-per-plane convention as modal ports, so S-parameters — and the input impedance \(Z_0(1+S_{11})/(1-S_{11})\) computed with the declared full-model \(Z_0\) — come out as full-model quantities with no further correction. One caveat mirrors the modal ports: the ratio of the raw recorded terminal signals stays a half-model quantity, so a directly measured \(-V/I\) of a passive load in a magnetic plane reads the doubled device.
Edge-path rasterisation#
Lumped elements and thin wires ride on a canonical curve rasteriser
that converts an arbitrary polyline/curve into an ordered, directed
staircase of grid edges with per-edge orientation signs
(circuit/rasterize.py, DD-076), plus the line integral
integrate_E along the path. This is in-house infrastructure.