Device-agnostic Python library and GUI for 3D magnetic-sensor analysis of
tokamak MHD instabilities — quasi-stationary (locked) modes and rapidly-rotating
modes. See docs/VISION.md for context.
The GUI ships inside the package — no Node or build toolchain needed.
uvx magnetics # zero-install: uv provisions Python + runs it
# or
pip install magnetics # into an existing environment
magnetics # start the app, open it in your browsermagnetics starts the service and opens the GUI in your browser. Useful flags:
magnetics --port 8000 # fixed port (default: first free port from 8000)
magnetics --no-browser # headless (e.g. on a server)
magnetics --data-dir PATH # where shot data lives (see below)Three more console scripts come with the install:
magnetics-fetch --shot 184927 # pull a shot to the local data dir
magnetics-service # start just the API/service (honors HOST/PORT)
magnetics-connect omega # run the server on a remote host, browse it locallyFetched shots are cached persistently so you don't re-pull them. The location is, in order:
$MAGNETICS_DATA_DIR(or--data-dir) if set — the knob to relocate it;- the repo's
data/dir when run from a source checkout; - otherwise a per-user data dir (
~/.local/share/magneticson Linux,~/Library/Application Support/magneticson macOS).
On a cluster, set MAGNETICS_DATA_DIR to scratch/project space (home dirs are
usually quota'd and shot files are large) — e.g. in your shell profile:
export MAGNETICS_DATA_DIR=$SCRATCH/magneticsThe server should run where the data lives (a cluster work node), but that is
rarely where your browser lives — you're on a laptop off-site, or on a NoMachine
desktop on the site's login/gateway node with a firewall between you and the
work node. magnetics-connect does the standard SSH-tunnel dance in one command:
it opens one authenticated connection to the host, asks it for a free port and
its real node name, starts the server there bound to loopback only, carries
a -L port-forward on the same connection, and opens your local browser once
the server answers through the tunnel. Only HTTP crosses the wire — fetches and
fits run next to the data, plots stream back. Ctrl-C stops both ends.
magnetics-connect omega # ~/.ssh/config alias (keys, ProxyJump)
magnetics-connect me@omega.gat.com -J me@cybele.gat.com:2039
magnetics-connect omega --data-dir /cscratch/$USER/magnetics
magnetics-connect flux --remote-cmd \
'module load magnetics && magnetics --no-browser --port {port}'Zero-install on the remote. By default the launcher bootstraps the server
for you: it runs an already-installed magnetics if the host has one; otherwise
it installs uv (the official root-free installer, into ~/.local/bin) and
launches with uvx magnetics, which fetches the package and provisions its own
Python — so a bare cluster node with only an old system Python needs nothing
installed by hand. The first run downloads + provisions (slower; watch the
streamed [host] log); later runs are cached and fast.
--install-from SOURCE— install from a wheel URL/path (any uv source) instead of PyPI. This is the shim untilmagneticsis published to PyPI.--remote-cmd '…{port}…'— full override of the launch line (bypasses the bootstrap), e.g. for an eventualmodule load magnetics.
The launcher itself is stdlib-only and standalone — on a gateway node with
only system python3, copy the single file and run it directly:
scp src/magnetics/connect.py gateway:
python3 connect.py omegaThe Python project is the repo root (a uv project); the React GUI is in gui/web/.
uv sync --group dev
uv run nbstripout --install # strip notebook outputs on commit (run once per clone)
./run-dev.sh # live: FastAPI service + GUI dev server (hot reload)
./run-dev.sh static # GUI only, static mock fixtures (offline frontend work)
./run-dev.sh --prod # build the GUI and serve it on one port (preview the wheel)
scripts/build-dist.sh # build the distributable wheel + sdist (GUI bundled) into dist/
scripts/build-dist.sh --smoke # …and smoke-test the wheel in a clean venvTo try a freshly built wheel the way an end user would — in a throwaway
environment that touches neither your .venv nor the repo's data/ — run it with
uvx --from (the installed package lives in uv's cache, so it uses the per-user
data dir, not this checkout):
uvx --from ./dist/magnetics-*.whl magnetics
uvx --from ./dist/magnetics-*.whl magnetics --data-dir "$(mktemp -d)" # touch nothing persistent
uvx --reinstall --from ./dist/magnetics-*.whl magnetics # after a rebuild at the same versionsrc/magnetics/core/— pure, device-agnostic analysis (e.g.spectral.py).src/magnetics/service/— FastAPI app; the built GUI is bundled atservice/webapp/.gui/web/— React + Vite + TypeScript frontend.tests/— pytest suite (synthetic fixtures; no real tokamak data committed).examples/— runnable Jupyter notebooks.
uv run pytest # run the Python tests
uv run jupyter lab examples/ # open the example notebooks
cd gui/web && npm test # run the frontend testsReleasing to PyPI is documented in docs/RELEASING.md.