Skip to content

1.0.0-beta.5 Public Beta

Pre-release
Pre-release

Choose a tag to compare

@potatobeanradio potatobeanradio released this 30 Aug 04:21
· 171 commits to main since this release

Fifth public beta.

Significant Match Designer Improvements

  • now supports lowpass & highpass (and pre-existing bandpass)
  • now supports dual-band and tri-band synthesis

Electromagnetic MoM Solver

  • now solves ~10x faster with ~2.4x reduction in memory use

New Components

  • added BJT, ITone, and Voltage Controlled Current Source (VCCS)

Loadpull Display

  • Loadpull contour markers now read out all contours on the plot

Details

The Match Designer

A Match is no longer only a bandpass ladder. Two new forms join it — lowpass and
highpass — each a ladder of single elements matched between F1 and F2, with the impedance ratio
pinned by the ladder's own transparency at DC (lowpass) or at infinity (highpass). Order keeps its
meaning and the element count is 2n in every form. Chebyshev and Butterworth only: once the ratio is
pinned the family has one free parameter left, so neither a second prescribed Q nor a delay target
exists for these forms. Form is an additive field defaulting to Bandpass, so a design written before
this release rebuilds the identical ladder.

Multi-band matching. The Designer now synthesises dual-band networks via a frequency mapping
and tri-band networks via a multi-interval Remez fit, both feeding the existing prototype and
element-extraction path — so a multi-band solution flattens to a cell, plots and simulates exactly
like a single-band one. Odd element counts are synthesisable too: an odd extraction terminates on a
conductance ratio. Butterworth has no tri-band member, structurally, and the Designer says so rather
than searching for one.

New feasibility hints show the per-band Fano ceiling and the gap rise beside the finished
solution list, so an unreachable specification is visible before the search runs rather than after
it fails.

A Match can carry a DC block. The Designer's DC Block toggle puts a capacitor in series with the
first real shunt inductor on each termination's DC path — found by walking the ladder rather than by
looking at the end node, so a pi of inductors gets both its hosts and an interior collision becomes
interior rather than impossible. The series inductance is compensated so the arm's reactance is exact
at f0 and second order elsewhere; the status line and the flattened record both name the route.

Four of the design note's own claims did not survive measurement, and the code follows the
measurement rather than the note. The orientation of a lowpass or highpass ladder is not free:
the family depends on the terminations only through Γ₀², which is invariant under r and 1/r, so one
extraction serves both and the low-impedance port always takes the series element. A shunt
capacitance on the low side of a step-up is therefore absorbable by neither new form. The general
numerical route cannot solve this family past order 4 — 144 cells of a 360-cell sweep fail, every
order-6 cell among them — so the roots are written down rather than searched for, taking it to zero
failures. And the near-end element is unimodal in K rather than monotone, so the search scans before
it bisects.

The Match Designer's UI, rebuilt around the Solutions list

Clicking a solution card applies it. Arrow keys step between cards and yield to a selected
marker, and applying no longer scrolls the list out from under you.

More room to see the solutions. Band and Ripple merge into one card, Probe moves onto its
termination's heading, the Specification pane sizes itself, and all the freed height goes to the
Solutions list. The filter gains a Form group above Orders, and every card names its form. Undo
and Redo sit on the title strip.

A termination edit or a probe now always lands on a solution that meets the target when one
exists
, drawn from the filtered list, with candidates widening past the design's own family,
nearest first. Two bugs had been stopping that: a refused design still reported itself as on-target,
and the request was being spent on the first cell to land, which is the empty one.

Solution cards are selectable text, return loss is quoted signed, group delay carries its unit,
and the Match symbol draws its own design. A pi/T switch no longer re-frames the schematic.

The two Chebyshev families are renamed single-match and double-match — the broadband-matching
literature's own terms for one prescribed reactive termination against two. Display only; nothing in
a saved file changes.

