circuitRF Reference Guide

Simulations

Every analysis circuitRF runs, what it computes, a short overview of the method, and the full set of settings. Algorithm details are deferred to the design notes / white papers; this chapter is the operational reference.

How results are stored

Every analysis returns a DataSet — a named bundle of labelled, unit-bearing arrays (DataCubes), each a single kind, real or complex. A whole run's DataSets go into one file in the workspace's shared results/ folder, and you name that file in the Results file field above the analyses list; leaving it blank uses the schematic's own name. This is what the Data Display plots and what .npy / .mat export produces. Full detail: Where results live.

DC (operating point)

Computes: the steady-state node voltages and branch currents with no signal applied — the bias point. For nonlinear circuits this is the prerequisite to harmonic balance.

Method: modified nodal analysis (MNA); nonlinear devices solved by Newton–Raphson with gmin and source stepping for convergence robustness.

SettingMeaning
TolNewton convergence tolerance on the residual.
MaxIterMaximum Newton iterations.
gmin / source steppingContinuation aids that ease a hard bias point into convergence.

Read currents directly with an IProbe in series, and name a voltage with a VProbe on the wire.

S-Parameters

Computes: the linear, small-signal multiport network parameters over a frequency sweep — the S-matrix at each frequency, with renormalization to the port reference impedances. Ports are defined by Term components (or P1Tone port numbers).

Method: complex MNA solved per frequency point; each port is excited in turn and the scattered waves are extracted and renormalized. Embedded Touchstone (SnP) blocks are interpolated onto the sweep.

