Skip to content

Repository files navigation

MM — Modal-Matching Waveguide Solver

A MATLAB toolbox (with a pure-Python port) for computing generalised scattering matrices (GSMs) of rectangular waveguide structures via the modal-matching (also known as mode-matching) method.

Overview

The solver assembles multi-port waveguide devices from elementary building blocks called TwoPortDevices (a cascade of rectangular waveguide sections) and Nto1Connections (junctions between N guides and a single wider guide). For each frequency point the GSM of the full structure is obtained by

  1. Computing mode indices and eigenfunctions for every cross-section.
  2. Evaluating overlap coupling integrals between modes at each interface.
  3. Building per-step scattering matrices and cascading them with phase delays.
  4. Condensing the global GSM to expose only the user-defined open ports.

Reference designs implemented in the project scripts include the Riblet and Hildebrand direct waveguide couplers and two bifurcation benchmarks (BifurcationE and BifurcationH) validated against HFSS full-wave data.


Repository layout

MM/
├── matlab/ # MATLAB solver library and benchmark scripts
│ ├── lib/ # Reusable solver functions (add to MATLAB path)
│ └── *.m # Top-level benchmark scripts
├── python/ # Pure-Python implementation of the same API
│ ├── run.py # CLI entry point
│ ├── core.py # Core numerical and solver functions
│ ├── scripts.py # Top-level benchmark scripts
│ └── lib/ # One module per matlab/lib/*.m function
├── HFSSe.csv # HFSS reference data for BifurcationE
└── HFSSh.csv # HFSS reference data for BifurcationH

MATLAB

Requirements

  • MATLAB R2014b or later.
  • No additional toolboxes are required.

Getting started

All top-level scripts bootstrap the library path themselves. Either run a script from within its own directory or from the repository root:

% From the repository root
run('matlab/BifurcationE.m')
run('matlab/BifurcationH.m')
run('matlab/Riblet.m')
run('matlab/HildebrandHalf.m')
run('matlab/HildebrandSemiAuto.m')
run('matlab/HildebrandFull.m')

To use the library from your own script, add lib/ to the path first:

projectRoot = fileparts(mfilename('fullpath'));
addpath(fullfile(projectRoot, 'lib'));

Benchmark scripts

ScriptStructurePortsFrequency
BifurcationE.mE-plane T-junction310–25 GHz
BifurcationH.mH-plane T-junction310–25 GHz
Riblet.mRiblet broadwall coupler47–15 GHz
HildebrandHalf.mHildebrand coupler (half, exploits symmetry)213–15 GHz
HildebrandSemiAuto.mHildebrand coupler (half, explicit assembly)213–15 GHz
HildebrandFull.mHildebrand coupler (full 4-port, 6 devices)413–15 GHz

The bifurcation scripts overlay HFSSe.csv / HFSSh.csv when the files are present at the repository root.

Library reference

Solver pipeline

FunctionSignatureDescription
MultiPortDevice(WGS, Nto1, OpenPorts, ConnectedPorts, FS, flag, Options) → [Sf, Sinfo, …, Error]Top-level entry point: validates, solves, and optionally draws
MultiPortDeviceValidate(…) → [WGS, Nto1, …, Symmetry, Topology, Error]Validates all inputs, fills defaults, builds topology
MultiPortDeviceSolve(…) → [Sf, Sinfo, Error, …]Runs frequency sweep and assembles condensed GSM
MultiPortDeviceTopology(…) → TopologyBuilds internal port connectivity graph
MultiPortDeviceDraw(…)Renders 3-D exploded device view

Scattering matrix primitives

FunctionDescription
SingleStepScattering matrix at a single waveguide cross-section step
SingleCascadeCascade two back-to-back steps with a propagation delay
MultiStepMulti-section cascade for an entire TwoPortDevice arm
CascadeFull WaveGuideStructure cascade
CondenseGSMGSM condensation via the Selleri method
RenormalizeGSMRe-normalise a multi-port GSM to new reference impedances
RenormalizeRe-normalise a two-port S-matrix

Junction handling

FunctionDescription
Nto1JunctionBuilds the scattering matrix of an N-to-1 rectangular junction
Nto1DeviceValidateValidates an Nto1Connection definition
Nto1DeviceDraw3-D visualisation of an Nto1 junction

Modal analysis