Match fixes

  • Every undo added an entry. A two-way-bound slider wrote back mid-undo, so redo was wiped and
    eight edits could take fourteen undos. A coercing control's write-back is not an edit.
  • One gesture is one undo entry again. Edits are now held against the analysis landings, which
    can write the design mid-edit.
  • Flatten to Cell wrote 15 significant digits into the flattened element values.
  • The synthesis refusal was rendered three times.
  • The solution search's generation guard was check-then-lock, so a stale search could land over
    a newer one.
  • A lowpass or highpass ladder has no Norton pairs — every like-kind element in one shares an
    orientation — and the transform rack now says so with a note rather than the refusal wording,
    because that design is finished, not stuck.
  • The Ripple row's note was putting a scroll bar on the specification column whenever both
    terminations carry a reactance, which is most designs; it moves to the row's tooltip.

EM simulation: about 10x faster, on layouts that used to be refused

If you run the EM simulator, this is the release you will feel. Twelve milestones of work on the
planar method-of-moments engine landed together. Here is what changes on screen.

Your sweeps finish in a fraction of the time. A frequency point on a mid-sized board that took
just over a minute now takes a handful of seconds — roughly 10x end to end. A sweep you used to
start and walk away from is now something you wait through.

Bigger layouts fit in the same machine. Memory per point dropped about 2.4x, so the same 1 GB
that used to cap you at roughly 4,450 unknowns now carries about 6,970 — half again as much
structure at the same mesh, or the same structure at a finer one.

Turn the accelerated solver on for everyday parts, not just big ones. It used to be a loser below
roughly 3,700 unknowns, so for most real parts it was the wrong choice. It now pays from about
1,100 unknowns up, and at 3,700 it is around 13x faster than the dense solve. If you have been
leaving it off out of habit, turn it on.

Runs that used to be refused by the MoM solver are now accepted. Three kinds in particular:

  • Layouts with ground vias or more than one metal level can use the accelerated solver at all now.
    Previously they were forced onto the dense path and hit its ceiling. At 15,000 unknowns this is
    about 15x less memory than dense.
  • De-embedded runs with wide ports. The reference-impedance step was always solved densely no
    matter what your settings said, so a wide port could refuse a run whose actual structure would have
    solved comfortably. One such run that refused outright now finishes 3 points in 107 s.
  • Refined meshes that used to stall. A mesh ladder that climbed from 21 to 143 to 372 solver
    iterations and then failed to converge now runs 21 → 28 → 36.

The messages tell you the truth. Refusals used to quote about a quarter of the memory a run really
needed, so "this will not fit" arrived with a number that made no sense. A via-bearing mesh could also
pass the estimate you read before pressing Simulate and then be refused by the run itself. Both are
fixed, and the run-start message now names the solver kernel it actually resolved to instead of
hedging, reports adaptive sampling as an outcome — with a reason when it disagrees with your setting —
and no longer claims a modelled remainder on a sweep where every point was solved.

One piece of practical advice, from measuring the default. With adaptive frequency sampling on,
asking for more frequency points is close to free, and asking for fewer buys you almost nothing.
The saving comes from skipping points the engine can already predict, and the panel's default
101-point grid is not dense enough for there to be many. The same structure at 401 points solves
only 81 of them in 3.01 s
, against 4.25 s for a non-adaptive 101-point sweep of the same band — four
times the resolution, in less time. So raise the point count; you will get a smoother curve for less
wall clock than you are spending today. (This also corrects the documentation, which promised a 5–10x
reduction in solve count at the default settings. On the default grid it is 1.0–1.7x. The 5–10x is
real, but you have to ask for the finer grid to collect it.)

Nothing about your existing setups needs to change to get any of this, and no result moves: the
accuracy gates on this work are exactness against the old arithmetic, not a tolerance. The engineering
detail is at the bottom of these notes.

