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.

A new analysis shows its frequencies in the preferred unit from Settings ▸ General ▸ Units; a unit the analysis or the schematic already states wins.

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), or Single for one frequency; one or more segments, which + Segment adds as a single point. The run solves every segment's frequencies together, sorted, each once. 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.
Noise=yesAlso compute the small-signal noise: the noise-wave correlation, and for a two-port the noise figure and noise parameters. The Noise box in the analysis editor. Default no.
NoiseContrib=yesWith Noise=yes, also write NCs, each part's share of the noise-wave correlation. The box under Noise. Default no.

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.

Noise: which analysis answers what

Noise is not an analysis of its own. Five analyses compute it, each switched on with Noise=yes (on the oscillator, PhaseNoise=yes), and each answers a different question. Without the key, every one of them runs exactly as before.

Each part's noise model is the same in all five. Passive parts are thermal at their own temperature. An SnP block with Touchstone noise data is noisy by that data. Diodes, BJTs, MOSFETs and FETs have shot, channel, thermal and flicker noise as their models declare. A system Amplifier and Mixer are noisy by their NF, and a compiled model by the sources it declares. A part the analysis cannot give noise to is named in a warning, and Noise=no on a part switches its noise off without one. circuitrf explain <file> --analysis lists every part's noise model.

Conventions

Densities Single-sided and absolute. A matched load's outgoing noise wave is kT W/Hz, a resistor's open-circuit noise voltage 4kTR V²/Hz, with k = 1.380649e-23 J/K exactly. Phase noise L(f) is single-sided, in dBc/Hz.
Reference temperature A noise figure is referred to T0 = 290 K: its input termination is at T0.
Part temperature Each part is at its own physical temperature: its Temp, or Dtemp above the ambient, or the ambient. The default ambient is 26.85 °C, which is 300 K, not T0. A matched 6 dB pad at 300 K reads 6.111 dB, not 6.000 dB. temp = 16.85 puts the whole design at 290 K.
Noise data Noise data from a file is absolute, at T0, and does not move with the ambient. A device's Tg and Td are noise temperatures in kelvin.
Ports and sources Noiseless. A port's termination enters only as the T0 reference of a noise figure. In an envelope run, Noise=yes on a source makes its impedance thermal.
Random noise Only an envelope run draws random noise, from Seed (1 by default). The same seed gives the same bits on every computer and in every split of the run.

Worked examples. File ▸ New ▸ Examples ▸ S-Parameter Noise checks the S-parameter noise against closed forms. File ▸ New ▸ Examples ▸ Receiver Noise goes through a 2.45 GHz receiver, one bench per analysis: an LNA noise match, the chain's budget against Friis, a diode mixer's SSB and DSB noise figure, the LNA under a blocker, a noise sourcepull, an NR signal's EVM through the chain with a phase-noisy LO, and the VCO whose phase noise that LO carries.

Small-signal noise

Noise=yes adds the circuit's small-signal noise to the S-parameter run. The noise comes from the same solve: one more back-substitution per port at each frequency, and no further factorization. S does not change. Noise=no, the default, adds nothing.

analysis SP1 type=sparam start=1 stop=6 npts=51 Unit=GHz Noise=yes

What the run adds. The cubes go into the analysis's own group, beside S and Z0. A parametric sweep stacks them as it stacks S.

Cube Axes Unit What it is
Cs [freq, i, j] W/Hz The correlation ⟨b·bᴴ⟩ of the noise waves leaving the ports. Every port is terminated in its own noiseless Z0.
NF [freq] dB The noise figure with port 1 driven from its own Z0 and port 2 loaded in its own.
Fn [freq] — The same noise factor F, linear: 10^(NF/10).
NFmin [freq] dB The minimum noise figure, over every source.
Fmin [freq] — The minimum noise factor, linear.
Te [freq] K The equivalent input noise temperature, T0·(F − 1).
Tmin [freq] K The lowest Te any source gives, T0·(Fmin − 1).
Rn [freq] Ω The equivalent noise resistance, not normalised.
Sopt [freq] — The optimum source reflection, the one that reaches NFmin, on R = Re Z0 of port 1.
Zopt [freq] Ω The optimum source as an impedance, R·(1 + Sopt)/(1 − Sopt).
NFc [freq, part] — Each part's share of F − 1. They sum to F − 1.
NFpct [freq, part] % The same as a percentage of F − 1, which is the number to sort by.
NCs [freq, part, i, j] W/Hz Each part's share of Cs, with NoiseContrib=yes only. They sum to Cs.

Every cube but Cs and NCs is for a two-port only, because a noise figure and noise parameters belong to a two-port. The names are the ones commonly used for them. The linear noise factor is Fn rather than F, because SP1.F("probe") already reads a WSProbe's return difference.

A network with more than two ports gets Cs only. For the noise figure from port 1 to any other port, or from any other source, and for constant-noise-figure circles, use nf(), te(), rn() and ns_circle(): nf(SP1.S, 0, 3) is the noise figure from port 1 to port 3 of a three-port. Gain and stability circles to draw beside the noise circles are ga_circle(), gp_circle() and stab_circle(), and the Noise and gain circles chart shows all of them in one action.

What is noisy.

