Mode ports and S-parameters
Terminate waveguides with WavePort or ModePort, run a port sweep, and read, renormalize and export the S-matrix.
This guide shows how to measure the S-parameters of a waveguide device: put a modal port at each waveguide end, sweep the ports, and read result.network. For a voltage-gap feed on a PCB or an antenna, use lumped ports instead. The complete runnable example is the rectangular waveguide tutorial.
WavePort or ModePort#
| Port | What it does | Use it for |
|---|---|---|
WavePort | A network port: launches and measures named waveguide modes, with a reference plane and impedance definitions. Driven by PortExcitation or PortSweep. | S-parameters of waveguide devices |
ModePort | A ModeSource plus a ModeMonitor on one aperture. Returns modal amplitudes through result.monitor(name). | Mode launch and modal overlap, monitor-only taps |
Warning
Modal ports and the underlying mode solver are experimental. Check a new port setup against a known case (a straight guide should give |S21| close to 1 and |S11| close to 0) before trusting device results.
Place a WavePort#
A WavePort lies on an axis-aligned plane. Exactly one size component is zero; that axis is the propagation axis.
port_in = mw.WavePort(
"input",
position=(-0.3, 0.0, 0.0),
size=(0.0, 0.6, 0.3), # covers the guide cross section in y and z
direction="+", # the port launches toward +x
reference_plane=-0.3, # S-parameter phase reference along x, m
modes=(mw.WaveModeSpec("te", polarization="Ez"),),
)
scene.add_port(port_in)directionpoints into the device:"+"on the low end,"-"on the high end.modeslists the modes to solve and track.WaveModeSpec(family, mode_index=0, polarization=...)selects a family (such as"te"or"tem") and the dominant polarization. Each mode becomes a network channel named"<port>::<mode>", for example"input::TE0".- The aperture must cover the whole guided field. For closed metal guides, size it to the inner cross section.
Waveguide walls must run through the PML to the edge of the padded grid, or the open wall ends reflect. With num_layers PML cells of size dl outside a domain that spans ±L along the guide, make the walls at least 2 * (L + num_layers * dl) long.
Run a port sweep#
PortSweep() drives each port in turn and assembles the S-matrix. For WavePort sweeps, Maxwell runs calibrated CW simulations per frequency, so leave source_time unset:
import numpy as np
freqs = tuple(float(f) for f in np.linspace(300e6, 550e6, 6))
result = mw.Simulation.fdtd(
scene,
frequencies=freqs,
excitations=mw.PortSweep(),
run_time=mw.TimeConfig.auto(steady_cycles=8, transient_cycles=16),
).run()To drive one port with a chosen amplitude instead, pass excitations=mw.PortExcitation("input").
Read the S-matrix#
result.network is a NetworkData:
net = result.network
S = net.s # complex tensor, shape (num_frequencies, N, N)
print(net.port_names) # ('left::TE0', 'right::TE0')
s21_db = 20 * np.log10(np.abs(S[:, 1, 0].cpu().numpy()))Maxwell uses the exp(-i omega t) phasor convention, so a matched line of length L gives arg(S21) = +beta * L (modulo 2π).
NetworkData also provides:
| Method | Result |
|---|---|
to_z(), to_y() | Impedance and admittance matrices |
renormalize(z0) | S-matrix for another reference impedance |
shift_reference_planes(...) | De-embed or move the reference planes |
return_loss_db, vswr, reflection_coefficient (properties) | Per-port reflection figures, shape (num_frequencies, N) |
terminate(port, impedance=...), cascade(other, port_map=...) | Load a port, or connect two networks |
validate_physicality() | Passivity and reciprocity report |
to_touchstone(path), save(path) | Write a Touchstone .sNp file or a snapshot |
See the Networks API for arguments.
A WavePort references each mode to its own wave impedance, which changes with frequency. A Touchstone file holds one reference impedance per port, so call net.renormalize(50.0).to_touchstone("device.s2p"); net.to_touchstone(...) alone raises ValueError.
Modal amplitudes with ModePort#
A ModePort without source_time is a monitor-only tap. result.monitor(name) then resolves to the forward and backward modal amplitudes on that plane:
tap = mw.ModePort("tap", position=(0.2, 0.0, 0.0), size=(0.0, 0.6, 0.3),
polarization="Ez", frequencies=freqs)
scene.add_port(tap)witwin.maxwell.postprocess.compute_mode_overlap and compute_s_parameters turn mode data into overlaps and S-parameters (see the Postprocess API).