Sources and Ports
rfx distinguishes between sources (field injection without impedance loading) and ports (sources with a matched load for S-parameter extraction).
Waveforms
Section titled “Waveforms”add_source() and lumped/wire add_port() accept waveform objects.
Waveguide and TFSF sources use their own excitation arguments. The verified
add_polarized_source() scope below is linear and uses
GaussianPulse-compatible f0, bandwidth, and amplitude attributes.
GaussianPulse
Section titled “GaussianPulse”A differentiated Gaussian pulse (no explicit carrier) — the default excitation
when add_source/add_port build a waveform for you:
from rfx import GaussianPulse
pulse = GaussianPulse( f0=2.4e9, # center frequency (Hz) bandwidth=0.5, # fractional bandwidth (class default 0.5) amplitude=1.0, # peak amplitude)f0 sets the center frequency and bandwidth the fractional spectral width;
a larger bandwidth gives broader (more wideband) excitation, a smaller one a
narrower band. The class default is bandwidth=0.5, but when rfx auto-builds
the pulse for a source or port it uses 0.8. The differentiated Gaussian
can carry a small numerical DC residual. Compare the low-frequency spectrum
and resulting fields when DC sensitivity matters.
ModulatedGaussian
Section titled “ModulatedGaussian”A Gaussian-envelope modulated carrier (Meep-style). It usually reduces
low-frequency/DC content relative to the differentiated Gaussian, but a finite
sampled pulse does not generally have an exactly zero integral. Its spectrum is
centered near f0 with fractional width bandwidth, which makes it useful for
resonance-focused add_source() runs:
from rfx import ModulatedGaussian
waveform = ModulatedGaussian(f0=3e9, bandwidth=0.6)When waveform is omitted, add_source/add_port default to GaussianPulse
(not ModulatedGaussian); pass ModulatedGaussian(...) explicitly when its
carrier-centered spectrum is preferable, then check the sampled spectrum if a
low-frequency component would affect the result.
CWSource
Section titled “CWSource”Continuous-wave sinusoid, useful for steady-state field visualization:
from rfx import CWSource
cw = CWSource( f0=5.8e9, # frequency (Hz) ramp_steps=200, # cosine-taper onset over 200 source cycles (default 50; 0 = instant on))CustomWaveform
Section titled “CustomWaveform”Arbitrary time-domain waveform via a JAX-compatible callable:
from rfx import CustomWaveformimport jax.numpy as jnp
def chirp(t): f_start, f_end = 1e9, 4e9 rate = (f_end - f_start) / 10e-9 return jnp.sin(2 * jnp.pi * (f_start * t + 0.5 * rate * t**2))
sim.add_source((0.02, 0.02, 0.01), "ez", waveform=CustomWaveform(func=chirp))Point source (add_source)
Section titled “Point source (add_source)”A soft current source injected at a single Yee cell. No resistive load — cavity Q is not affected.
sim.add_source( position=(0.025, 0.025, 0.010), # (x, y, z) in meters component="ez", # "ex", "ey", or "ez" waveform=GaussianPulse(f0=3e9),)Use add_source for resonance characterization (Harminv) where port loading would suppress the ring-down signal.
What the waveform amplitude means is controlled by amplitude_kind
(issue #571): 'current' — amperes, realized as E += Cb·I/dV on every
path and boundary (resolution-independent injected power, the future
default) — or 'field' — a raw E-field increment E += w(t). Omitting it
keeps a deprecated per-path legacy meaning (raw add on uniform+PEC,
Cb-normalized add on uniform+CPML/UPML, current on non-uniform meshes)
and emits one DeprecationWarning per simulation naming which one applies.
To migrate an open-boundary uniform script exactly, multiply the waveform
amplitude by the cell volume dV and pass amplitude_kind='current'.
Polarized source (add_polarized_source)
Section titled “Polarized source (add_polarized_source)”Injects one or two Cartesian source components. The documented scope is linear:
# Linearsim.add_polarized_source((0.025, 0.025, 0.01), polarization="ez")
# 45-degree slant linearsim.add_polarized_source((0.025, 0.025, 0.01), polarization="slant45")
# Arbitrary real linear combinationsim.add_polarized_source( (0.025, 0.025, 0.01), polarization=(1.0, -0.5), # real (Ex, Ey), normalized internally waveform=GaussianPulse(f0=3e9),)Verified shortcuts are "ex", "ey", "ez", and "slant45".
"circular"/"rhcp", "lhcp", and complex Jones tuples are accepted by
the API, but the generated components’ time-domain quadrature has not been
independently verified. Do not infer circular/elliptical polarization, axial
ratio, or polarization sense from those inputs.
Lumped port (add_port)
Section titled “Lumped port (add_port)”A single-cell port with a resistive load equal to impedance. Enables S-parameter extraction referenced to that impedance. It is validated on the uniform-Yee lumped-port examples; scope any broader result to that evidence.
sim.add_port( position=(0.01, 0.025, 0.010), component="ez", impedance=50.0, # ohms waveform=GaussianPulse(f0=5e9),)Run with compute_s_params=True to extract the S-matrix. When no frequency
grid is supplied, rfx uses 50 points from freq_max/10 to freq_max:
result = sim.run(n_steps=2000, compute_s_params=True)s11 = result.s_params[0, 0, :] # s_params shape (n_ports, n_ports, n_freqs), complexSee Probes and S-parameters for the full extraction, plotting, and Touchstone-export path.
!!! tip
Multiple add_port() calls create a multi-port structure. rfx drives one
port at a time and assembles the full N×N S-matrix automatically — you do
not need to set excite=False yourself. The set must be homogeneous
(all lumped or all wire); a mixed lumped+wire set is not supported.
Wire port (add_port with extent)
Section titled “Wire port (add_port with extent)”A wire port spans multiple Yee cells along the port axis, connecting conductor to conductor across a gap. It is a common model for probe- and coaxial-style feeds:
# Port spans 1.6 mm in z, from substrate bottom to patch surfacesim.add_port( position=(0.029, 0.030, 0.0), # start of wire component="ez", impedance=50.0, extent=0.0016, # wire length in meters waveform=GaussianPulse(f0=2.4e9),)The extent parameter turns the lumped port into a WirePort. S-parameters are extracted from voltage/current integrals over the wire span. The public examples exercise this for probe-fed resonance workflows; treat absolute calibration outside that regime as unvalidated.
!!! warning
extent must be aligned with the component direction. For component="ez", extent
spans in the z-direction from position[2] to position[2] + extent.
Lumped RLC element (add_lumped_rlc)
Section titled “Lumped RLC element (add_lumped_rlc)”add_lumped_rlc(...) places a discrete R, L, C, or RLC element into the FDTD
update at one Yee cell. It is useful for modeling a local load or circuit
element, but it is not an impedance-referenced port by itself. To measure the
reflection that load presents to a reference line, excite the structure with
add_port(..., impedance=Z0) and place the RLC element as the load.
sim.add_lumped_rlc( position=(0.03, 0.02, 0.01), component="ez", R=50.0, C=0.2e-12,)On the uniform single-device differentiable path, registered RLC elements now
affect forward(...), and scalar component values can be overridden by tracers:
result = sim.forward( port_s11_freqs=jnp.array([5e9]), rlc_values_override={0: {"R": R}},)The 0 key is the registration order among add_lumped_rlc(...) calls.
Non-uniform and distributed forward(...) lanes reject this override.
Microstrip-line port (add_msl_port)
Section titled “Microstrip-line port (add_msl_port)”add_msl_port(...) is the specialized full-strip microstrip-line port. It is
not routed through run(compute_s_params=True). Use the dedicated calculator:
sim.add_msl_port( position=(0.004, 0.003, 0.0), width=0.5e-3, height=0.25e-3, direction="+x", impedance=50.0, name="in",)sim.add_msl_port( position=(0.016, 0.003, 0.0), width=0.5e-3, height=0.25e-3, direction="-x", impedance=50.0, name="out",)
msl = sim.compute_msl_s_matrix(n_freqs=101, num_periods=40)S = msl.S # (n_ports, n_ports, n_freqs) complexZ0 = msl.Z0 # (n_ports, n_freqs) complexThe calculator drives each port in turn and de-embeds β and Z0 downstream of the
feed plane with an N-probe (default n_probes=5) least-squares
wave-decomposition extractor. It is a uniform-mesh path; unsupported
mesh/source/port combinations are rejected rather than silently returning
None. num_periods defaults to 40 because MSL transients drain slowly. The
thru-line and notch examples define the tested uniform-Yee configurations.
Waveguide port (add_waveguide_port)
Section titled “Waveguide port (add_waveguide_port)”Modal waveguide excitation for rectangular waveguide S-matrix extraction.
Injects a specific TE/TM mode and decomposes the reflected/transmitted fields into the same modal basis. Requires boundary="cpml" (with cpml_layers > 0) and mode="3d"; it cannot be combined with lumped ports or a TFSF source. The public examples cover rectangular guides — validate branch or T-junction geometries against a reference before reporting.
sim.add_waveguide_port( x_position=0.01, # physical x-coordinate of port face y_range=(0.0, 0.023), # aperture y-extent z_range=(0.0, 0.010), # aperture z-extent mode=(1, 0), # TE10 mode mode_type="TE", direction="+x", # propagation direction f0=10e9, bandwidth=0.5, name="port1",)For a full multi-port S-matrix, use compute_waveguide_s_matrix(...):
sim.add_waveguide_port( x_position=0.09, y_range=(0.0, 0.023), z_range=(0.0, 0.010), direction="-x", f0=10e9, bandwidth=0.5, name="port2",)
wg = sim.compute_waveguide_s_matrix(num_periods=30, normalize=True)S = wg.s_params # (n_ports, n_ports, n_freqs) complexnormalize=True cancels one-way Yee dispersion in the transmission
(off-diagonal) terms and is the right choice for S21. It does not correct
the round-trip dispersion in reflection. On a uniform grid, S11 studies may use
normalize=False (default) or normalize="flux"; non-uniform waveguide runs
reject normalize=False and require normalize="flux". If a passive
normalize=False result rises above the documented single-run over-unity
range, rfx emits a soft advisory rather than silently treating it as ordinary
evidence. Use the guide-recommended flux normalization or a reference check
before reporting that number.
A plain run(...) also populates result.waveguide_sparams — per-port,
plane-calibrated s11/s21 from that single excitation. This is a diagnostic
of one driven configuration, not the full driven-in-turn matrix that
compute_waveguide_s_matrix assembles:
result = sim.run(n_steps=3000)sp = result.waveguide_sparams["port1"]# sp.freqs, sp.s11, sp.s21See Waveguide ports for mode selection, calibration planes, and validated gates.
TFSF plane-wave source (add_tfsf_source)
Section titled “TFSF plane-wave source (add_tfsf_source)”Total-field/scattered-field plane wave for scattering and RCS simulations.
Requires boundary="cpml" (with cpml_layers > 0) and a 3D or 2D-TE/TM mode.
Scope is deliberately narrow: propagation along x only (direction "+x"/"-x")
and polarization "ez" or "ey".
sim.add_tfsf_source( f0=5e9, bandwidth=0.4, polarization="ez", direction="+x", # propagation direction (+x or -x) angle_deg=0.0, # oblique incidence angle (|angle_deg| < 90, degrees from normal) method="bloch", # oblique path: "bloch" (periodic unit cell) or # "methodB" (open-domain compact scatterer))For oblique incidence (angle_deg != 0) the method argument selects the
transverse boundary-value problem: "bloch" is the laterally periodic
(unit-cell / infinite-array) path, "methodB" is the open-domain 2.5-D path
for a compact isolated scatterer (polarization="ez", uniform grid, and
direction="+x" only — "-x" raises NotImplementedError under
"methodB" at run() time, since the Simulation API defers TFSF
construction to the run; mirror the geometry instead).
!!! warning
add_tfsf_source cannot be combined with lumped ports or waveguide ports.
Summary table
Section titled “Summary table”| Method | Use case | S-parameter extraction |
|---|---|---|
add_source() | Resonance, field visualization | none (soft source, no port) |
add_polarized_source() | Linear antenna excitation; circular/elliptical outputs are not validated | none (soft source, no port) |
add_port(extent=None) | Lumped-port S-params | run(compute_s_params=True); validated on uniform-Yee examples |
add_port(extent=...) | Wire / probe-feed S-params | run(compute_s_params=True); probe-feed examples, calibration caveated |
add_lumped_rlc() | Lumped circuit/load element | no direct S-parameter calculator; can be a differentiable scalar load via forward(rlc_values_override=...) in a uniform single-device calculation |
add_msl_port() | Full-strip microstrip S-matrix | compute_msl_s_matrix(); uniform mesh is externally compared; nonuniform laplace/uniform modes have internal checks only; eigenmode is unsupported |
add_waveguide_port() | Modal waveguide S-matrix | compute_waveguide_s_matrix(); rectangular guide, cpml + 3D |
add_coaxial_port() | One-port coaxial line reflection | compute_coaxial_line_reflection(); float32 precision; nonperiodic 3D second-order uniform Yee grid; CPML on all six boundary faces with positive thickness on both z faces; cpml_axes="z"; exactly one face="top" port; see the API constraints |
add_tfsf_source() | RCS / scattering plane wave | none (not a port) |
compute_coaxial_line_reflection() constructs its own line, TEM source, DFT
planes, and termination. Do not add separate geometry, thin conductors, lumped
RLC elements, probes, field monitors, NTFF boxes, or add_coaxial_*
termination helpers to that simulation; the method rejects them. The port’s z
position, pin_length, and impedance do not set the internally derived line
layout or loads. Use feed_impedance, and use dut_impedance only with
termination="matched". At least three probe planes are required, and every
requested plane must fit before the source. See the
complete calculator rules before a long run.