FunctionDescription
OrderModesAssigns TE/TM mode indices for a cross-section respecting symmetry
EigenModesComputes $k_x$, $k_y$, $k_z$ eigenvalues for all retained modes
OneModeEigensEigenvalues for a single $(m,n)$ mode pair
NormCoeffMode normalisation constants $A_h$, $A_e$
OneModeNormCoeffNormalisation constant for a single mode
DelayMatrixDiagonal phase-delay matrix $D = \mathrm{diag}(e^{-j k_z l})$
WaveNumbersPropagation constants $k_z$ at a given free-space wavenumber $k_0$
DgammaMatricesDiagonal $\Gamma$ matrices for a cross-section step
UMatricesUnit-normalised coupling matrices
MxxMatricesMode-coupling integral matrices $M_{hh}$, $M_{he}$, $M_{eh}$, $M_{ee}$
IntegralsOverlap integrals between two rectangular waveguide modes

Port and GSM utilities

FunctionDescription
ExtractPortSExtract an $(i,j)$ sub-block from a partitioned GSM
InsertPortSInsert a sub-block back into a GSM
ExtractSingleSExtract one modal $S_{ij}$ coefficient by mode type and index
FrequencySweepValidateValidate and complete a FrequencySweep struct

Geometry helpers

FunctionDescription
TwoPortDeviceValidateValidate and fill a TwoPortDevice definition
TwoPortDeviceGetPortSegmentRetrieve the WaveGuideSegment at a given port
TwoPortDeviceInsertPortSegmentInsert or replace a port segment
TwoPortDeviceDraw3-D rendering of a TwoPortDevice
ReverseWaveGuideStructureReverse the section order (flip input ↔ output)
WaveGuideSegmentGetBoundingAxis-aligned bounding box of a segment
WaveGuideSegmentGetCrossSectionCross-section parameters
WaveGuideCapDrawDraw end-cap patch for visualisation
WaveGuideConnectionCapDrawDraw interface cap at a device connection
ShowSegment3-D plot of a single WaveGuideSegment
NotInRectGeometry test: point outside a set of rectangles

Post-processing and diagnostics

FunctionDescription
GSMDrawPlot modal S-parameters in dB vs frequency
RelativePhaseDrawPlot phase difference between two modal S-parameters
DumpErrorPrint an Error struct to stdout; returns halt flag

Data structures

WaveGuideSegment

A struct describing one rectangular waveguide cross-section.

FieldTypeDescription
a, bscalarWidth and height [m]
lscalarSection length [m]
xo, yo, zoscalarCentre-position offset [m]
NmodesintegerNumber of modes to retain
Nh, NeintegerNumber of TE and TM modes after ordering
mh, nh, me, nevectorMode index arrays
kh.x, kh.y, kh.mnvectorTE eigenvalues and propagation constants
ke.x, ke.y, ke.mnvectorTM eigenvalues and propagation constants
Ah, AevectorNormalisation coefficients
Ddiagonal matrixPropagation-delay matrix

TwoPortDevice

WGS{i} — a cell whose .D field is an ordered cell array of WaveGuideSegment structs forming the cascade of sections for device arm i. Port 1 is WGS{i}.D{1} and port 2 is WGS{i}.D{end}.

Nto1Connection

Describes the junction between N input TwoPortDevice arms and a single wider guide.

FieldDescription
SideOne{i}.TwoPortDeviceIndexIndex of the i-th input device
SideOne{i}.TwoPortDevicePortPort number (1 or 2) on that device
SideTwo{1}.TwoPortDeviceIndexIndex of the single output device
SideTwo{1}.TwoPortDevicePortPort number on the output device
zoz-coordinate of the junction plane [m]

FrequencySweep

FieldDescription
start, endSweep limits [Hz]
NNumber of uniformly-spaced points
f(computed) Frequency vector [Hz]

Options

Solver and draw control struct passed to MultiPortDevice.

FieldValuesDescription
DeviceSymmetry.Use0 / 1Enable geometric symmetry to halve solve time
DeviceSymmetry.Side1 / 2Which half of the device to solve (port-1 or port-2 side)
Connections0 / 1Enable ConnectedPorts internal port merging

Error

Struct returned by most library functions. A fatal flag indicates an unrecoverable problem; call DumpError to print it and obtain a halt flag.


GSMDraw — ModeStruct format

ModeStruct is a cell array; each entry describes one trace to plot:

ModeStruct{k} = {outPort, inPort, outType, outM, outN, inType, inM, inN, label}
PositionExampleDescription
11Output port index
22Input port index
3'h'Output mode family: 'h' = TE, 'e' = TM
41Output mode index m
50Output mode index n
6'h'Input mode family
71Input mode index m
80Input mode index n
9'md'Label tag (used in legend)