What is noiseless. The following parts have no noise model here:

These are taken as noiseless, and one warning names them all (sparam.noise.noiseless), so a result never leaves out a part without saying so. Ports and independent sources are noiseless by definition, and they are not named.

Switching a part's noise off. Noise=no on a part makes it noiseless in this analysis:

R:Rbias  b 0  R=10 kOhm Noise=no

Use it for a resistor that stands in for an instrument's load, or one that is part of a model and not a physical resistor. The part is not named in the warning, because the statement is yours. Noise=yes, the default, leaves the part as noisy as its model. The key is accepted on every part: on a Mutual or any of the inductors it couples it quiets the whole coupled group, and on a port, a source or a probe it does nothing (check says so, as check.noise.inert-switch). In the parameter editor it is the Noise check box in the part's own list. Its show-on-schematic box draws it on the symbol, and is off by default.

Which part sets the noise

A two-port run also writes NFc and NFpct on a part axis. The axis has one entry per noisy part, labelled with its instance path. A Mutual and the inductors it couples are one entry, labelled with their names joined by +, because their loss is one network. NFc is the part's own output noise at port 2 divided by the source's, with port 1 driven from its Z0, so F − 1 is their sum. NFpct is each one as a percentage of F − 1. The shares come from the same solve as Cs, at no extra cost. To see them, plot NFpct against part at one frequency on a rectangular plot.

NoiseContrib=yes also writes NCs, each part's share of the whole Cs matrix, for any number of ports. Its size is frequencies × parts × ports² complex values, which is why it is not written by default. circuitrf explain bench.cnl --analysis states that size, and lists every part's noise model: thermal at its temperature in K, the file and band of an SnP block's noise data, a compiled model's noise sources, switched off, or noiseless with the reason. Read it before trusting an NF.

Conventions and noise data

The densities, T0 and the ambient are in Noise ▸ Conventions. Two more belong to this analysis:

An SnP block's noise data. circuitRF reads Touchstone 1.x noise rows: frequency, NFmin in dB, |Γopt|, ∠Γopt in degrees, and rn normalised to the option line's R. Between rows the noise parameters are linear. Outside the rows the nearest row holds, and sparam.noise.data-band says so. A row that no physical two-port could have (0 ≤ Fmin − 1 ≤ 4·Rn·Re Yopt fails) gets sparam.noise.data-unphysical, and it is used as given.

Written out. circuitrf sparam bench.cnl -o out.s2p on a two-port writes a Touchstone noise block after the data. It is in the same 1.x format, with Γopt and rn on the option line's R. The rows are the run's own cubes, so reading the file back gives them again.

A worked example: Pozar's Ex. 12.5. A transistor is described by its S-parameters and its noise parameters in a Touchstone file. The book gives them at 4 GHz, and the file holds them over 3.5 to 4.5 GHz: Fmin = 1.6 dB, Γopt = 0.62∠100° and rn = 0.4 on 50 Ω, which is Rn = 20 Ω.

# GHz S MA R 50
3.5 0.6 -60 1.9 81 0.05 26 0.5 -60
4 0.6 -60 1.9 81 0.05 26 0.5 -60
4.5 0.6 -60 1.9 81 0.05 26 0.5 -60
! noise: f  NFmin(dB)  |Gamma_opt|  angle(Gamma_opt) deg  Rn/R
3.5 1.6 0.62 100 0.4
4 1.6 0.62 100 0.4
4.5 1.6 0.62 100 0.4
Port:P1  p1 0  Num=1 Z=50 Ohm
Port:P2  p2 0  Num=2 Z=50 Ohm
SnP:X1   p1 p2 NumPorts=2 File="pozar_ex12_5.s2p"
analysis SP1 type=sparam start=3.5 stop=4.5 npts=3 Unit=GHz Noise=yes
measure NFopt = nf(SP1.S, polar(0.62, 100))
measure NF2   = ns_circle(SP1.S, 2)

The run gives back the file's NFmin = 1.6 dB, Rn = 20 Ω and Sopt = 0.62∠100°, which is Zopt = 19.2 + j38.2 Ω. NF is 2.948 dB, which is the noise figure from the 50 Ω source, and its noise temperature Te is 282 K against a Tmin of 129 K. NFopt is 1.6 dB again. NF2 is the 2 dB noise circle: its centre is at 0.563∠100° and its radius is 0.245. The book rounds these to 0.56 and 0.24.

Worked examples. File ▸ New ▸ Examples ▸ S-Parameter Noise has three benches, each with a Data Display that opens when it is simulated. A matched 6 dB pad is swept over its temperature, and its NF lies on 1 + (T/T0)(L − 1). A low-noise stage, a pad and a second stage lie on Friis' formula as the pad's loss is swept. A noise-matched LNA shows NF against NFmin, Te against Tmin, its Sopt and two ns_circle circles on a Smith chart, and Zopt.

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
Noiseyes adds noise over the small-signal frequencies. See Noise in harmonic balance.no
NoiseIn / NoiseOutThe port the noise figure is referred to, and the port the output noise is read at.1 / 2
NoiseSidebandThe input sideband that carries the signal: the input frequency is f_out + k·f0.0
NoiseContribyes adds each part's share of the noise.no
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).