SettingMeaning
Frequency sweepStart / Stop / Step (or point count); one or more segments. Units accepted (GHz, MHz, …).
Reference impedancePer-port reference (from each Term's Z); the result renormalizes to it.
NDF=yesAlso compute the normalized determinant function — the rigorous count of right-half-plane poles of the whole network. PassiveVars= / PassiveParams= name what passivates a user-supplied model.
MarginThreshold=The stability margin in dB below which a WSProbe is reported as worth looking at. Default -15; none disables.

A WSProbe anywhere in the circuit adds the wsp matrix and its derived quantities to this run, at the cost of a few extra back-substitutions and no extra factorization. See The WSProbe.

For the ideal System Components this is the analysis that answers almost everything about them: port match, isolation, coupling and directivity, a filter's passband and rejection, a duplexer's arm-to-arm leakage, and an amplifier's small-signal gain and stability.

From the CLI: circuitrf sparam circuit.cnl --freq 1GHz:10GHz:50MHz -o out.s2p.

Harmonic Balance

Computes: the steady-state response of a nonlinear circuit driven by one or more tones (up to six) — the spectrum (DC + harmonics, and mixing products when there is more than one tone) at every node. From it come Pout, gain, efficiency, PAE, compression, and intermodulation.

Method: the circuit is split into a linear sub-network (solved in the frequency domain) and nonlinear devices (evaluated in the time domain and transformed by FFT). A multidimensional Newton solve drives the harmonic-balance residual — frequency-domain KCL — to zero, using a conversion-matrix Jacobian. Power/source continuation ramps the drive to reach convergence deep into compression. Multi-tone analysis uses a diamond-truncated mixing spectrum.

For the ideal System Components this is the analysis for everything an S-parameter run cannot reach — a mixer's conversion gain and its mixing products, an amplifier's compression and IM3, and passive intermodulation on the blocks that carry it. Two tones (multi-tone) are what an intercept measurement needs.

SettingMeaningTypical
ToneThe fundamental frequency (a value or a variable such as RFfreq). Two or more tones for intermodulation.2 GHz
ToneUnitUnit for the tone value when it doesn't carry its own (a unit on the referenced variable wins).GHz
MaxHarmHighest harmonic order retained (5 → DC plus 5 harmonics). Higher captures sharper nonlinearity at more cost.5–7
FFTOverSampleOversampling factor for the time grid used by the nonlinear FFT. Raise to reduce aliasing on stiff devices.1–2
TolNewton convergence tolerance on the HB residual.1e-6
DriveSteppingPower/source continuation: IfNecessary (ramp only when a direct solve stalls), or always/never.IfNecessary
GuardHarmonicExtra guard harmonic(s) beyond MaxHarm for anti-alias safety.0
LambdaNewton damping factor (1 = full step; lower damps an unstable solve).1
MaxIterMaximum Newton iterations per solve point.100
SSStart / SSStop / SSNpts / SSStep / SSUnit / SSLogThe small-signal "tickle" sweep a WSProbe is driven over, around the converged large-signal operating point. Absent, there is no small-signal solve and the run is unchanged. See The WSProbe under harmonic balance.0.1-10 GHz
SSMaxHarmSidebands retained in the conversion matrix for that solve.= MaxHarm
MarginThresholdAs for S-parameters, applied at each operating point.-15 dB
Multi-tone analysis adds the extra tones and a mixing order (≥ 5 at two tones, to capture close-in IM products; lower it as tones are added).

Parametric Sweep

Computes: nothing new on its own — it wraps another analysis and re-runs it across one or more variables (drive power, a bias voltage, frequency, any user variable). Nest sweeps to cover several variables; the innermost analysis is what each point solves.

SettingMeaning
VarThe variable to sweep (e.g. Pin, RFfreq).
Start / StopSweep endpoints (units accepted).
Step or NptsStep size, or a point count; linear or logarithmic.
InnerThe analysis (or inner sweep) this sweep wraps — how sweeps nest.
A whole sweep tree writes a single results file named after its innermost analysis.

Loadpull / Sourcepull

Computes: figures of merit (Pout, gain, efficiency, PAE) as the load (or source) reflection coefficient is swept over a grid on the Smith chart — the classic loadpull experiment — ready to draw as contours. Harmonic terminations can be swept too (harmonic loadpull).

Method: a programmable Tuner presents each grid termination; a harmonic-balance solve runs per point (with previous-point continuation for convergence); the FOMs are collected into the DataSet over the Γ-grid. The Data Display fits a surface and extracts contour lines; measured loadpull files (.spl/.lpcwave) plot identically.

SettingMeaning
Tuned port / roleLoad or source side, and which DUT port the tuner terminates.
Γ gridThe set of reflection-coefficient points (a Smith-chart grid, typically within \|Γ\| ≤ 0.9).
Tuned harmonicFundamental, or a specified harmonic for harmonic loadpull.
Power / biasThe drive level and bias at which the pull is performed (often a power sweep too).

The termination grid file (.gam)

The grid of terminations a loadpull visits lives in a small, human-readable text file with a .gam extension — one termination per line. It is kept out of the netlist on purpose: a grid is data (often hundreds of points), and a real tuner system works the same way, reading a "pattern file" of impedance states to visit. A loadpull analysis points its grid at a .gam file; a Loadpull Pursuit can write one for you (its recommended terminations).

Each point is either an impedance (Ω) or a reflection coefficient Γ, in one of three column layouts. An optional header line (starting #) declares the form, the reference impedance, and the layout; if you omit it, circuitRF assumes impedance and infers the layout from the first data line. The parser is deliberately forgiving — blank lines and ; or # comment lines are skipped.

Header tokenMeaningDefault
gamma / impedanceValues are reflection coefficients Γ, or impedances in Ω.impedance
Z0=<value>Reference impedance for the Γ↔Z conversion.50 Ω
re_imTwo columns: real, imaginary.inferred from the first line: a j/i marker ⇒ re+j*imag, else re_im
mag_angTwo columns: magnitude, then angle in degrees.
re+j*imagOne column, a complex literal (e.g. 80+j*10, 0.5-j*0.3).
Header tokens are case-insensitive and may appear in any order, e.g. # gamma Z0=50 mag_ang.

A Γ grid in magnitude/angle form (rings on the Smith chart):

# gamma Z0=50 mag_ang
; concentric rings of load reflection coefficient
0.00    0
0.20    0
0.20   90
0.40    0
0.40   90
0.40  180
0.60    0
0.80   30
Multi-frequency grids

One .gam file can hold a different grid per frequency, for frequency-swept loadpull. Start each block with a bare freq=<value><unit> line (Hz/kHz/MHz/GHz/THz); every point below it belongs to that frequency until the next freq= line. circuitRF reads the block whose frequency is nearest the one being simulated. A file with no freq= line is a single grid used at every frequency.

# impedance Z0=50 re+j*imag
freq=1.8GHz
80+j*10
60-j*5
freq=2.2GHz
85+j*5
70+j*0

Using one. In the Loadpull editor, set the grid field to your .gam file (paths are resolved relative to the netlist). Getting one. A Loadpull Pursuit with an OutputGrid path writes its recommended terminations as a .gam — always in impedance … re+j*imag form, with a generated-by header and a # WARNING: line if any near-non-convergent points were dropped. That output reads straight back into a standard loadpull, including the multi-frequency layout for a frequency-swept pursuit.

Loadpull Pursuit

Computes: the load (or source) terminations that optimize a figure of merit — found automatically by a query-minimizing search rather than by gridding the whole Smith chart. It reports two optima at constant compression — MXP (maximum output power) and MXE (maximum efficiency) — recommends a conjugate-match source impedance for each, and can run a focused high-fidelity loadpull around them, all from one analysis.

Why use it: it automates the real-world PA loadpull procedure — find the optimum, then loadpull a focused grid around it with a sensible source match — into a single unattended run. Point it at a DUT whose optimal terminations are unknown, run, and come back to a complete loadpull dataset already concentrated at the right terminations. It is repeatable (great for characterizing many devices with one netlist), avoids wasted query points far from the optima, and avoids non-convergent terminations.

It complements, it doesn't replace, a standard loadpull. Pursuit finds the useful terminations and the region of interest; a standard loadpull then shows the performance tradeoffs across that region. Pursuit shares all Loadpull settings except the Γ grid (it generates terminations rather than reading them), plus the keys below.

How a pursuit run works

  1. Search — a steepest-ascent search in the VSWR plane finds MXP, then MXE (seeded from MXP, so the second search is cheap). Each "query" is one drive-to-compression harmonic-balance run; queries are cached so MXE reuses MXP's data.
  2. Recommend — builds a focused-around-the-optima set of recommended terminations (dense near MXP/MXE, sparse further out) and a conjugate-match source impedance for each optimum.
  3. Loadpull — optionally runs a standard loadpull over those recommended terminations, using a recommended source match, producing the high-fidelity contour data.

Search method

Pursuit settings

KeyMeaningDefault
EffTypeThe MXE (efficiency) criterion: drain efficiency DE or PAE.DE
SearchMethodSteepestAscent or IteratedQuadratic (above).SteepestAscent
ZsourceOBOOutput back-off (dB from compression) at which the input impedance is sampled for the auto-Zsource report. Granularity set by PinStep.5
VSWR1 (focused)Focused box size (VSWR-circle radius) around MXP and MXE — the dense sampling region.1.5
VSWR1_resolutionGrid spacing (N×N samples) inside each focused box.4
VSWR2 (broad)Broad box size (VSWR-circle radius) for the surrounding coarse grid.3
VSWR2_resolutionGrid spacing (N×N samples) for the broad box.4
keepNonconvergingPointsIf false, drop recommended points near terminations found non-convergent during the search (and warn).false
nonconvergentVSWRExclusion radius (VSWR) around known non-convergent terminations.1.05
OutputGridPath to write the recommended terminations as a .gam file. Absent → no file written.none
CreateLoadpullResultRun the follow-on standard loadpull over the recommended terminations. Independent of OutputGrid.on
LoadpullResultZsourceSource match the follow-on loadpull uses: MXE, MXP, or None (use the Source Tuner's own Z1).MXE
The recommended terminations always exist in memory; OutputGrid controls only the file, CreateLoadpullResult controls only the follow-on simulation — the two are orthogonal.
Auto-Zsource — a recommended source match

After each optimum is found, pursuit backs the drive off by ZsourceOBO dB, computes the DUT input impedance Zin there, and reports Zsource = Zin* (the conjugate match) — a ready-to-use input-match target, per optimum, without a separate sourcepull.

The DUT must compress

MXP/MXE are defined at compression. If the DUT does not reach Compression within PinMax, the search aborts with a clear message (raise PinMax or check bias/load) — it never silently raises your PinMax cap.

Full design + algorithm: docs/design/loadpull_pursuit.md (in the repo).

Multi-Tone Harmonic Balance (2 to 6 tones)

Computes: the steady-state spectrum of a nonlinear circuit driven by several carriers at once — every harmonic and every intermodulation (mixing) product. Two tones is the standard test for linearity: third-order intermodulation (IM3), intercept points, spectral regrowth, and the asymmetry between the lower and upper IM products. Three or more tones is the natural setting for mixers (RF, LO and their products) and for multi-carrier work. Two tones is enough for an ideal mixer on its own — one for the RF, one for the LO — and the converted power reads off the IF port at the mixing product you want.

Multi-tone is not a separate analysis — it is Harmonic Balance with more than one tone. Everything from the single-tone chapter (the linear/nonlinear split, the Newton solve, drive continuation, all the convergence knobs) still applies. This chapter covers only what is different.

Up to six tones are supported. Everything below is written for two because that is the common case, but it holds at any tone count: read "(k₁, k₂)" as the full list "(k₁, …, kT)". The one thing that changes with tone count is how far you can push Max mix order — see How many tones, at what order.

Setting it up

In the Harmonic Balance analysis editor, switch the Single / Multi-tone toggle to Multi-tone. Three things change:

The analysis owns the tones, not the source

The fundamentals come from the HB analysis (the tone list, written as NumFreqs=N Tone[1]=… Tone[N]=… in the netlist), not from the source. A PnTone (or V_nTone) just supplies power at those frequencies. Every source tone must land exactly on the analysis grid — an off-grid drive frequency is rejected at setup with a clear message rather than producing silent garbage.

The mixing grid and mixing order

With two fundamentals, a spectral line is no longer indexed by a single harmonic number. Each line is a pair of integers (k₁, k₂) sitting at the physical frequency

f = k₁·f₁ + k₂·f₂

and its mixing order is m = |k₁| + |k₂|. The carriers are (1, 0) and (0, 1) at order 1; a third-order intermod such as 2f₁−f₂ is (2, −1) at order 3. Solving for every possible (k₁, k₂) is infinite, so circuitRF keeps a finite set bounded by Max mix order:

retain (k₁, k₂)  ⟺  |k₁| + |k₂| ≤ MaxMixOrder

That inequality is a diamond (a rotated square) in the (k₁, k₂) plane — hence "diamond truncation." A diamond rather than a full rectangular box because the high–high corner products (e.g. 5f₁+5f₂) carry no meaningful energy in a real intermod test, while every low-order product that does matter is inside the diamond. MaxMixOrder is the two-tone analog of single-tone MaxHarm: order 5 retains products down to IM5. The number of products grows quadratically with the order (≈ 2·order² lines), so a two-tone run is much larger than a single-tone run at the same order — order 5 yields dozens of mixing products versus a handful of harmonics.

With T tones the same rule reads retain (k₁,&nbsp;…,&nbsp;k<sub>T</sub>) ⟺ |k₁| + … + |k<sub>T</sub>| ≤ MaxMixOrder, and the diamond becomes a T-dimensional one. There is still a single order knob — no per-tone limits.

How many tones, at what order

The retained product count is what actually costs you, and it grows steeply with tone count — much faster than the quadratic growth two tones would suggest. The default MaxMixOrder&nbsp;=&nbsp;5 is sized for two tones; lower it as you add tones:

Retained mixing products. Configurations over the 600-product limit are refused, with a message naming the largest order that fits.
Tonesorder 2order 3order 4order 5
27132131
3133263116
42165161341
643189645 (refused)1827 (refused)

Practical starting points: order 5 at 2–3 tones, order 4 at 4 tones, order 3 at 5–6 tones. On a single-FET PA, six tones at order 3 solves in a few seconds.

Too large is refused before the run starts, not after

If the tone count and mixing order together ask for more products than the solver will take, circuitRF refuses immediately — not after a long solve — and the message tells you the largest MaxMixOrder that would work at that tone count. The editor shows the same number live beside the field, so you normally see it before you press Run.

Equally spaced tones put two products at the same frequency

With evenly spaced carriers — the usual multi-carrier stimulus — different mixing products can land on the same physical frequency. At 1.99 / 2.00 / 2.01 GHz, both (1, −1, 0) and (0, 1, −1) sit at 10 MHz. They stay separate products with their own phasors, and the spectrum plot shows both stems at the same position — they are not added together. Use the marker readout, which names the product, to tell them apart.

(k₁, k₂)OrderFrequencyWhat it isAt f₁=1.99, f₂=2.01 GHz
(0, 0)00DC / baseband0
(1, −1)2f₁ − f₂IM2, the tone spacing (baseband)0.02 GHz
(1, 0)1f₁Carrier 11.99 GHz
(0, 1)1f₂Carrier 22.01 GHz
(2, −1)32f₁ − f₂IM3 lower1.97 GHz
(−1, 2)32f₂ − f₁IM3 upper2.03 GHz
(3, −2)53f₁ − 2f₂IM5 lower1.95 GHz
(−2, 3)53f₂ − 2f₁IM5 upper2.05 GHz
(2, 0)22f₁2nd harmonic of carrier 13.98 GHz
(1, 1)2f₁ + f₂2nd-harmonic sum band4.00 GHz
The IM "order" is the sum of the integer coefficients: IM3 = |2|+|−1|, IM5 = |3|+|−2|. Choose closely-spaced tones (small Δ) so the IM3 and IM5 products fall right beside the carriers.
Reading the spectrum in the Data Display

A multi-tone result plotted as a spectrum uses a mixIndex axis: each stem is a mixing product, drawn at its physical frequency, labelled with its (k₁, k₂) tag — or (k₁, …, kT) at more tones. The spectrum is shown single-sided — the negative-frequency products (the conjugate halves, e.g. f₁−f₂ and f₂−f₁) are folded onto their positive frequency, so each product appears once.

Arrow keys step a marker in frequency order, which is what you want with tightly spaced IM products. Everything here works the same at any tone count.

Measuring intermodulation (IMD2 / IMD3)

A measurement reads a mixing product out of the result by its (k₁, k₂) tag — at three tones the tag simply has three entries, e.g. HB1.V("Vout",&nbsp;"(1,1,-1)") for the triple-beat product f₁+f₂−f₃. The classic figures of merit are the intermodulation ratios in dBc — an IM product relative to its carrier:

IMD2 = dB( HB1.V("Vout", "(1,-1)") ) - dB( HB1.V("Vout", "(1,0)") )   dB
IMD3 = dB( HB1.V("Vout", "(2,-1)") ) - dB( HB1.V("Vout", "(1,0)") )   dB

Replace HB1 with your analysis name and "Vout" with your output node. IMD2 uses the baseband product (1,−1) = f₁−f₂; IMD3 uses the lower third-order product (2,−1) = 2f₁−f₂ (use (−1,2), referenced to the (0,1) carrier, for the upper IM3). To get the curve vs Pin, sweep the drive power (a parametric sweep over the variable your source Pavl references) — the same expression then returns IMD vs Pin instead of a single value.

Two ways to address a product — use the accessor for IMD

The accessor HB1.V("Vout", "(1,-1)") names the node and the product tag and keeps any swept axes automatically, so the same expression works whether or not you have added a Pin sweep — one value without it, a curve over Pin with it. The bracket form HB1.V[:, "Vout", "(1,-1)"] is positional: one token per cube axis (a leading : to keep the Pin sweep), so it must be re-edited when a sweep is added or removed. Prefer the accessor for IMD metrics you keep; reach for the bracket when copy-pasting from a trace card. See Referencing analysis results.

Source impedance and harmonic terminations

With dozens of products spread across the spectrum, each one needs a source termination. circuitRF assigns it by frequency, not by mixing order. It defines a band-center fundamental — for two tones the average f_c = (f₁ + f₂) / 2 — and places each product in the harmonic band nearest its frequency:

band n = round( |f| / f_c ) → Z[n] if declared, else the shared Z

So band 0 (|f| < f_c/2) is the baseband and uses Z[0]; band 1 (around f_c) holds both carriers and all the close-in IM3/IM5 products and uses Z[1] (or the default Z); band 2 (around 2·f_c) holds the second-harmonic products and uses Z[2]; and so on. The decision is purely the product's frequency: as a high-order product such as (3, −1) drifts down past the band-1/band-2 boundary it switches from Z[2] to Z[1] automatically — no order threshold is hard-coded. This is the same harmonic-termination model as P1Tone, and a PnTone presents its shared Z and per-band Z[k] to the whole multi-tone spectrum this way.

Why baseband termination matters

In single-tone HB the baseband is decoupled. In two-tone it is not — the IM2 product (1, −1) at f₁−f₂ lands at baseband, and its termination Z[0] feeds back into the carriers and the IM3 levels. Studying source/load baseband-termination effects is a primary reason to run two-tone, so set Z[0] deliberately.

What's different from single-tone

Full design + algorithm: docs/design/harmonic-balance.md and the harmonic-termination model in docs/design/p1tone-harmonic-terminations.md (in the repo).


Running an analysis from the command line

Everything above is driven from the GUI's Run button, and everything above also runs headless. The command-line driver takes a .cnl netlist — hand-authored, or extracted from a schematic — and runs one analysis:

circuitrf sparam <file.cnl> [--freq start:stop:step] [-o out.sNp]
circuitrf dc     <file.cnl>
circuitrf hb     <file.cnl>       harmonic balance; runs the sweep if one wraps it
circuitrf lp     <file.cnl>       loadpull over the directive's Gamma grid
circuitrf lpp    <file.cnl>       loadpull pursuit: searches for MXP / MXE
circuitrf em     <file.cem>       electromagnetic extraction of the layout it names
circuitrf elab   <file.cnl>       dump the elaborated netlist

From a source checkout, put dotnet run --project src/Cli -- where circuitrf appears.

The full reference for every verb, its options, its output and its exit codes is The Command Line — including a worked EM run from an empty folder. This section covers only the part that is about analyses rather than about the driver.

Harmonic balance from the command line

dotnet run --project src/Cli -- hb hero2.cnl -o out.mat

That runs the netlist's harmonic-balance analysis — single- or multi-tone; the same verb does both — prints the spectra as tables, and writes out.mat.

Option What it does
-a, --analysis <name> Which analysis to run. Optional when the file declares only one HB chain.
--set <var=expr> Override a global variable before elaboration, so anything derived from it follows. Repeatable.
--maxharm K Override MaxHarm.
--maxmix M Override MaxMixOrder (multi-tone only).
--tol t, --max-iter N Override the convergence tolerance and the iteration cap.
--rows N, --all How much of each printed table to show. Default is a truncated head.
--diag Engine convergence diagnostics to stderr.
-o, --export <path> Export the results. The extension picks the format: .mat, .npy or .txt.
--kits <dir> A folder of installed kits, for externally-supplied device models. Repeatable; valid on any verb.
dotnet run --project src/Cli -- hb hero5.cnl --set Pavl_dbm=0 -o hero5.txt
The sweep runs, whichever name you give

When a parametric sweep wraps an analysis, the sweep is what runs — and naming the inner analysis with -a is promoted to its outermost enabled wrapper, with a line on stderr saying so. That promotion exists because running the inner analysis alone gives a converged, plausible, complete-looking result with the sweep axis silently missing. It is the same rule for hb, lp and lpp: a frequency-swept loadpull has exactly that shape.

Loadpull and pursuit from the command line

dotnet run --project src/Cli -- lp  hero3.cnl  --pin -20:1:15 -o hero3.spl
dotnet run --project src/Cli -- lpp hero3B.cnl --out-grid found.gam -o hero3B.npy

lp prints one row per Γ grid point — where it was, how it stopped, and its figures of merit at the last converged, non-tickle drive step — rather than the raw [gridPoint × driveStep] cubes. lpp prints its MXP and MXE optima first, then the follow-on grid. Both take the HB options above, plus --pin, --compression, and one grid option each: --grid for lp, --out-grid for lpp. lp also exports .spl and .lpcwave, the loadpull interchange formats the Data Display reads back. Full detail.

Electromagnetic extraction from the command line

dotnet run --project src/Cli -- em Amp.cem

em takes a .cem EM setup and needs no other arguments — the layout and the technology resolve by walking up to their own workspaces, exactly as they do in the GUI. It writes the Touchstone and the diagnostics .npy where the Simulate button writes them, so a schematic's SnP reference survives a headless re-run. Full detail, with a worked example.

Measurements, and why headless is the regression harness

The measure lines on the TestBench are evaluated exactly as the GUI evaluates them, and a measurement that fails to evaluate is reported on stderr and the run continues. A .cnl that works headless works when opened, which is what makes the command line usable as a regression harness: the two paths share the elaborator, the engines and the measurement evaluator.


See also: the netlist format (how analyses are written) · Components · Plot types · Measurements · Results & data export · The command line.