Metadata-Version: 2.4
Name: momwire
Version: 0.2.3
Summary: A pure-Python method-of-moments antenna simulator with optional C++ accelerators
Author-email: "Steven M. Burns" <smburns47@gmail.com>
Project-URL: Homepage, https://github.com/stevenmburns/momwire
Project-URL: Issues, https://github.com/stevenmburns/momwire/issues
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Requires-Dist: scipy>=1.7
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: matplotlib; extra == "test"
Requires-Dist: scikit-rf; extra == "test"
Provides-Extra: validation
Requires-Dist: pymininec>=1.2; extra == "validation"
Dynamic: license-file

# momwire

A pure-Python method-of-moments antenna simulator with optional C++ accelerators (pybind11).

Extracted from [antenna_designer](https://github.com/stevenmburns/antenna_designer).

## Install

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .
```

**macOS:** the C++ accelerator uses Homebrew's OpenMP runtime, so `brew install
libomp` is required — both to compile from source and to run the prebuilt
Apple-Silicon wheel. The wheel deliberately does **not** bundle `libomp` (it links
Homebrew's by absolute path) so that it shares a single OpenMP runtime with
pynec-accel; two private copies in one process abort with `OMP: Error #15` (or
deadlock). Without `libomp` installed, the accelerator can't load and momwire
warns and falls back to the slower pure-Python path. On Linux the system
`libgomp` covers this, so no extra step is needed.

## Test

```bash
pip install -e ".[test]"   # core + test deps (pytest, matplotlib, scikit-rf)
pytest tests/
```

The cross-validation against NEC2 (`tests/test_pynec_backend.py`) additionally
needs PyNEC — a test-only dependency installed separately from a wheel (see
below). Those tests skip cleanly when it isn't present; everything else runs
without it.

## Optional: PyNEC backend (test-only)

momwire can be cross-validated against NEC2 via [PyNEC](https://github.com/tmolteno/python-necpp) (the `tests/test_pynec_backend.py` suite); NEC2 also delivers ~5–10× faster single-frequency solves. PyNEC is a **test-flow-only** dependency — momwire's own solver never imports it.

### Install the PyNEC wheel

Install PyNEC from the [python-necpp fork](https://github.com/stevenmburns/python-necpp)'s release. The wheels are self-contained — OpenBLAS is vendored (via scipy-openblas32), so no system BLAS, SWIG, or build toolchain is needed — and cover Linux + Windows on CPython 3.10–3.14:

```bash
pip install PyNEC --no-index \
    --find-links https://github.com/stevenmburns/python-necpp/releases/expanded_assets/v1.7.4-accel.1
```

`--no-index` ensures pip takes the fork's wheel rather than upstream PyNEC on PyPI (same version, but its builds are broken on current Python and it lacks the OpenBLAS/OpenMP work). After install, `from PyNEC import nec_context` works and the cross-validation tests run; without it they're skipped (momwire itself needs no PyNEC).

### Runtime thread pinning

The wheel links OpenBLAS and parallelises the NEC2 matrix fill with OpenMP. Pick thread counts up front:

```bash
export OMP_NUM_THREADS=$(nproc --all)   # PyNEC matrix fill
export OPENBLAS_NUM_THREADS=1           # muzzle numpy/scipy's idle pool
```

Pinning `OPENBLAS_NUM_THREADS=1` stops numpy/scipy from spinning up their own OpenBLAS thread pool that contends with PyNEC's threads on the same cores. On a 100-director Yagi (2142 segs) this is worth ~8% wall time at NP=4.