Noise in harmonic balance

Computes: the noise figure of a circuit with a large signal in it — a mixer's SSB and DSB noise figure, an LNA's noise figure while a blocker drives it, or a receiver chain's with its LO running. The S-parameter noise analysis cannot answer these: it linearises at the DC operating point, so it has no LO, no blocker and no frequency conversion.

analysis HB1 type=hb Tone=LOfreq MaxHarm=7 SSStart=10 SSStop=200 SSNpts=20 SSUnit=MHz Noise=yes NoiseIn=1 NoiseOut=2 NoiseSideband=1

The small-signal frequencies are the output frequencies — for a mixer, the IF. NoiseSideband says where the signal comes in: the input frequency is f_out + k·f0.

Case NoiseSideband
An amplifier, or an LNA with a blocker as the tone 0 (same frequency in and out)
A down-converter with f_IF = f_RF − f_LO 1
A down-converter with f_IF = f_LO − f_RF -1
Two tones (a blocker and an LO) one integer per tone, 1,0

The image is the opposite sideband (-k). circuitrf explain <file> --analysis prints the signal and image input frequencies at the first and last output point, so a wrong sign shows before a run.

Results, over ssfreq:

Cube Meaning
NF, Fn The SSB noise figure, dB and linear: only the signal sideband counts as signal.
NFdsb, Fndsb The DSB noise figure: the signal sideband and its image both count as signal. Written only when the input and output frequencies differ.
Te The SSB equivalent input noise temperature, K.
Nout The noise at the output port, W/Hz, with the input port's termination at T0 = 290 K.
Gc The conversion (or small-signal) gain from the input sideband to the output, dB. Fn = Nout / (k·T0·Gc).
NFsb [ssfreq, sideband] The share of the output noise that arrives through each input sideband, %: noise folding made visible.
NFc, NFpct [ssfreq, part] With NoiseContrib=yes: each part's share of F − 1, and the same as a percentage. The input termination's other sidebands are one entry.

What is noisy.

A device's noise is correlated between sidebands, so it cannot be split by sideband. NFsb shows the noise from the terminations and passive parts, and it adds up to 100 % less the devices' share. Each device's share is in NFc.

A compiled (Verilog-A) device reports its noise for the whole waveform in one request per operating point.

Inside a parametric sweep, LO power or blocker power is an ordinary sweep axis, so "NF against LO drive" and "NF against blocker level" need nothing more. With the drive turned down to nothing, NF equals the S-parameter noise analysis's.

Oscillator

An oscillator analysis finds a free-running oscillation: its frequency, its amplitude, its harmonics, and how they tune. Harmonic balance alone cannot do this. It needs the fundamental stated, and an oscillator's frequency is part of the answer. Started on an oscillator, it finds the circuit sitting still at its DC operating point, which is always a solution.

Place one OscProbe in the oscillator's loop (series) or across its resonator (shunt), and add an analysis of type Oscillator:

OscProbe:OP1  c 0  Mode=shunt
analysis OSC1 type=osc Fguess=140 Unit=MHz MaxHarm=9

How it works. At the OscProbe the analysis inserts a generator that works at one frequency f, with amplitude A. For any trial (A, f) an ordinary harmonic-balance run at f gives the current the generator delivers (shunt) or the voltage across it (series). Where that is zero, the generator is doing no work and the circuit sustains itself. That is two equations, the real and imaginary parts, in two unknowns, solved by Newton's method around harmonic-balance runs.

Start-up. First, the circuit is linearised about its DC operating point, and the admittance looking into the probe is computed from Fstart to Fstop (by default half and one and a half times Fguess). The circuit starts to oscillate where that admittance has a negative real part at a zero of its imaginary part: the circuit gives back more power than it takes, at a frequency where it resonates. If no such point is found, the run stops and says so, naming the span it searched. It never returns the DC solution as an oscillation. Then the amplitude is raised from small until the negative resistance runs out, which brackets the oscillation, and Newton's method finishes it.

circuitrf explain --analysis shows the start-up search without running harmonic balance.

Key Default
Probe the only OscProbe Which OscProbe, when there are several
Fguess (required) Where the start-up search is centred, in Unit
Fstart, Fstop Fguess/2, 1.5·Fguess The start-up search's span
Unit GHz Unit of the three frequencies
MaxHarm, FFTOverSample, Tol 7, 1, 1e-6 As harmonic balance's
Aguess ramp from small Amplitude to start from: volts in shunt, amperes in series

Results. The run writes the cubes a harmonic-balance analysis writes, at the oscillation (V, I, ToneFreqs and the rest), so every HB measurement and Data Display preset works on it. It adds:

Tuning. Inside a parametric sweep, Fosc against the swept variable is a tuning curve. A measurement gives Kvco, the slope of that curve, with deriv():

analysis SW1 type=parametric_sweep Var=Vtune Start=0 Stop=5 Step=0.25 Inner=OSC1
measure Kvco = deriv(OSC1.Fosc)

