English / 日本語

Gregory J. Durnan, PhD, CEng, FIMarEST

MININEC v2 (QB45) — The Reference Source
San Diego, CA, USA · Contact · durnan.org

MININEC v2 (QB45) — Publishing the Clean Copy

Aside: this is meant to be the last piece of the MININEC saga on this site, at least for a while. MiniNEC x64 is the feature-rich daily driver, jMiniNEC is the modern Java port with the math written up in detail — and this page is neither of those. It's the plain, un-improved QB45 source those two both trace back to, published because I think it's worth more sitting out in the open than sitting in a folder.

Every antenna code I've touched over the last few decades — the QB64 rewrite that became MiniNEC x64, and now the Java port I've written up as jMiniNEC — descends from one piece of BASIC I wrote somewhere around 2003 to 2007: a procedural rewrite of an even older QB45 program of mine from the early 1990s. That older, non-procedural version is what jMiniNEC's engine now calls "v1," and it's retired. This one — I'm calling it MININEC v2 — is the one I actually kept building on, and it's the version that matters if anyone besides me ever wants to port MININEC from scratch again.

The only thing that's changed in the file below is that the GOTOs and line-number jumps are gone, replaced with IF/END IF, DO/LOOP and EXIT FOR — a mechanical cleanup pass, not a rewrite. Every branch and every loop fires under exactly the same conditions, the same number of times, as the original. No computation changed. That matters, because it means what you're looking at is still, faithfully, NOSC TD 516's algorithm — not my opinion of how it should have been written.

Why This One, and Not MiniNEC x64

MiniNEC x64 is the version I actually use, and it's grown a lot in the QB64 port: CSV export, extra diagnostics, quality-of-life input handling — none of which was ever part of MININEC 2 or 3's actual specification. That's exactly why it's the wrong thing to hand someone who wants to understand or re-implement MININEC, as opposed to my particular tool built on MININEC. Every extra feature is one more thing a future porter has to mentally subtract before they can see the algorithm underneath.

This file has nothing to subtract. It's organized into eight SUBs and two kernel FUNCTIONs, does exactly what NOSC TD 516 describes, and stops there. If you're writing a Python, Rust, or C port of MININEC from first principles — or just trying to understand what MININEC is, as opposed to any one person's version of it — this is a shorter path to that understanding than either of my other two versions.

DECLARE SUB Intro ()
DECLARE SUB EnterData ()
DECLARE SUB WireConnect ()
DECLARE SUB MainEngine ()
DECLARE SUB PrintCurrentData ()
DECLARE SUB Redo ()
DECLARE SUB FUNCTION1 ()
DECLARE SUB FUNCTION2 ()

That's the whole program's shape, right up front. EnterData and WireConnect build the geometry; MainEngine fills and solves the impedance matrix; PrintCurrentData reports currents, impedance and far-field pattern; FUNCTION1 and FUNCTION2 are the kernel integration, called from deep inside MainEngine's matrix fill. Nothing is hidden behind a GUI, a config file, or an output format — it's eight procedures and a couple of thousand lines of arithmetic, exactly the size a real electromagnetics code needs to be and not a line more.

The Wilton–Pocklington Split, and Why 1980 Needed It

Pocklington worked out the exact integral equation for current on a thin conducting wire back in 1897. It's not a hard equation to write down. It's a very hard one to evaluate cheaply, because the kernel — the free-space Green's function, averaged around the wire's circumference — has a logarithmic singularity exactly where a method-of-moments code needs to sample it most: the self term, where the observation point sits on the source segment itself. Evaluate that honestly, with a full numerical treatment of the near-singular kernel across its whole domain, and you need careful adaptive quadrature or a proper numerical Green's-function treatment — exactly the kind of thing NEC does, running on the minicomputers and mainframes available to Lawrence Livermore in the 1970s.

MININEC's authors didn't have that hardware to target. The whole point of the 1980 NOSC effort — as the original paper puts it, investigating "techniques that may result in a reduced version of an antenna modeling code" — was to get a usable method-of-moments solver running in BASIC, in the field, on a microcomputer with a few tens of kilobytes of RAM and no floating-point coprocessor. A full adaptive-quadrature treatment of a hypersingular kernel, evaluated fresh for every one of an N×N matrix's entries, was simply not on the table.

The technique that made it possible — what I've taken to calling the Wilton–Pocklington split, since the original MININEC paper credits it directly to Dr. Donald R. Wilton, then of the University of Mississippi, as a co-author alongside Julian, Logan and Rockway — is the trick underneath FUNCTION1 below. Instead of integrating the singular kernel directly, you subtract the singularity out analytically (a bare logarithm, integrable in closed form), replace the near-field remainder with a well-known rational-plus-log approximation to an elliptic integral (ten fixed constants, looked up once, not computed from scratch), and hand only the smooth leftover to numerical quadrature. Three pieces, one singular and closed-form, two smooth and cheap — instead of one nasty piece that isn't safe to sample near its own singularity at all.

That's the whole reason this program could exist on 1980s hardware. NEC's full/exact thin-wire kernel is more accurate, especially for thick wires, and it needed the machine to match. MININEC's reduced kernel trades a sliver of that accuracy — the elliptic-integral approximation is good, not exact — for evaluating each matrix entry in a few dozen floating-point operations instead of an adaptive numerical integration. On a machine with no FPU, that's not a minor optimization; it's the difference between a program that runs overnight and one that never finishes.