Three new components

  • BJT — a charge-control bipolar transistor intrinsic model, both polarities (BJT_NPN /
    BJT_PNP): transport currents with Early effect and high-level injection, junction and diffusion
    charge, and Rb/Re/Rc on internal nodes with base-resistance modulation. Two palette tiles, one law.
  • ITone (I_1Tone / I_nTone) — a current excitation, the dual of VTone: the same tone
    table, the same DC offset and the same multi-tone behaviour, with I/Idc where VTone has V/Vdc. The
    shared machinery is a common base type, so a current tone source cannot slip past a
    commensurability check or a sweep-point re-evaluation.
  • VCCS — an ideal voltage-controlled current source, four terminals in two pairs, with the
    control pair drawing no current at all.

Both sources are linear, so both work in every analysis — DC, S-parameters and every retained
harmonic of a harmonic-balance run, plus the sweeps built on those. Current direction is drawn on
both glyphs and pinned by signed assertions, because a reversed current source still solves and still
plots: ITone's arrow points up, since an independent source delivers into its first pin, and the
VCCS's points down, so a 50 Ω, 10 mS stage is inverting and measures S21 = −0.25.

Loadpull contour markers read out the whole plot at one termination

Resolves #2. A marker dropped on a loadpull contour now reports every contour trace in its
plot
at the termination it sits on, so power and efficiency at one tuner setting is a single
reading rather than three markers. Rows, in order: marker name, one row per contour trace in
placement order, the impedance in ohms, then Γ on a Smith or polar plot. The marker editor popup
shows the same set, and every other marker's info box is unchanged.

The impedance row reads rectangular (R+jX) — a termination written 133.3∠26.6° is not the number
that goes into a matching network — and converts with the loadpull surface's own convention against
the contour trace's own Z0, which is the reference the fitted surface agrees with.

Prefixed voltage, current and power units were being ignored

Vdc=2 mV resolved to two volts, and I=2 mA to two amps. mV, kV, uV, nV, mA, uA,
nA, mW, uW and kW all sat in the units table as identity markers, so every one of them fell
through to a multiplier of exactly 1. The value parsed, stamped, converged and plotted, and nothing
anywhere reported it. A sweep written from 1 to 3 mV ran from 1 to 3 volts.

They now carry their real scale factors. Nothing in the shipped test data used one of these
spellings, but any design of your own that did was running 1000x off and is now correct — worth a
look if you have a bias source or a small-signal drive written with a prefix. The three base symbols
V, A and W deliberately stay as they are: their multiplier of 1 is already right, and W is
also this codebase's own name for a microstrip width.

Other fixes and improvements

A click in a tool panel could freeze the machine. Clicking anywhere in the Properties inspector
was being treated as activating that panel as a document, which wiped the cell properties being
viewed mid-click and replaced them with the empty-state placeholder — and is also why the inspector
only sometimes picked up a newly selected cell in the project tree. Worse, the press hid the panel
before a ComboBox inside it finished handling the same click, leaving a latch set on a now-hidden
control; a hidden ComboBox cannot open a dropdown, so it retried forever, allocating a native popup
window every turn. One hung process carried 5,597 live popup roots against 66 ComboBoxes, and because
an open popup holds a system-wide input grab, no other application could be focused to diagnose it.
Both the cause and a defence-in-depth guard against the retry loop are in.

The active document follows the focused pane. In a side-by-side split, Save, Close Window, Run
Analysis, Generate Netlist, Check Design Rules, the exports and the undo target all acted on whatever
the first pane showed, whichever pane you were actually working in. Moving between panes now
retargets all of them, including undo.

⌘S reaches a torn-off window. In a floating layout window ⌘S toggled geometry snap instead of
saving: the snap toggle matched S with no modifier guard, and no torn-off document window had a
Save binding at all. Both halves are fixed.

Rulers.

  • Ruler labels can be placed by hand. F5 moves the selected ruler's readout anywhere in the
    layout; the position and a text anchor persist in the .clay and export to DXF as the dimension's
    text midpoint and attachment point. Editable in the Properties Inspector and the Edit Ruler dialog,
    with a Reset button and a context-menu item. An untouched ruler serializes byte for byte and renders
    exactly as before.
  • Rulers paint above wires. A ruler measuring a bond wire was rendering underneath it, in the
    canvas and in both wBond export paths. DXF ordering was already correct and is now pinned.
  • A press on a ruler no longer starts a marquee that silently selected every wire it swept.

wBond drag gestures. Shift constrains a move drag to ortho, anchored at the press, for wires,
points and segments alike. Alt duplicates — ghosts of the copies follow the cursor, the originals
stay put, and it commits as one undo entry. Alt at the press on a wire end still stretches the span,
which is the only moment that can be decided; Alt at any other time copies. Wires and primitives
selected together now drag as one
, with a single Ctrl+Z across both histories. The Layout Editor's
DXF export also carries the bond wires it had been dropping.

The update download shows a progress bar you can stop. Checking for updates used to sit on one
unchanging line for the whole of a 160 MB transfer with no way to cancel. One row now spans download,
verify and stage — verification of a 335 MB bundle is slow too, so a bar that finished at 100% and
went quiet would have moved the stall rather than removed it. It starts indeterminate rather than at
zero, because DNS, TLS and the redirect are the seconds before the first byte and 0% reads as stuck.
The Cancel is wired into the scheduler, so the background check gets one too — that is the path
moving 160 MB unprompted. Stopping is safe, but it does start over rather than resume: resuming into
a release re-published at the same version and size would yield the right length with the wrong
content, and a hash failure blacklists that version permanently.

An interrupted update no longer reports a crash, a downgrade, or a lost rollback. Auto-updating to
beta.4 could announce on first launch that circuitRF had not shut down cleanly when nothing had
crashed — the updater replaces the running process image in place, so the process id survives and the
new image promoted a session nobody owned. beta.4 fixed that going forward, but could not reach a
build already installed; the receiving build now recognises its own predecessor instead. Reviewing
what else a force-quit could leave turned up three more, all the same shape — a disk operation and the
record of it are two steps:

  • On macOS, a kill between the bundle exchange and the record left the state file saying staged
    while the disk said installed, so the next launch exchanged the pair back, relaunched the old
    version and released the rollback copy. The user was silently downgraded and the update
    destroyed.
    Now closed by a marker written before anything moves.
  • On Windows and Linux, a kill between the pointer flip and the record recorded the running
    directory as the rollback — by then, the new version. Revert would have restored a failing version
    to itself and reported success. It now records no previous directory and refuses rather than lying.
  • Two residues outside the updater's own directories: a bundle stranded by the non-atomic fallback,
    now adopted as the rollback rather than deleted, and a leaked disk-image mount, now swept.

Release notes open on the first launch of a new version. This dialog is why you may be reading
these in the application. It appears over the workspace the first time a newly installed version
runs, never on a clean install and never twice for the same version, and works for any published
release rather than only the newest. Always Show New Release Notes sits at the dialog's bottom
left and again in Settings ▸ Security & Permissions ▸ Updates. The fetch is an outbound network call,
so the same overrides that suppress the update check suppress it; the plain "Automatic updates"
preference deliberately does not, since a version installed by hand is still a new version.

Smaller things.

  • A parameter group added with the + button carried blank expressions, and a blank expression
    renders no schematic label whatever the show on schematic box says — so ticking it appeared to do
    nothing. Every group whose members show on the schematic now states a real default.
  • Three leftover debug lines that fired on every impedance commit in the shipped GUI are gone.
  • The build is back to zero warnings.

Technical detail: the P1–P12 planar MoM performance series

For anyone who wants the mechanism behind the 10x speed boost. Each milestone is independently gated, and every
figure below was measured rather than projected.

P1 — honest memory accounting. The refusals had been quoting 381 MB against a real 1,338 MB.
Every ceiling now reports what a run actually holds.

P2 — cheap memory wins. The cached kernel cores were trimmed 24%.

P3 — multi-level fill scaling. Parallel scaling tops out around 3.7x on a ten-core machine with
four performance cores and six efficiency ones, which is the ceiling every later parallel figure is
measured against.

P4 — the moment cache. One quadrature pass per cell pair instead of four. The fill fell 2.7–3.0x
and the once-per-mesh core build 3.6–4.0x. Note that the ŷ block integrates a cell pair in both
orientations, so an orientation-blind cache could not have met its own accuracy gate.

P5 — translation classes. Memoise by translation class rather than by pair: a further 2.8–5.8x on
the fill and 3.7–41x on the core build, the large end on repetitive artwork. Per-entry equality to
1e-12 is unattainable here for structural reasons (absolute-coordinate roundoff and a cancellation),
so the gate is on √(Z_ii·Z_jj) instead.

P6 — AIM state built once per mesh. The singular quadrature behind every near-field entry is paid
once per mesh now rather than once per frequency. This, with P4 and P5, is what moved the
accelerator's crossover from N ≈ 3,700 to N ≈ 1,100.

P7 — the dense solve is an in-place complex-symmetric LDLᵀ. Z is complex-symmetric bit for bit —
both fills compute the lower triangle and mirror it — and the general LU exploited none of it: 2N³/3
complex multiply-adds on one core, holding L and U as two further full N×N matrices beside the one
the system already had. The replacement is an unpivoted A = LDLᵀ written into Z's own lower triangle:
half the arithmetic, no second matrix, blocked at 64 columns with the trailing update parallel over
destination columns through the fill's own parallel cap, so caps 1/2/4/unbounded are bit-identical.

N        cap 1     cap 10    general LU    LU / cap 10
  552    0.04 s    0.01 s      0.04 s          2.7x
1,980    0.91 s    0.26 s      1.92 s          7.3x
4,836   13.14 s    3.59 s     41.21 s         11.5x

3.1x of that is arithmetic and blocking alone; the rest is the ten cores the LU never used. Resident
peak falls 2.45x, flat. Accuracy: x agrees with the general LU's to 1e-14…5e-13 relative, growth
max|L| is 1.0–4.5 everywhere, and nothing came within three decades of the stop condition that would
have required pivoting — so Bunch-Kaufman stays a named follow-up rather than a silent fallback.

Three findings worth another engineer's time. dotnet test builds Debug, and that inverts this
comparison: the general LU is native and moves 1% between configurations, this is managed C# and moves
11x, so Debug reads it as 3.5x slower for code that is 3.1x faster in Release. A synthetic
1/(1+|i−j|) matrix is not a stand-in for a real filled Z when timing a factorisation (97.0 s against
41.2 s at the same N). And a 1e-12 residual gate cannot be applied to the near-DC fixture, because the
general LU misses it too — that is the mixed-potential formulation's own low-frequency breakdown, a
property of Z rather than of the solver.

P8 — the near field spans the image depth. The exact near-field radius was measured in basis
supports
, so refining a mesh at a fixed footprint shrank it in metres — 8.92 h at the shipping
resolution down to 1.28 h at seven times that. The scalar kernel over a grounded slab is
1/ρ − 1/√(ρ²+4h²) plus smooth terms, so past ρ ≈ 2h the residue falls like 2h²/ρ³ rather than 1/ρ:
2h is where the coupling stops being long-ranged, and a near field narrower than it is missing the
dominant part. Flooring the radius at 2h is what turns the 21 → 143 → 372 → non-convergent ladder
into 21 → 28 → 36. It is inert on every mesh either starter technology produces at a shipped
resolution — identical near set, identical factorisation, bit-identical solved current — so no answer
anyone already has moves. Two corrections to the original premise: the radius is also the boundary
between entries computed exactly and entries the projection approximates, so the operator degrades
too and the floor recovers 17x of it; and past the accelerated ceiling the failure mode changed rather
than vanished — N no longer bounds the accelerated path, near entries per row does.

P9 — adaptive sampling, measured. A decision brief; nothing was flipped. Five fixtures on the EM
panel's own default sweep give 1.73x, 1.16x and nothing three times. The explanation is one column
wide: adjacent points of the default grid already differ by 0.13 to 0.63 in |ΔS|, which is 130x to
630x the tolerance, and an interpolant may skip a point only when it predicts it inside that
tolerance. So the saving is a function of the grid, not of the structure. The narrow-resonance
failure mode could not be constructed — the notch depth and frequency were recovered exactly in all 33
configurations run, because for a distributed structure the background phase rotation forces
refinement down to the grid floor and brackets any resonance on the way down. Raising the initial
point count above 5 changes the realised error not at all and the solved count upward, so it is a pure
loss. Tolerance remains a local stopping test, not an error bound.

P10 — the fan-out is not starving the thread pool. Instrumented and refuted at the first
milestone, so the shared row queue was never built. Peak threads parked waiting for a permit is 3
against the 40 predicted, and the pool's thread count moves 11 → 12 across a whole frequency point.
The control settles it: force the pool large first and the predicted picture appears in full — 34
threads parked at once — and the point still takes the same time, with utilisation going up. A
parked thread burns no CPU and holds no permit. Two facts make it work, neither visible from reading
the code: the parallel cap never exceeds the processor count, which is also the pool's minimum worker
count, so no injected thread is needed to reach it; and unmet parallel demand queues rather than
blocks. The four counters that answered it are kept.

P11 — the reference impedance no longer needs a dense m×m solve. De-embedding references every
published s-parameter to a characteristic impedance derived from a per-unit-length capacitance, which
differences two calibration standards' static capacitances. That solve was always a dense complex LU
whatever the run's settings said — the one step the accelerator did not reach — so a wide port's
standard could refuse a run whose DUT would have solved comfortably. The static operator is exactly
the scalar block AIM already projects, so it now runs with the same projection, near-set rule, grid
FFT and preconditioned GMRES. Nothing is a second implementation: the shared machinery was moved
rather than copied, and the near set's exact entries are the dense path's own arithmetic carved out
whole, so a non-accelerated run is bit-identical. Measured against the dense solve on the differenced
quantity itself: 1.04e-7 on the FR-4 hero's standards, 2.02e-9 on the GaAs hero's. The near radius
reads in basis supports here rather than cell spans, and that is the whole accuracy story — the
natural cell-span reading halves it and would have left GaAs outside the gate by 12%. A wide-port
de-embedded run that refused at setup now completes 3 points in 107 s, the largest standard's static
solve costing 9.7 s and 222 MB against 683 MB dense.

P12 — a board with a ground via is no longer capped at the dense ceiling. The accelerator's
multi-level and via refusal is lifted. Its stated reason — a projection with a derivative in it — was
true of projecting the vertical bases and was never an argument for projecting them. Every
horizontal rooftop is already ordered before every z-directed one, so the matrix is
[Z_hh Z_hz; Z_zh Z_zz] with N_z in the tens: the horizontal prefix is projected alone, with one grid
kernel table per level pairing over one shared auxiliary grid, and the vertical unknowns are carried
as a dense border. The preconditioner is the near-field LU with that border folded in exactly, by
block elimination on an N_z×N_z Schur complement.

The gate is exactness rather than a tolerance: widen the near radius until every pair is in the near
set, and the bordered operator has to reproduce the direct fill's matrix to round-off. It does —
1.1e-15 entry-wise and 4e-11 in the solved current, on an interior via and on a ground attachment
alike. De-embedded S through the whole sweep driver agrees to 1.6e-14. What survives at the shipped
radius is the projection's own error, unchanged, at 3.97e-7 on the ground-via fixture.

Two things recorded rather than acted on. Accuracy figures are not transferable between fixtures
a single-level control with no via in it spans 55x from the mesh alone — so every figure is kept with
its mesh and its control beside it. And the accelerated ceiling is deliberately not widened to via
meshes: the ladder that would justify it is healthy past 15,000 unknowns, but a ceiling stated in N
alone would promise something a via field does not deliver.

Still on the table, measured and not built: the trailing update in the factorisation is scalar
managed C#, and SIMD complex multiply-add with destination-column register blocking is an obvious
further 2–4x.