What moves the frequency. The oscillation sits a little away from the start-up frequency. As the swing grows, the active device spends part of each cycle cut off, and the capacitance and conductance it adds to the resonator change. A Colpitts, for example, starts where its transistor's low emitter impedance loads one capacitor, and settles toward the resonance of the tank with the transistor off. The harmonics pull the frequency a little too, so raise MaxHarm until Fosc stops moving.

Phase noise

PhaseNoise=yes computes the oscillation's phase noise from the circuit's own noise sources, so the effect of the resonator's Q, the device's flicker noise and the bias can be designed instead of assumed:

analysis OSC1 type=osc Fguess=140 Unit=MHz MaxHarm=9 PhaseNoise=yes PnStart=100 Hz PnStop=10 MHz PnNpts=61

The noise sources are the ones harmonic-balance noise uses: passive parts at their own temperature, each device's noise over its own large-signal waveform (shot, channel and flicker, as the model declares them), and a Mixer's NF. Every Port and Term is the oscillator's load, a resistance at 290 K.

Two descriptions, joined. Far from the carrier, the noise at the carrier's two sidebands comes straight from the conversion matrix about the oscillation, and splits into its phase part (PN) and its amplitude part (AN). Close to the carrier, nothing holds the oscillation's phase, so the noise moves its frequency instead. There the analysis uses how far the frequency moves for a small current at each noise source, which the oscillator's own solve already knows: white noise gives a 1/f² slope, flicker noise a 1/f³ slope. The two are joined at one tenth of the rate at which the oscillation's amplitude settles after a disturbance. Both hold there, and if they differ by more than 0.5 dB, the run warns.

Key Default
PhaseNoise no yes adds the cubes below
PnStart, PnStop 100 Hz, 10 MHz The offsets from the carrier, with an optional mHz, Hz, kHz, MHz or GHz (m milli, M mega)
PnNpts 61 Number of offsets
PnLog yes Offsets evenly spaced in log-frequency, or linearly
PnPort see below The Num of the Port or Term the noise is read at

With PnPort blank, the noise is read across a shunt OscProbe, or at the circuit's one Port or Term when the OscProbe is in series. Close to the carrier the phase noise is the same everywhere. Far from it, it depends on where it is read, so name the output port.

Cube Axes
PN offset Phase noise L(f), dBc/Hz, single-sided
AN offset Amplitude noise, dBc/Hz
PNc offset, part Each part's phase noise, dBc/Hz; together they make PN
PNpct offset, part Each part's share of PN, %
PNcorner — The flicker corner, Hz: where the 1/f³ and 1/f² slopes cross (NaN without flicker)

Offsets at or beyond half the carrier are NaN.

Into a system simulation. Right-click a PN trace's legend entry (or its header in a table) and choose Copy as Phase-Noise Mask. The clipboard then holds PhaseNoise="100Hz:-71.19, …", ready to paste onto a source an envelope run reads.

From the command line, circuitrf osc design.csch runs it (osc).

Circuit Envelope (modulated signals)

Computes: the circuit's response to a modulated signal — LTE, NR, several carriers, a pulsed-CW train, or an imported I/Q waveform from a MOD_Source, designed in the Signal Designer — as a time series of harmonic-balance spectra, and from it the modulated measurements: ACLR / ACPR1 / ACPR2 per carrier, channel power, PAPR and CCDF, AM-AM and AM-PM (with the memory spread), gain, error (NMSE) against the input, drain efficiency from the DC supply currents, and for two widely spaced carriers the IM2 / IM3 / IM5 products. For a pulsed-CW source it measures inside the pulses instead: pulse droop, pulse-to-pulse spread, phase droop, rise and fall (see Pulsed CW: what is measured).

Method. Every envelope time step is one harmonic-balance solve with a time derivative added to each charge (Gear-2, backward Euler on the first step). The linear network is factored once per harmonic for the whole run, and each step costs one back-substitution per harmonic plus a small Newton solve at the nonlinear devices, reusing its factored Jacobian while it keeps converging fast. Each Newton update is damped by a line search, so a device driven hard at a signal peak, or one with trap or thermal states, does not throw the solve off. A step that still does not converge is retaken as up to 16 shorter steps, and the Messages pane says how many were.

Three things make long modulated runs practical:

Frequency-dependent blocks. Resistors, capacitors and inductors enter every step exactly. Other blocks are handled as follows.

Setting Default Meaning
Stop 100e-6 s Signal time simulated. ACPR settles within ~0.1 dB from 0.5 ms of LTE/NR.
Accuracy standard draft / standard / fine = 1× / 4× / 8× the signal rate.
MaxHarm 5 Harmonics of the fundamental at every step.
Envelope auto auto / lattice: carriers more than 3× their bandwidth apart get their own fundamentals, and so does a P1Tone off the carriers' frequencies (an LO). single: one envelope around Fc spans every carrier and their IM3 (smaller step, slower).
MaxMixOrder 7 Lattice order when carriers get their own fundamentals; 7 converges IM5.
Segments auto Parallel-in-time segments, one per core and none shorter than 4× the warm-up; one when the circuit's memory is too long to warm up in parallel. 1 runs sequentially.
Warmup auto Simulated before each segment and discarded. auto is 5 µs, or, with Segments auto, five of the circuit's slowest time constants when that is longer. A number is used as written.
Tol 1e-6 The Newton tolerance per step, in amperes. A run uses at most 10⁻⁴ of its own RF excitation at the record's RMS drive, so a small signal is solved as closely as a large one.
SaveWaveforms yes Keep the envelopes against time (V, I, Pprobe_t_W). no keeps every spectrum and every power, AM-AM and pulse number.
Noise no yes adds thermal noise to the run (below).
Seed 1 The seed of every random draw: thermal noise and every source's PhaseNoise mask.
SeedPerPoint no yes gives each point of a parametric sweep its own seed.
EvmSplit no yes runs the record a second time without noise, to split each carrier's error into distortion and noise and measure the SNR (below). Twice the run time.