Here's that split exactly as it survives in this file — FUNCTION1, called once per matrix entry from FUNCTION2's kernel integration:

SUB FUNCTION1
    Rem Elliptical integral?
    X3 = T * D(P4)
    Y3 = T * F(P4)
    Z3 = F(P4) * T * G(P4)
    Den = D0 - 2 * F3 * (X3 * X2 + Y3 * Y2 + Z3 * Z2) + X3 * X3 + Y3 * Y3 + Z3 * Z3
    Den = SQR(Den)
    IF I6 <> H0 THEN
        D3 = Den * Den - A2
        B = D3 / (D3 + 4 * A2)
        V1 = W(1): Rem A0
        V2 = W(6): Rem B0
        B1 = 1
        B2 = -36
        FOR L2 = 2 TO 5
            B2 = B2 / (L2 - 1)
            IF B < 10 ^ (B2) THEN EXIT FOR
            B1 = B1 * B
            V1 = V1 + W(L2) * B1
            V2 = V2 + W(L2 + 5) * B1
        NEXT L2
        V = (V1 - V2 * LOG(B)) * SQR(1 - B)
        D3 = SQR(D3) / 8 / A(P4)
        T3 = T3 + (LOG(D3) + V) / P / A(P4) - 1 / Den
    END IF
    B1 = Den * Wavelength
    T3 = T3 + COS(B1) / Den
    T4 = T4 - SIN(B1) / Den
END SUB

V is the elliptic-integral approximation, built from the ten W() constants read in as DATA near the top of the file — the "well-behaved near-field remainder" term. The LOG(D3) alongside it is the closed-form singular piece, and the final two lines (the COS/SIN of Den) are the third, smooth far-field piece. All three pieces of the split, sitting in eleven lines, doing in a handful of flops what a rigorous exact-kernel evaluation would need real numerical integration to achieve. Note that this is published exactly as it stood in the original file, transcription quirks included — the DATA constant on the last bx line is very slightly off from the reference table (a digit-transposition, not a typo of mine), and I've written up the fix and its (negligible) effect on jMiniNEC's own page, if you're curious.

A Programmer's Note to Whoever Ports This Next

When I did the GOTO cleanup pass on this file, I found two places where the control flow looked odd enough that I stopped to leave myself — or whoever came after — a note, rather than quietly "fixing" something I didn't fully understand yet mid-refactor. I left both exactly as they were, and wrote this at the bottom of the file:

Note on two pre-existing quirks (left untouched):
1) H0 is used both as a plain "0" constant early on and is later set to 1
   inside MainEngine, which changes the lower bound of several "FOR I = H0
   TO N" loops in the print/pattern routines. That's the original code's
   doing, not something introduced by this GOTO cleanup.
2) In MainEngine's pivot-search loop, the pivot-scan block is still guarded
   by "IF P = 0 THEN ..." (originally "IF P THEN GOTO ..."). Because P
   holds Pi (set near the top of the program) it is never zero, so that
   block never runs - exactly as in the original GOTO version. Renaming P
   there was presumably meant to be a separate flag; left exactly as-is.

That note sat in this file for years before I actually chased either quirk down. Both turned out to be real bugs, not stylistic oddities — and both are written up in detail on jMiniNEC's own page. Quirk #1 is the reason H0 (renamed loopBase in the Java port) has to be explicitly reset to zero at the top of every call to MainEngine; left alone, it leaks a stale value from a prior call into the matrix zero-fill and corrupts results for any geometry that isn't perfectly centered on the origin. Quirk #2 is worse: it means the "complete pivoting" pivot search this code was clearly meant to have — scanning the whole remaining submatrix for the largest entry before every elimination step, not just the current column — never actually ran, in any BASIC version of this program, ever. jMiniNEC restores it, ported over from a completely independent Java engine of mine that happened to implement the search correctly.

I think that's a nice thing to be able to point at: the comment above isn't hindsight dressed up as foresight. It's a genuine "something's not right here, but I'm not stopping to fix it during a mechanical pass" note, written in real time, years before either problem was actually understood. Whoever ports this file next inherits both quirks exactly as documented — which is, I'd argue, exactly how a reference source should hand them off: named, dated, and left alone until someone's ready to actually fix them.

What's Deliberately Not Here

There's no screenshot section on this page, and no fresh DOSBox run to go with it. I could dig out an emulator and re-run this exact file, but between MiniNEC x64's screenshots and jMiniNEC's validated cross-checks against a published worked example and an independent Python reimplementation, this exact algorithm's correctness (bar the two documented quirks above) is already established elsewhere on this site. This page isn't here to prove MININEC v2 works — it's here so the source itself has a permanent, citable home, separate from any downstream tool built on top of it.

Wrapping It Up

So: three MININEC codebases now live on this site, and each earns its place for a different reason. MiniNEC x64 is the tool. jMiniNEC is the modern, documented, bug-fixed engine. And this — MININEC v2, unglamorous QB45, GOTOs removed and nothing else touched — is the reference. If you ever want to know what NOSC TD 516's algorithm actually looks like in code, with no features added and no bugs silently patched over, this is the file to start from.

#GregoryJDurnan #DurnanOrg #GregDurnanRF #RFEngineering #MININEC #MethodOfMoments #AntennaTheory #NumericalElectromagnetics #AntennaDesign #ElectromagneticsResearch #NavalHistory #QuickBASIC