Chapter 1 — Introduction and Scope
This manual covers the NEC antenna modeling system: a Python toolkit that generates NEC2/NEC4 input decks, runs the engine, parses output, and displays radiation patterns, impedance, and gain. The system supports dipoles, verticals, Yagis, log-periodics, quad loops, phased arrays, and ground-mounted verticals with buried radials. An optional REST API exposes the toolkit to web front-ends or remote callers.
Engines supported: nec2c (Linux, free), NEC4 (licensed), 4nec2 (Windows GUI via Wine), necpp (open-source alternative).
Chapter 2 — Theory of Operation
2-1 NEC Card Format Summary
NEC input is a plain-text deck of two-character card mnemonics, one per line:
| Card | Purpose | Key fields |
|---|---|---|
| CM | Comment | Free text |
| CE | End comments | (no fields) |
| GW | Wire segment | tag, segs, x1 y1 z1, x2 y2 z2, radius |
| GS | Scale geometry | scale factor (e.g., 0.3048 = feet to meters) |
| GE | End geometry | 0=free space, 1=ground present |
| GN | Ground parameters | type, εr, σ (good earth: 13, 0.005) |
| EX | Excitation (source) | tag, segment, V_real, V_imag |
| FR | Frequency | start MHz, step MHz, N steps |
| RP | Radiation pattern | theta/phi start, step, N points, mode |
| EN | End of input | (last card) |
2-2 Segment Count Guidelines
NEC accuracy depends on adequate segmentation. Rule: 10–20 segments per wavelength, odd count preferred for symmetric elements with a center source. Minimum 3 segments per wire. The system function:
def recommended_segments(length_m, freq_mhz, min_segs=3):
wavelength = 299.792458e6 / (freq_mhz * 1e6)
segs = max(min_segs, int(length_m / wavelength * 10))
return segs if segs % 2 == 1 else segs + 1
Chapter 3 — Installation and Dependencies
3-1 Python Environment
Python 3.10 or later is required. Install dependencies:
pip install -r requirements.txt
Core packages: numpy, scipy, pandas, matplotlib, fastapi, uvicorn, pydantic, httpx, tqdm, requests. Optional: scikit-rf (Smith chart), plotly (interactive 3D patterns).
3-2 NEC2 Engine Installation (Debian/Ubuntu)
sudo apt install nec2c
Verify: nec2c --version should return without error. The runner
(nec_runner.py) auto-detects available engines in the order: nec2c, nec4,
necpp, 4nec2.
Chapter 4 — Generating NEC Input Files
4-1 Dipole Example
from nec_generator import Dipole, NECModel
ant = Dipole(freq_mhz=14.25, height_m=10.0)
model = ant.to_nec_model()
model.write("dipole_20m.nec")
This generates a half-wave dipole resonant at 14.25 MHz, 10 m above real
ground (εr=13, σ=0.005 S/m). The GW card for each half-element
uses recommended_segments() to set segment count automatically.
4-2 Vertical with Radials
from nec_generator import VerticalWithRadials
ant = VerticalWithRadials(freq_mhz=7.1, n_radials=32, radial_length_m=10.0,
height_m=0.1)
model = ant.to_nec_model()
model.write("vertical_40m.nec")
4-3 Frequency Sweep
from freq_sweep import FrequencySweep sweep = FrequencySweep(model, start_mhz=1.8, stop_mhz=30.0, n_steps=200) results = sweep.run() sweep.plot_impedance(results) sweep.plot_swr(results, z0=50.0)
Chapter 5 — Running and Parsing
5-1 Running the Engine
from nec_runner import NECRunner, NECEngine
runner = NECRunner(engine=NECEngine.NEC2)
result = runner.run(model)
if not result.success:
print(result.stderr)
The runner writes a temporary .nec file, invokes nec2c as a subprocess, captures stdout/stderr, and returns a NECResult object. Timeout default: 60 seconds (adjustable for large sweeps).
5-2 Parsing Output
from nec_parser import NECOutputParser
parser = NECOutputParser()
points = parser.parse(result.output_file)
# points: list of {freq_mhz, gain_dbi, z_real, z_imag, swr, theta, phi}
Chapter 6 — Optimization
Three optimizers are available:
- genetic_optimizer.py: Genetic algorithm; best for multi-parameter problems with discontinuous cost functions. Suitable for Yagi element lengths + spacings.
- pso_optimizer.py: Particle swarm; faster convergence for smooth continuous cost surfaces. Suitable for matching network component values.
- batch_optimizer.py / batch_multiband.py: Evaluates a parameter grid; useful for initial design-space exploration before running a stochastic optimizer.
Cost function examples: maximize F/B ratio, minimize SWR at target frequency, maximize gain over ground at 20° elevation.
Chapter 7 — Verification and Acceptance
- Run the included test case (half-wave dipole in free space at 14.25 MHz). Expected: feedpoint impedance 73+j42.5Ω, gain 2.15 dBi.
- Verify against ARRL Antenna Book reference tables for dipole impedance vs. height above ground. Deviations >5% indicate a segmentation or ground parameter error.
- For a calibration-quality check: compare modeled resonant frequency against NanoVNA measurement of a physical antenna. Agreement within 2% is typical for wire antennas over flat ground.
- Log: NEC version, Python version, test antenna type, modeled vs. reference gain, modeled vs. measured resonant frequency.
Appendix A — Common NEC Errors
| Error | Cause | Fix |
|---|---|---|
| SEGMENT FAILURE | Wire too short for segment count | Reduce min_segs or increase length |
| Impedance = 0+j0 | Source on wrong tag/segment | Verify EX card tag matches GW tag |
| Gain >30 dBi | Wire below ground (Z < 0) | Ensure all Z coordinates ≥ 0 or use buried-radial GN type |
| NaN impedance | Singular matrix (parallel wires touching) | Increase wire separation >2 × radius |
Appendix B — Dipole Resonant Length Formula
L_half (meters) = (142.5 / f_MHz) × k
k = velocity factor:
Bare wire in free space: k = 0.975
Wire near ground (h < λ/4): k = 0.94–0.97
Insulated wire: k = 0.93–0.97 depending on insulation