RelativePhaseDraw takes the same ModeStruct format but requires exactly two entries and plots their phase difference in degrees.


Typical workflow for a new device

%%1. Define waveguide sections (one WGS per arm, one D per section)
WGS{1}.D{1}.a =0.01905; WGS{1}.D{1}.b =0.009525;
WGS{1}.D{1}.Nmodes =12; WGS{1}.D{1}.l =0.01;
WGS{1}.D{1}.xo =0; WGS{1}.D{1}.yo =0;
WGS{1}.D{1}.zo =0;
WGS{2}.D{1}.a =0.01905; WGS{2}.D{1}.b =0.009525;
WGS{2}.D{1}.Nmodes =12; WGS{2}.D{1}.l =0.01;
WGS{2}.D{1}.xo =0; WGS{2}.D{1}.yo =0;
WGS{2}.D{1}.zo =0;
%%2. Define the N-to-1 junction
Nto1{1}.SideOne{1}.TwoPortDeviceIndex =1;
Nto1{1}.SideOne{1}.TwoPortDevicePort =2;
Nto1{1}.SideTwo{1}.TwoPortDeviceIndex =2;
Nto1{1}.SideTwo{1}.TwoPortDevicePort =1;
Nto1{1}.zo =0;
%%3. Define open ports and frequency sweep
OpenPorts{1}.TwoPortDeviceIndex =1; OpenPorts{1}.TwoPortDevicePort =1;
OpenPorts{2}.TwoPortDeviceIndex =2; OpenPorts{2}.TwoPortDevicePort =2;
FS.start =8e9; FS.end =12e9; FS.N =51;
%%4. Set options and solve
ConnectedPorts = {};
Options.DeviceSymmetry.Use =0;
Options.DeviceSymmetry.Side =2;
Options.Connections =0;
[Sf, Sinfo, WGS, Nto1, ConnectedPorts, FS, Error] = ...
MultiPortDevice(WGS, Nto1, OpenPorts, ConnectedPorts, FS, 2, Options);
ifError.fatal; return; end%%5. Plot — S21 for TE10 input/output
f =FS.f;
ModeStruct = { {2, 1, 'h',1,0, 'h',1,0, 'S21'} };
GSMDraw(f, Sf, Sinfo, ModeStruct, 1);
xlabel('Frequency (Hz)'); ylabel('|S| (dB)'); legend('S21 TE10');

Python implementation

A pure-Python implementation of the solver lives in python/. It maintains MATLAB lib/ API compatibility with matching function names and equivalent data conventions.

Requirements

No additional installation is required. Run scripts directly from the repository root.

Python layout

python/
├── run.py # CLI entry point
├── core.py # Core numerical and solver functions
├── scripts.py # Top-level benchmark scripts
└── lib/ # One module per matlab/lib/*.m function

Running the Python CLI

From the repository root:

python python/run.py <Script> [--no-plot]
OptionDescription
<Script>One of BifurcationE, BifurcationH, Riblet, HildebrandHalf, HildebrandSemiAuto, HildebrandFull
--no-plotSkip plotting for headless or CI runs

Script names are case-insensitive. Common misspellings such as hilderbrandhalf are accepted as aliases.

Examples:

# Single run without plot
python python/run.py BifurcationE --no-plot
# Interactive plot
python python/run.py BifurcationH
# Run all benchmarks headlesslyforsin BifurcationE BifurcationH Riblet HildebrandHalf HildebrandSemiAuto HildebrandFull;doecho"=== $s ==="
python python/run.py "$s" --no-plot
done

Python status

ScriptRunsHFSS overlay
BifurcationEyesyes (HFSSe.csv)
BifurcationHyesyes (HFSSh.csv)
Ribletyes
HildebrandHalfyes
HildebrandSemiAutoyes
HildebrandFullyes

Every MATLAB lib/*.m function has a Python counterpart in python/lib/. Some advanced drawing helpers in python/core.py (notably RelativePhaseDraw) are present but currently raise NotImplementedError.

Python reference overlay behavior

When HFSSe.csv / HFSSh.csv are present at the repository root, bifurcation scripts overlay full-wave reference traces on top of MM curves. The overlay logic in python/scripts.py:

  • Pairs reference columns to MM traces by RMSE.
  • Preserves MM line colors for the paired reference traces.
  • Supports optional dB bias correction for BifurcationH to improve visual parity.

Releases

Packages

Contributors

Languages