What a run records

Name a net to see its voltage. Put an IProbe where you want power.

No impedance is assumed anywhere: a voltage is reported as a voltage, and a power is measured where a current is known. Without an IProbe an envelope run reports voltages only; place one in series with a load to measure its power. A chain of 50 Ω system blocks needs one IProbe in front of its load.

The envelope card shows what the schematic will record ("Records 4 labeled nets and 3 IProbes"); hover over it for the names. Every recorded net keeps its envelopes, so a design that labels 40 nets, swept over five drive levels, keeps about 1 GB of waveforms. Set SaveWaveforms to no to keep only the measurements.

At every labeled net (axis node, in the order harmonic balance lists nets):

Cube Axes Meaning
V time, node, harmonic the voltage envelopes, peak phasors (SaveWaveforms)
VSD node, freq the voltage spectral density in dBV/Hz: RMS volts² per hertz (the same number as dBV/√Hz of the RMS amplitude density)

With two fundamentals the harmonic axis is mixIndex. VSD is one continuous spectrum over every product of the baseband and fundamental zones; into 50 Ω, a VSD in dBV/Hz plus 13.01 dB is the power density in dBm/Hz.

At every IProbe (axis branch, named after the probes):

Cube Axes Meaning
Pprobe_W branch, harmonic average power at each harmonic, in W. Harmonic 0 is the DC power.
Pprobe_fund_W branch average fundamental-band power, in W. With two fundamentals, the sum over the carrier products.
Pavg_dBm branch Pprobe_fund_W in dBm. At the output probe this is the output power.
PSD branch, freq the delivered power spectral density in dBm/Hz, on VSD's frequency grid: ½·Re of the cross-spectrum of the probe's voltage and current. A bin where power flows backwards through the probe is blank.
ACLR branch, carrier, metric Pch_dBm, and ACPR1L / ACPR1U / ACPR2L / ACPR2U in dBc, from PSD
IMD branch, product the delivered power of each carrier and distortion product, in dBm, for two widely spaced carriers (either mode)
PAPR_dB, CCDF branch / branch, prob of the instantaneous delivered power ½·Re(V₁·I₁*)
Gt_dB, DE_pct branch below
NMSE_dB, Delay_s branch the drive aligned to the probe node's fundamental envelope: the residual, and the delay
AMAM, AMPM and their _min / _max branch, Pin_inst AM-AM, AM-PM and memory
EVM_pct, FSKerr_pct, CSSerr_pct and the rest branch, carrier each carrier's received quality (Signal quality)
Const branch, carrier, symbol the received symbols, complex, for the constellation
I time, branch, harmonic the current envelopes (SaveWaveforms)
Pprobe_t_W time, branch fundamental-band power at every step (SaveWaveforms)

Every IProbe gets every row, so the branch axis is the same at every sweep point. A probe that carries no RF, such as a supply or bias probe, is blank in the output rows (Gt_dB, DE_pct, NMSE_dB, ACLR, PAPR_dB, CCDF, AM-AM, AM-PM and EVM): its fundamental-band power is below 10⁻⁶ of the available drive. Its Pprobe_W still reads its DC power.

A design with a wBond also gets WireTemp and WireTempState (axis wire array): each array's hottest wire under the record's average heat (wBond ▸ In a circuit envelope run).

A Parametric Sweep around an envelope analysis (sweeping the source's Pavg, say) gives ACPR against output power in one run. A sweep point that fails is NaN and named in the Messages pane; the other points are kept. A Data Display trace pins a net or a probe by its name, so it keeps reading the same one when a re-run adds nets, probes or a sweep.

Power and efficiency

Two quantities never depend on where the output is, and are scalars:

Scalar How it is calculated
Pin_avg_dBm The available power of the drive: the mean of |Vdrive(t)|² / (8 · Re Zsource), where Vdrive is the source's open-circuit envelope. This is not the power delivered into the input. Every MOD_Source counts.
Pdc_W The sum over every DC source with a non-zero Vdc of −Vdc · ⟨Idc(t)⟩, where Idc(t) is that source's DC branch current at each envelope step. The current follows the modulation, so this is the true average supply power. Gate and auxiliary supplies count; a resistive bias network's own dissipation lowers DE_pct.

At every IProbe:

Cube How it is calculated
Pavg_dBm The fundamental-band power through the probe. Everything inside the carrier's envelope counts, including the adjacent-channel and distortion power. Power at 2f₀, 3f₀ … does not.
Gt_dB Pavg / Pin_avg: transducer gain from the available input power.
DE_pct 100 · Pavg / Pdc: average drain efficiency in the fundamental band. Right for one amplifier; blank when no DC power is drawn.

Each is a time average over the measured record (after the warm-up). With two fundamentals (carriers on the mixing lattice), Pdc_W and DE_pct are not produced; the supply probe's (0,0) product gives the DC power.

Where to place IProbes

Put one in series with each place you want a power:

With the probe's first node on the source side, each of these powers is positive in a working amplifier. The probe node's voltage is what AM-PM, NMSE and the delay are read from, so the output probe belongs at the load itself, not ahead of an output match.

One amplifier. With IProbe:Iin at the input, IProbe:Iout at the load and IProbe:IDC at the drain supply:

Quantity From
Output power Pavg_dBm at Iout
Delivered input power Pavg_dBm at Iin
DC power Pprobe_W at IDC, harmonic 0
PAE (output power − delivered input power) / DC power
Power gain output power / delivered input power

Two amplifiers, A and B, each with its own load and supply: place IoutA, IDCA, IoutB and IDCB. Each amplifier's drain efficiency is its own Pprobe_fund_W at Iout over its own Pprobe_W at IDC, harmonic 0, and the total DC power is the sum of the two.

In a measure line, ENV1.P("probe") reads these cubes by name (see Power through an IProbe):

measure Pout_dBm = dBm(ENV1.P("Iout"))
measure Pin_dBm  = dBm(ENV1.P("Iin"))
measure Pdc_W    = ENV1.P("IDC", 0)
measure Gain_dB  = Pout_dBm - Pin_dBm
measure DE_pct   = 100 * ENV1.P("Iout") / ENV1.P("IDC", 0)
measure PAE_pct  = 100 * (ENV1.P("Iout") - ENV1.P("Iin")) / ENV1.P("IDC", 0)

The same lines work inside any sweep, and on a harmonic-balance analysis (HB1.P("Iout")).

AM-AM, AM-PM and memory

With one fundamental and a modulated drive, every IProbe gets AM-AM and AM-PM against the instantaneous available drive power, |Vdrive(t)|² / (8 · Re Zsource), on a fixed grid of 0.5 dB bins (axis Pin_inst, −40 to 60 dBm). A bin needs 16 samples; one with fewer is blank.

The memory cloud is every one of those samples on one time axis, so the spread can be seen point by point rather than as two edges:

Cube Axes Meaning
Pin_inst_t_dBm time the available drive power at each sample
Gt_t_dB time, branch the gain at each sample: the values AM-AM bins
PM_t_deg time, branch the phase at each sample: the values AM-PM bins

They keep at most 20,000 samples per probe (every m-th, m set by Stop and the step alone, so a sweep stacks), and they are kept with SaveWaveforms no. Plot the cloud with a versus trace drawn with markers and no line:

ENV1.Gt_t_dB[:, "Iout"] vs ENV1.Pin_inst_t_dBm

Under a sweep of the drive, each level draws its own cloud with no edit.

Pulsed CW: what is measured

A pulsed-CW source turns the run into a pulse measurement. Harmonic balance cannot see what happens inside a pulse: it solves one steady state, so the device sits at one temperature and one trap state. An envelope run steps a ComposedFET's thermal and trap states, and the bias network's storage, as real memory over time, so the output falls across the top of each pulse as the channel heats and the drain supply sags.

The run starts from harmonic balance at the first sample of the record, which for a pulse is the off-time, so the first pulse starts from the quiescent point. A record that started on the pulse top would start with the device already at its CW temperature, and the first pulse would show no droop at all; that is why a pulse's Delay must be greater than zero.

At every IProbe, from the power delivered through it, it adds:

Cube Axes Meaning
Pout_t_dBm branch, time delivered power at every step, floored at −100 dBm in the off-time, so it plots with no arithmetic
PulseTop_dBm branch, pulse the power at the droop window's start
PulseDroop_dB branch, pulse the power at the window's start minus at its end: positive is drooping
PulsePhaseDroop_deg branch, pulse the probe node's phase (relative to the input) at the window's start minus at its end
PulseRise_s, PulseFall_s branch, pulse the 10–90 % times of the amplitude (the square root of the delivered power)
PulseToPulse_dB, PulseToPulse_deg branch the peak-to-peak spread over the pulses of PulseTop_dBm, and of the phase at the window's start; the first pulse counts

The droop window is the flat top from MeasureStart to MeasureEnd (10–90 % of the top by default; set them on the carrier). A pulse only partly inside Stop is not measured, and the Messages pane says how many were and over which window. ACLR, CCDF, PAPR, AM-AM / AM-PM and NMSE are not reported for a pulse: they are defined for a modulated signal, and on a pulse train they print numbers that look like results. Pin_avg_dBm, Pavg_dBm, Gt_dB, Pdc_W and DE_pct are averages over the whole run, off-time included, with no settling skipped (the train starts at quiescent and has nothing to settle), so they depend on how many pulses Stop holds. Pdc_W is what the DC sources deliver, so a drain fed from a storage capacitor draws most of each pulse's energy from the capacitor and repays the supply after the pulse: end Stop once the last pulse's recharge is over (about five of the supply resistance × storage capacitance time constants after its fall), or DE_pct reads high — above 100 % on a run that stops at the end of a pulse.

Before the run starts, the Messages pane states how many pulses Stop holds and the circuit's slowest memory — a thermal pole, a trap's emission, the bias network — against it: a run shorter than about five of those time constants shows the first pulse from cold, not a settled train. A drain-lag trap that captures at the pulse peak in nanoseconds and emits over a millisecond lowers the quiescent current after the first pulse, so pulse 1 differs from pulse 2: that is current collapse, the effect a pulse-to-pulse figure exists to report. A thermal train settles only after several of its slowest time constants, which on a package pole means milliseconds of record at a step of a few nanoseconds.

From the command line: circuitrf env design.cnl [--stop 0.5e-3] [--accuracy standard] [-o out.npy]. It prints each IProbe's power, gain, efficiency, NMSE and PAPR, and the ACLR and signal quality (EVM, FSK error or the de-chirped error) at every probe that carries RF. For a pulsed-CW source it prints each pulse's top power, droop, phase droop, rise and fall at those probes, then the pulse-to-pulse spread.

Noise and phase noise

Without these settings every signal in an envelope run is noiseless: the spectrum has no floor and an LO is a perfect tone. Two settings add what a receiver is limited by.

Thermal noise (Noise=yes). Every part the S-parameter noise analysis makes noisy adds noise to the run: resistors and other passive parts at their own temperature, S-parameter blocks with noise data, system amplifiers with an NF, and a Mixer's NF as a source at its RF port, as in harmonic balance. At every step, each harmonic band gets a random current on the part's nodes, with the part's noise at the centre of that band. The noise is taken as flat across one band, so a part whose noise changes noticeably across the signal bandwidth is evaluated at the band centre. The PSD then shows the noise floor, and ACLR includes it.

Phase noise (PhaseNoise= on a source). P1Tone, PnTone, the tone sources and MOD_Source take a phase-noise mask, single-sided L(f) in dBc/Hz at offsets from the carrier:

P1Tone:LO  lo 0  Pavl=7 dBm  Freq=2 GHz  PhaseNoise="1kHz:-80, 10kHz:-100, 100kHz:-120, 1MHz:-140"

Seed. The same Seed gives the same noise on every run and on every computer, and a run split into parallel segments draws the same noise as one run. Every point of a parametric sweep uses the same seed, so the differences between points come from the circuit; SeedPerPoint=yes gives each point its own.

Signal quality: EVM, FSK error and the constellation

At every IProbe, each carrier that carries symbols is demodulated from the probe's voltage and compared with what the source sent. NMSE measures the waveform; these measure what a receiver reads. Each modulation is measured the way its receiver reads it:

Carrier Measured as Picture
LTE, NR EVM over every subcarrier of every OFDM symbol, as the Signal Designer measures the source's own signal constellation
DBPSK EVM at each bit centre, where a bit peaks at ±1 and its neighbours are zero constellation
FSK, GFSK, GMSK the frequency at each bit centre against the frequency sent: FSK error, deviation and offset frequency at each bit
CSS de-chirped: each chirp times the conjugate of the chirp sent, with one gain per chirp, as a receiver reads each symbol on its own —

LTE, NR and DBPSK (axes branch, carrier):

Cube Meaning
EVM_pct, EVM_dB RMS EVM, in percent and in dB
EVMpeak_pct the largest error, over the RMS of the ideal points
MER_dB −20·log10(EVM)
Const (branch, carrier, symbol) the received points, after the delay and gain are removed (at most 6000 per carrier)
ConstRef (carrier, symbol) the ideal point at each of those

FSK:

Cube Meaning
FSKerr_pct the RMS frequency error at the bit centres, over the nominal deviation, after the deviation and the offset are fitted
FreqDev_Hz the deviation measured: the fitted scale times the nominal one
FreqOffset_Hz the carrier frequency offset measured
FreqPts (branch, carrier, bit) the frequency at each bit centre, offset removed
FreqRef (carrier, bit) the frequency sent there

CSS: CSSerr_pct, the square root of the energy outside each symbol's own FFT bin over the energy in it, after de-chirping, in percent. FSK and CSS have constant envelopes, so a constellation would be a circle; these say more.

Distortion and noise (EvmSplit=yes). With Noise=yes or a PhaseNoise mask, EvmSplit runs the same record a second time with every noise source and mask off. Each error then splits into its distortion and its noise, and the in-channel SNR is measured:

Cube Meaning
EVMdist_pct, FSKerrDist_pct, CSSerrDist_pct the error without noise: the distortion
EVMnoise_pct, FSKerrNoise_pct, CSSerrNoise_pct √(error² − distortion²): the noise's part
SNR_dB the in-channel signal over the in-channel noise, the noise being the difference between the two runs

For LTE, NR, DBPSK and CSS the noise's part is 1/√SNR. FSK reads its frequency, and noise moves the frequency more than the amplitude, so its error is larger than that. The split is what decides whether to fix linearity or noise. Over a short record the noisy run can read slightly less than the noiseless one; the noise part is then 0 and the run says so. Without EvmSplit, SNR_dB is not written: the spectrum's floor alone would mix distortion into it. explain states the second run, and check says when there is nothing to split.

In the Data Display. Signal quality (on the toolbar, and Open Signal Quality on a result file's menu) adds the picture, a table of every measure the result holds, and the output spectrum, at the probe carrying the signal. The constellation plots imag(Const[…]) against real(Const[…]), points only, with Equal X and Y scale set in Axes Limits, so a circle of error looks like a circle. FSK's picture is the frequency at each bit, received over sent. CSS has the table and the spectrum.

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).

Noise figure in loadpull and sourcepull

Noise=yes adds the noise figure to a loadpull, beside Pout, gain and PAE. Swept on the source side (Sweep=Source), its contours with gain and PAE on the same chart show the trade an input match is chosen on.

analysis LP1 type=loadpull Tone=2.4 ToneUnit=GHz LoadTuner=TL SourceTuner=TS Grid="source.gam" Sweep=Source Noise=yes

At every drive level of every grid point that converges, circuitRF reads the harmonic-balance noise on that drive level's own large-signal waveform. NF [gridPoint, pinStep] (dB, SSB) is written with the other results, the contour plot offers it as a metric, and a .spl export carries it as one more column. A .spl reader that predates the NF column skips it and reads the rest.

Key Meaning Default
Noise yes adds NF. no
NoiseFreq, NoiseFreqUnit The output frequency the noise is read at. 1 % above the tone
NoiseIn The termination NF is referred to: a Tuner's name, or the Num of a Port, Term or P1Tone. the source tuner
NoiseOut The termination the noise is read at. the load tuner
NoiseSideband The input sideband: the signal comes in at NoiseFreq + k·Tone. 0

Why not at the tone itself. Under drive, noise near the carrier is low-frequency noise that the drive carries up, so how much of it there is depends on how close you look. 1 kHz off the tone, flicker noise and a high-impedance bias network can add several dB that a signal 10 MHz away never sees. By default the noise is read 1 % above the tone, the noise a small signal beside the drive sees. For a receive band, set NoiseFreq to it. A NoiseFreq exactly at the tone, or at any half-multiple of it, is read 1e-6 of the tone above, and the run says so.

The tuners are noiseless, as ports are. The input termination is at T0 = 290 K. At the low drive levels of the ladder, NF equals the S-parameter noise analysis on the same terminations. A small-signal sourcepull's NF contours are the circles ns_circle draws.

It costs about one Newton iteration per drive level. On a pursuit, the same keys add NF to its follow-on loadpull. The search itself does not read noise.

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).


TRL Calibration

A TRL calibration finds the two fixtures between your instrument and the part you measured (the launches, probes or connectors on each side), from three standards measured through those same fixtures. It writes them as two .s2p files, which two Deembed blocks then take off any other measurement made through the same fixtures. It solves no circuit.

Place three SnP blocks on a test bench, one for each measured standard, and add an analysis of type TRL Calibration:

Fixture A's port 1 is the instrument side and port 2 the part's. Fixture B's port 1 is the part's side and port 2 the instrument's. So the chain reads left to right as the measurement did: Port 1 — Deembed(A) — measured part — Deembed(B) — Port 2, with no flipping.

The usable band. TRL tells the two fixtures apart by how much longer the Line is than the Thru. Where that difference is near 0° or 180°, the two standards are nearly the same and the answer is unreliable. A run warns about every point outside 20° to 160° (modulo 180°), naming the frequencies. One line covers 8:1. A line that is 90° longer at the middle of your band, f_mid = (f_low + f_high) / 2, is a good choice; cover a wider band with a second line and a second calibration.

The analysis's result holds SA and SB (the two fixtures, as S cubes), LineDeg (the Line's extra length over the Thru, in degrees), LineLossDb, Gamma (the solved reflect) and Usable (1 inside the band, 0 outside), to plot like any other result. A measurement reads a fixture's entry by port number, as it reads S: abs(CAL.SA(2, 1)).

SnP:THRU     t1 t2 File="measurements/thru.s2p"    NumPorts=2
SnP:REFLECT  r1 r2 File="measurements/reflect.s2p" NumPorts=2
SnP:LINE     l1 l2 File="measurements/line.s2p"    NumPorts=2
analysis CAL type=trl thru=THRU reflect=REFLECT line=LINE reflectType=short fixtureA="fixtures/A.s2p" fixtureB="fixtures/B.s2p"

From the command line, circuitrf trl bench.csch runs it (trl).

A Deembed whose fixture file a TRL analysis in the workspace writes, run before that analysis, stops naming the bench to run first. The worked example is TRL and De-embedding (File ▸ New ▸ Examples): a calibration from synthetic standards, a filter de-embedded with its results and laid over the filter itself, and a GaN FET de-embedded to its intrinsic plane.


Tuning

The Tuning panel puts a slider on any value you choose and re-simulates as you move it, with every open Data Display following along. Nothing in the schematic changes until you Push. The whole chapter — choosing values, ranges, complex values by their parts, presets, snapshots — is Tuning.


Optimizer

The Optimizer panel searches the values you choose for ones that meet your goals, each goal a limit on an expression over one analysis, with every open Data Display following the best point found so far. Variables, goals, the algorithms and when to use each, railed variables and snap and polish are in Optimization.


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.