JAX-native monoenergetic neoclassical transport solver
Project description
NTX
NTX is a JAX-native monoenergetic neoclassical transport solver for stellarator flux surfaces. It implements the Legendre-space formulation described in Javier Escoto's PhD thesis, Fast monoenergetic neoclassical transport coefficients in stellarators.
NTX is intended to be useful in two modes:
- a straightforward command-line workflow for production solves
- an imported Python/JAX workflow for scans, autodiff, optimization, NEOPAX coupling, and throughput-oriented parallel execution
Fastest Start
Install:
python -m pip install ntx
Run the smallest bundled case:
ntx examples/example_surface.toml
That command:
- prints a Rich summary to the terminal
- solves one monoenergetic case
- writes
examples/example_surface.npz
Inspect the output graphically:
python examples/plot_output_npz.py examples/example_surface.npz
What NTX Computes
For a single flux surface and monoenergetic case, NTX computes:
D11D31D13D33D33_spitzer
plus diagnostics and, optionally, the low-order Legendre modes used in the coefficient calculation.
Physics Gates
NTX keeps solver-side and closure-side validation separate.
The active gate hierarchy is:
- analytical identities:
- Onsager symmetry
- exact generated
P=2Sonine/Hankel recovery of the current three-moment closure - fixed observable map
U_parallel = n c_0
- independent-code comparison gates:
- precise-QS Redl vs archived SFINCS on the interior benchmark window
- integrated-workflow transfer gate:
- rebuilt W7-X raw-branch imported workflow
- closure stress test:
- precise-QS fixed-field
NTX+NEOPAXcurrent benchmark
- precise-QS fixed-field
Inspect the tracked artifact-backed gates locally:
python scripts/check_physics_gates.py
That report is the quickest way to see which comparisons are hard acceptance gates and which are monitored closure stress tests.
Installation
Basic install:
python -m pip install ntx
Editable install with test and docs tools:
python -m pip install -e ".[dev,docs,io]"
Install the optional JAX VMEC/Boozer geometry helpers directly when needed:
python -m pip install git+https://github.com/uwplasma/vmec_jax.git
python -m pip install git+https://github.com/uwplasma/booz_xform_jax.git
Simplest Way To Run The Code
The main command is:
ntx input.toml
Minimal input file:
[surface]
type = "example"
[grid]
n_theta = 9
n_zeta = 9
n_xi = 8
[case]
nu_hat = 1e-2
epsi_hat = 0.0
The CLI writes a compressed .npz file containing:
- transport coefficients
- residual and Onsager diagnostics
- resolved electric-field normalization
- geometry arrays on the angular grid
- run metadata and the original input text
Main Inputs
The CLI supports:
- the built-in analytic sample surface
- DKES-style Boozer harmonic files
- VMEC
woutfiles throughvmec_jax
Imported Python workflows additionally support direct Boozer-file loading and in-memory surface construction.
Important numerical knobs:
n_theta,n_zeta,n_xinu_hatepsi_hatorer_hat- VMEC radial and mode-selection options
Full schema:
Ways To Run NTX
1. CLI solve from TOML
ntx examples/sample_dkes.toml
ntx examples/sample_vmec.toml
2. Python single-case solve
from ntx import GridSpec, MonoenergeticCase, example_surface, solve_monoenergetic
surface = example_surface()
grid = GridSpec(n_theta=17, n_zeta=25, n_xi=32)
case = MonoenergeticCase(nu_hat=1e-3, epsi_hat=0.0)
result = solve_monoenergetic(surface, grid, case)
print(result.D11, result.D31, result.D13, result.D33)
3. Batched differentiable JAX scans
import jax.numpy as jnp
from ntx import GridSpec, example_surface, solve_monoenergetic_scan
surface = example_surface()
grid = GridSpec(17, 25, 16)
nu_hat = jnp.logspace(-5, -2, 8)
epsi_hat = jnp.zeros_like(nu_hat)
coefficients = solve_monoenergetic_scan(surface, grid, nu_hat, epsi_hat=epsi_hat)
4. Throughput-oriented multiprocess scans
import jax.numpy as jnp
from ntx import GridSpec, example_surface, solve_monoenergetic_multiprocess_scan
surface = example_surface()
grid = GridSpec(17, 25, 16)
nu_hat = jnp.logspace(-5, -2, 64)
epsi_hat = jnp.zeros_like(nu_hat)
coefficients = solve_monoenergetic_multiprocess_scan(
surface,
grid,
nu_hat,
epsi_hat=epsi_hat,
backend="cpu",
workers=4,
)
5. NEOPAX coupling
NTX exposes direct scan builders and mapping helpers for NEOPAX-style monoenergetic databases:
build_ntx_neopax_scan(...)build_ntx_neopax_scan_from_surfaces(...)scan_to_neopax_arrays(...)to_neopax_monoenergetic(...)write_neopax_scan_hdf5(...)
Run:
python examples/neopax_with_ntx.py
6. Autodiff and optimization workflows
Run:
python examples/autodiff_inverse_problem.py
python examples/neopax_autodiff_profiles.py
python examples/derivative_audit.py
python examples/derivative_path_benchmark.py
python examples/geometry_family_breadth_summary.py
python examples/geometry_family_transport_convergence.py --preset paper
python examples/prepared_geometry_reuse_profile.py --preset paper
python examples/ambipolar_profile.py
python examples/ambipolar_profile_family.py
python examples/profile_control_optimization.py
python examples/profile_basis_optimization.py
python examples/profile_transport_loop.py
python examples/primitive_profile_transport.py
python examples/bootstrap_current_optimization.py
python examples/bootstrap_current_with_neopax.py
These generate figures for:
- inverse problems
- sensitivity analysis
- direct autodiff versus finite-difference derivative audits
- direct reverse-mode versus prepared custom-VJP derivative timing
- artifact-backed geometry-family derivative breadth status
- VMEC geometry-family
D11/D31/D33convergence stress scans - prepared-geometry and compiled-solver reuse performance profiling
- ambipolar residual landscapes and bootstrap-current-proxy profile solves
- controlled families of ambipolar and bootstrap-current-proxy profiles
- differentiable optimization of scalar profile controls
- low-dimensional radial-basis optimization of profile controls
- accepted-step transport iteration of the radial profile closure with radial smoothing
- primitive density/temperature transport workflows with explicit source-target closure
- differentiable bootstrap-current optimization
- radial bootstrap-current profiles from NTX + NEOPAX
The geometry-family breadth summary reads the committed analytic, file-backed, boundary-projected, explicit-relaxed, and implicit-equilibrium derivative artifacts and makes the current validation scope explicit:
- active stress cases stay below the committed derivative thresholds
- the implicit-volume objective closes on the committed QA case
- implicit Boozer and NTX transport objectives are closed as non-shipping diagnostics until residual contraction and tangent parity pass
- broad W7-X/QI/omnigenous geometry-family validation remains planned work
The direct VMEC geometry-family transport diagnostic discovers reusable local
examples from the surrounding vmec_jax, STELLOPT, and SIMSOPT checkouts and
runs a D11/D31/D33 grid ladder:
python examples/geometry_family_transport_convergence.py --preset paper
The current artifact solves tokamak, precise-QS QA/QH, QI-style, W7-X EIM/EJM, LHD, HSX, and NCSX-family cases when those local public inputs are available. It is intentionally reported as a reduced NTX convergence stress diagnostic, not as hidden parity with an independent solver; paper-resolution independent parity and an owned W7-X KJM input remain explicit promotion requirements.
Bootstrap-Current Validation
Fixed-field precise-QS benchmark against archived SFINCS, SFINCS-JAX reruns,
Redl, and NTX+NEOPAX:
python examples/bootstrap_current_fixed_field_validation.py
This uses the local precise-QS Zenodo archive and writes:
docs/_static/bootstrap_current_fixed_field_validation.pngdocs/_static/bootstrap_current_fixed_field_validation.pdfdocs/_static/bootstrap_current_fixed_field_validation.json
The database-facing normalization now follows the actual NEOPAX loader path:
D11 -> D11 * drds^2D13 -> D13 * drdsD33 -> nu * D33
That closes the integrated W7-X database handoff, but it also makes the precise-QS fixed-field benchmark a stricter closure test than before. On that archive-backed benchmark, the present interior max relative errors versus archived SFINCS are:
- QA:
1.16e+0 - QH:
1.16e+0
Redl remains closer on the same family (6.86e-2 on QA and 4.06e-2 on QH,
interior-window metric). So this figure should now be read as a closure stress
test, not as a parity claim.
The benchmark keeps a monotone PCHIP radial postprocessing map by default,
since the interpolation audit showed that neither the final radial remap nor
the internal NTSS-style versus direct 3D interpolation choice is the dominant
error source here.
A dedicated W7-X rebuild audit now exists in:
examples/bootstrap_current_w7x_rebuild_audit.py
That script rebuilds a NEOPAX-format W7-X database with NTX and compares it
against the shipped external database on the same momentum-corrected workflow.
With the physically correct D13 database normalization, the integrated W7-X
transfer now closes on the raw branch:
- shipped external database:
1.18e-12max relative error against the frozen W7-X reference current - NTX-rebuilt W7-X,
d33_mode="raw":6.58e-6 - NTX-rebuilt W7-X,
d33_mode="spitzer":5.77e-1 - NTX-rebuilt W7-X,
d33_mode="conductivity_difference":2.67e+0
At the worst shipped W7-X coefficient points, the direct solver and the scan
builder both reproduce the frozen reference table on the benchmark grid
25x25x63 to about 1e-6 relative error. Lower-resolution scans are
under-resolved, and blindly increasing the angular/Legendre grid does not
reproduce the frozen benchmark monotonically on every point, so the W7-X audit
is now anchored to the actual reference grid rather than to a naive
monotonic-refinement assumption.
The compact closure-validation report is now tracked in:
scripts/build_closure_validation_report.py
It writes:
docs/_static/closure_validation_report.jsondocs/_static/closure_validation_report.txtdocs/_static/closure_validation_report.pngdocs/_static/closure_validation_report.pdf
and freezes the current model-family reading in one place:
- precise-QS Redl vs archived SFINCS stays inside the
1e-1gate - rebuilt W7-X raw-branch transfer stays inside the
2e-2gate - fixed-field
NTX+NEOPAXremains a monitored closure stress test - fixed-field closure diagnostics show that thermal-source reweighting can reduce the stress metric on the archive, but that diagnostic is not a transferable production closure
- the first
Pmax>2tail extension remains rejected because it regresses W7-X
Streamlined radial-profile example with NEOPAX:
python examples/bootstrap_current_with_neopax.py
This shows the direct NTX scan -> NEOPAX closure workflow and writes:
docs/_static/bootstrap_current_with_neopax.pngdocs/_static/bootstrap_current_with_neopax.pdfdocs/_static/bootstrap_current_with_neopax.json
W7-X bootstrap-current convergence audit:
python examples/bootstrap_current_reference_audit_w7x.py
This rebuilds a reduced W7-X scan at several NTX resolutions and writes a two-panel convergence figure showing the bootstrap-current profile and the maximum relative error versus grid resolution:
Parallelization
NTX supports:
- serial batched JAX scans
- guarded single-process device-parallel scans
- multiprocess CPU/GPU scans for larger throughput jobs
Current practical guidance:
- use serial batched JAX for small and medium studies
- compile and reuse prepared fixed-geometry closures for repeated monoenergetic scans and optimization loops
- use the multiprocess lane for larger throughput-oriented jobs
See:
NEOPAX Connection
NTX is designed to provide monoenergetic coefficient tables to NEOPAX. The dedicated interface is documented in:
Documentation
Full documentation:
Local Quality Checks
python -m ruff check .
python -m mypy src/ntx
python -m pytest -q
python -m sphinx -b html docs docs/_build/html
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ntx-0.2.0.tar.gz.
File metadata
- Download URL: ntx-0.2.0.tar.gz
- Upload date:
- Size: 8.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bc1c5050235467dd9001950ca73d48a282ea4b83a2404e3688cfbe08c652c6c
|
|
| MD5 |
cf7bd10e31a7ccd0ecc88c31b7e187da
|
|
| BLAKE2b-256 |
58f71485dfc40276e6baabd50941bb7d427fd4084115885d68bc05e2acd42289
|
Provenance
The following attestation bundles were made for ntx-0.2.0.tar.gz:
Publisher:
release.yml on uwplasma/NTX
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ntx-0.2.0.tar.gz -
Subject digest:
2bc1c5050235467dd9001950ca73d48a282ea4b83a2404e3688cfbe08c652c6c - Sigstore transparency entry: 1374125400
- Sigstore integration time:
-
Permalink:
uwplasma/NTX@c98ec8c28bfdcccce56e629d478ea7c1457c5b41 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/uwplasma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c98ec8c28bfdcccce56e629d478ea7c1457c5b41 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ntx-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ntx-0.2.0-py3-none-any.whl
- Upload date:
- Size: 122.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffb1458e9db2a7b5ab7fea86456ebb7817d9636248519accf871bcabf223d7ad
|
|
| MD5 |
57faca5f1cb83daf5c2dc1a823e4e297
|
|
| BLAKE2b-256 |
119b9c84b7132f7ce846e4a26ccd1c0e7a50fc1d88f415a272dc731e517449d6
|
Provenance
The following attestation bundles were made for ntx-0.2.0-py3-none-any.whl:
Publisher:
release.yml on uwplasma/NTX
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ntx-0.2.0-py3-none-any.whl -
Subject digest:
ffb1458e9db2a7b5ab7fea86456ebb7817d9636248519accf871bcabf223d7ad - Sigstore transparency entry: 1374125488
- Sigstore integration time:
-
Permalink:
uwplasma/NTX@c98ec8c28bfdcccce56e629d478ea7c1457c5b41 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/uwplasma
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c98ec8c28bfdcccce56e629d478ea7c1457c5b41 -
Trigger Event:
push
-
Statement type: