Skip to content

The solver & accuracy

antennaknobs solves antennas with momwire, an in-house method-of-moments (MoM) engine for wire structures, with an optional NEC-2 backend for cross-checking.

momwire offers two current-expansion basis families in one engine — uncommon among free tools, where basis quality is usually a paid feature — plus a third TESTING rule (razor, below) on the tent expansion the B-spline family’s own degree-1 case already uses:

  • B-spline — a degree-1/2 Galerkin basis, the default (degree 2),
  • sinusoidal — a NEC-2-style three-term basis, point-matched like NEC-2; useful as a cross-validator (it tracks PyNEC closely because it shares both the basis and the point matching),
  • sinusoidal-galerkin — the same three-term basis with Galerkin (integrated) test rows: exactly reciprocal Z by construction, markedly faster reactance convergence on junction-heavy geometry, and junction-node ports (free space; PEC since momwire#191). It exists so basis effects and testing effects can be told apart — the full story is momwire’s chapter 15. It also carries the one user-facing feed-model choice: Converged (the default zero-width gap — converges to the B-spline answer, exactly reciprocal Y) or NEC-compatible (a segment-wide gap — NEC’s own source, including the reactance drift as the mesh refines). It is the “feed model” control in the Sin-Galerkin slot’s gear menu (the B-spline family carries the same control, defaulting to Converged), and the workbench is the only place it is offered: the CLI and both momwire portals carry one sinusoidal-galerkin name, and it is the converged one (momwire#654). Reach for NEC-compatible when you want NEC’s source specifically — but for cross-checking against NEC, prefer momwire:sinusoidal, which is NEC-2’s whole formulation rather than just its feed. Converged is what near-open high-Q feeds want, and the workbench recommends it on the designs that need it — momwire#213 measured it removing 992×/374× of the cross-basis disagreement on lazy_h/vbeam, without reducing the mesh those designs genuinely need. The plain sinusoidal solver offers no such choice: a zero-width gap cannot be expressed under point matching (momwire#212).

Plus two accelerated solvers for large problems:

  • hmatrix — ACA / hierarchical-matrix, sub-quadratic on large single-wire structures,
  • arrayblock — element-aware block-low-rank, near-linear on arrays of identical / few-shape elements.

And one formulation twin:

  • razor-2p — a tent basis tested by NEC-5’s own razor-blade rule rather than point matching or Galerkin testing — see below.

razor-2p is RazorSolver’s only --basis roster name (razor-nec5 is its deprecated spelling) — the way bspline-d1 is BSplineSolver on a bound degree. Unknowns are the same interior-knot tent basis bspline-d1 uses, but the row equations come straight from the public NEC-5 manual’s mixed-potential method — the E-field boundary condition enforced on path integrals between element centroids (“razor-blade” testing), not point matching and not Galerkin testing. That makes it a formulation twin: a way to reproduce NEC-5’s own convergence behaviour, including its well-known O(1/N) reactance walk, without the licensed NEC-5 binary. razor-2p binds NEC-5’s identified quadrature rule for the one path integral the manual leaves numerically open (nec5_quadrature); the class’s other quadrature — the default, converged Gauss-Legendre lane — is not a --basis name. It left the CLI roster in momwire#753 (2026-09-02, “a roster entry is a menu item that must be worth ordering”, momwire#654) and is reached only by constructing RazorSolver(...) directly; the walk it shows is the testing rule’s own discretization error rather than a quadrature shortfall.

razor-2p is the orderable lane. It binds the two-point rule momwire#316 identified for the testing-path integral, where the unrostered Gauss-Legendre lane spends roughly 10× the wall time on quadrature nodes for an advantage that has evaporated by N≈192.

The name describes that rule rather than another vendor’s product, and the change is deliberate (momwire#785). Two things are true at once here, and it is worth keeping them apart:

  • At a working mesh it does track NEC-5, because it reproduces NEC-5’s testing formulation and therefore its discretization behaviour — measured against the licensed binary at 0.003–0.007 Ω across the drop-in’s ladder (momwire#603). That is the whole reason the lane exists.
  • It is not converging somewhere different. The limit it walks toward is the same one bspline-d2 reaches, and it gets there more slowly — roughly 16× the mesh for the same self-convergence on the ByDipole1 ladder. So the old name’s implication, that this lane is accurate because it is NEC-5’s, is the part that does not survive: it reproduces a formulation, not a privileged answer.

If reproducing that formulation is not your question, bspline-d2 is the better answer. razor-nec5 still works as a deprecated alias.

The Gauss-Legendre lane came first — it was built as the twin, before momwire#316’s residue study identified the two-point quadrature rule that made the match near-exact — and what it is now is an instrument, not a roster choice: it answers “is this coarse-mesh disagreement the testing rule or NEC-5’s quadrature shortcut?”, and nothing a model needs, which is why momwire#753 (2026-09-02) dropped it from the CLI --basis roster — reach it by constructing RazorSolver(...) directly. It costs about 20× the time for that (free space at N=1600, one box, momwire 0.44.0: 20.2 s against razor-2p’s 0.97 s) and, since momwire#742 gave both lanes the same fill, essentially the same memory (520 MB against 479 MB). At a fine mesh the two agree to 0.001 Ω, so the quadrature question it exists to answer is a coarse-mesh one. The measured two-lane table lives on momwire’s engine-choice page.

Refusal boundary. RazorSolver is deliberately scoped to what its formulation-twin role needs, and refuses the rest at construction with a named message rather than a silent wrong answer:

  • No extended kernel — it is reduced-kernel only; NEC-5’s formulation tests the expansion on the wire axis, and extending it would answer a different question than the one this solver exists to check.
  • No junction ports, no node gaps — a junction basis is already a through-current unknown with no unambiguous branch to drive a source or land a series gap on. Ordinary K=2 junctions (a wire split, or two wires meeting end to end) are unaffected — those resolve to the same tent basis automatically, from the geometry, with nothing to configure.
  • No ground contact over a finite ground (issue #282) — over both finite-ground flavours, an end resting in the plane would need its grounded-end tent’s image wing to carry spurious contact charge, since the fold hard-codes image coefficient 1 (PEC). Ground contact over a PEC plane (ground = pec) is fully served — including a base-fed monopole.
  • No K≥3 port, on a feed, load, or junction — the branch pair a source or load would drive is ambiguous at three or more coincident ends.

Free space and all three grounds (PEC, reflection-coefficient, Sommerfeld) are otherwise served, along with per-wire radii and both distributed and lumped wire loading. Full detail: momwire’s docs/razor-solver.md.

Every basis above solves a thin-wire integral equation: the current is treated as a filament on the wire axis, and the field is matched on the surface. That approximation is excellent while each segment is long compared to the wire’s radius, and it degrades as the ratio Δ/a falls — the same axis that produces convergence failure class 4. NEC’s answer is the extended thin-wire kernel — the EK card — which replaces the on-axis Green’s function with an O(a²) expansion integrated over the tube. The correction is a fraction of a percent at Δ/a > 10 and several percent below Δ/a ≈ 3, and it moves the answer toward NEC’s: on a Δ/a = 2.27 dipole, nec2c reads 123.7+28.6 j reduced and 113.6+29.0 j extended — an 8 % step — and momwire’s sinusoidal basis tracks both sides of the card to 0.5 % and 3.1 % respectively.

momwire serves it on five of the six solver families — sinusoidal, sinusoidal-galerkin, bspline, hmatrix and arrayblock — in free space and over every ground model, at about 1.0–1.3× the reduced-kernel solve. The Galerkin family joined with momwire 0.27.0: momwire#246 built its extended kernel, momwire#287 measured the Sommerfeld ground in, and momwire#299 made non-collinear geometry (vees, bent elements, junctions, radius steps) sound. Two things refuse it, loudly rather than silently:

  • RazorSolver (razor-2p, and its unrostered Gauss-Legendre quadrature) — reduced-kernel only, by design: NEC-5’s formulation is the comparison target, and it tests the expansion on the wire axis. See Razor above.
  • singular enrichment, on the bases that otherwise serve EK — the enrichment degrees of freedom carry their own quadrature and bypass the moment kernels the extended kernel corrects (momwire#249).

Three ways to ask for it: the extended kernel (EK) check in a solver slot’s gear menu (per slot, so you can put the same basis and mesh in two slots and read the difference); --extended-kernel on the CLI; or an EK card in an imported deck, which is honoured on its own. Reach for it on thick elements — tubing, cages, fat-wire imported decks — and when cross-checking a NEC model whose deck carries the card. On ordinary thin wire, leave it off: it changes the answer by less than the mesh does.

The selection matrix, the runtime findings (the ground-cost ladder, where the accelerated engines win and where they lose), the memory envelopes, and the fixed-accuracy census verdict behind the bspline d=2 default all live on momwire’s own engine-choice page — one home per fact, keyed to the same engine names the workbench’s --basis control takes:

Two app-side notes the engine page doesn’t carry:

  • PyNEC’s role here is the independent NEC-2 reference riding alongside: a great fast cross-check on small/medium single structures, but not universally fast — it loses the log-periodic on every ground model (free space: 14 s vs ArrayBlock’s 1.2 s), so “use PyNEC as the reference” holds for small designs, not arrays.
  • Junction-port (PortAtEnd) designs accept sinusoidal-galerkin as the second opinion (historically B-spline-only; the sin-Galerkin junction ports run in free space and over a PEC ground — momwire#191 — with finite grounds still refused). bspline remains the default.

Method-of-moments discretizes each wire into segments, and the solve builds one basis function per segment. The segments / wire (N) control in the workbench sets the design’s mesh density: every wire gets N segments per quarter-wavelength (at the design frequency), so a long radiator gets proportionally more segments than a short stub — and N means the same physical segment length on every design (N=15 ≈ λ/60). The total basis-function count — the sum across every wire — is the dimension of the impedance matrix the solver fills and factors.

That matrix is what sets both accuracy and cost:

  • Too few segments and the current distribution is under-resolved — the feed-point impedance hasn’t converged and your SWR/resonance readings are off, sometimes by a lot near a sharp feature.
  • More segments refine the answer, but the dense solvers form an N×N matrix: memory grows as and fill/factor cost as N²–N³. Past the point where the impedance stops moving, the extra segments only cost time.

Finding “enough” — the convergence sweep

Section titled “Finding “enough” — the convergence sweep”

The workbench’s convergence sweep re-solves the current antenna across a range of N and plots the resulting feed-point impedance R + jX against N. Read it like any convergence study: the curve drops steeply at small N, then flattens (“the knee”). The smallest N past the knee is your sweet spot — converged, but no slower than it needs to be. If the curve never settles, the geometry may have a feature (a tight bend, a very short feed gap) that needs finer local segmentation, or a different basis.

A quick rule of thumb: the dense bases want enough segments per half-wavelength to resolve the sinusoidal current — a couple of dozen across a half-wave element is typical. The convergence sweep turns that rule into an answer you can see for your antenna.

For the full method — convergence ladders, cross-basis validation (a flat curve can be flat at the wrong value), and the four distinct reasons a curve refuses to settle — see the advanced guide: How many segments?

Because the dense matrix grows as N², a runaway segment count (or a big array) can allocate hundreds of megabytes and stall a solve. The hosted instance therefore caps the total segment count and rejects oversized solves with a clear message rather than melting the shared box. The cap is engine-aware: the compressed arrayblock / hmatrix engines skip the dense matrix (ACA / H-matrix), so they’re allowed a much higher count — which is exactly why they exist for large arrays. The cap is off by default and enforced only on the shared hosted instance — a local pip install is uncapped; the toggle and the limits are env-configurable (see docs/deploy.md).

  • A NEC-2 reference engine (pynec-accel) runs alongside momwire — and a licensed NEC-5 can make it a three-way triangle — so any design can be solved two ways and compared — a built-in sanity check most tools lack.
  • The repo carries the benchmark above plus per-design solver comparisons.

In the spirit of not overselling: momwire wires are PEC by default; designs that declare a wire material (the WIRES catalog — see the wire-gauge example) get skin-effect conductor loss as a distributed series impedance in the solve itself (momwire ≥ 0.10.0, validated against NEC’s native LD 5 wire-loss card to ~0.5 %) plus the insulated-wire velocity factor (mirrored on the NEC engine as an LD 2 distributed-inductance card, agreeing on the resonance shift to ~1 %). Every momwire solver carries the loading (momwire ≥ 0.11.0): the B-spline family as a Galerkin overlap, the sinusoidal solver through NEC’s impedance boundary condition at its match points — so matched-basis cross-engine comparisons keep the full wire physics on both sides. Finite grounds are solved with a true Sommerfeld/Norton model on every momwire solver (momwire ≥ 0.8.0: NEC’s exact-image-plus-remainder decomposition; the accelerators carry the smooth remainder as one global low-rank term on their fast paths). Validation against an independent NEC-2 implementation across 0.02–0.5λ heights — including the very-low region where reflection-coefficient models are tens of ohms off — lands within ~2.4 Ω on the B-spline basis and ~0.1 Ω on the sinusoidal basis (which shares NEC’s basis, making it the sharpest check of the model). The faster reflection-coefficient model remains the default and the NEC path offers its own Sommerfeld–Norton — so real-ground results cross-check across two independent engines at every height.

Grazing heights (momwire ≥ 0.39.0): a wire very close to a finite ground — an elevated radial system, a ground screen, a low horizontal run — used to get worse with mesh refinement rather than better, which is the signature of a numerical defect rather than a modelling limit. It was two of them, in two different places, and both are fixed. Against captured reference printouts of the same 1.832 MHz radial system standing 1.8 cm (1.09e-4 λ) above earth, the served impedance moved from 275 Ω and 188 Ω away to 12.3 Ω and 5.2 Ω, the pattern peak now lands in exactly the captured direction with the level inside 0.3 dB, and — the tell that matters — two different meshes of that one antenna now agree with each other to 0.6 Ω where they previously disagreed by 100 Ω. Both the default bspline lane and the NEC-5 formulation twin are fixed, so the cross-check at grazing height is a genuine second opinion rather than two copies of one bug.

Two things to know about the edges. Standing a wire in a finite ground — ground contact, as distinct from grazing above it — is served on the bspline lane only, at about 7 % on impedance; the formulation twin refuses it by name rather than guess (momwire#624). And the accelerated solvers (HMatrixSolver, ArrayBlockSolver) reach the ground image by their own path and have not received the grazing fix (momwire#634) — they are accurate at ordinary heights and should not be selected for a grazing model; the default solver is unaffected.

Below the interface (momwire ≥ 0.38.0): wires strictly below the ground plane — buried radials and screens, buried fed elements, elevated feeds over buried counterpoises — serve impedance, currents and charges on the bspline lane over the Sommerfeld ground. Since momwire 0.41.0 a wire may also cross the interface at a declared junction: a buried wire whose end stands in the ground plane, junction-joined there to one above-ground wire (one above × N below, so a connected radial screen spells as N rises meeting the monopole at the surface, and a buried hub joining the radials at depth is allowed). The design catalog now authors buried geometry directly — a buried-radial vertical, an elevated counterpoise over buried radials, and a buried dipole — with their own in-medium meshing (medium-aware auto-meshing in the builder at large remains antennaknobs#983). What still refuses does so by name: a wire crossing the interface mid-span, a ground-contact wire combined with buried wires (the rise respelling is the served path), and near fields or patterns of buried decks. Validation below ground rests on exact identities (the lossless-limit collapse onto free space, the deep-burial limit onto the infinite-medium solve) plus ladder-limit agreement at the half-percent class on the radial and counterpoise classes, with the with/without-radials coupling differential matching to about a milliohm; and, where a licensed NEC-5 is asked with its documented below-ground card, the two engines agree to a few percent on wholly buried fed elements and on bonded-base verticals over buried radials (the validation page carries the numbers). The one buried spelling NEC-5 has no documented answer for is a conductor that stops on the interface above buried wires, which momwire refuses by name on its own physics. One cost note: a deck’s first buried solve builds its below-interface Sommerfeld tables (C++-accelerated since momwire 0.39.0, and cached on disk thereafter); crossing-junction pairs use a direct designed evaluation whose C++ twin landed in momwire 0.41.0, so a crossing-class solve runs in seconds warm.