fuse29 — Mamba-2 pedestal predictor for DIII-D
A 29-input, 6-layer Mamba-2 state-space model that predicts nine pedestal quantities at ρ_tor = 0.85 from DIII-D actuator settings, at 20 Hz, with a strictly causal O(1)-per-tick inference path suitable for real-time control.
Packaged as two ONNX graphs with normalisation baked in: raw physical units in, physical units out.
| Parameters | 11,254,425 |
| Architecture | Mamba-2 (SSD), 6 layers, d_model 512, d_state 128 |
| Inputs | 29 actuator channels, physical units |
| Outputs | 9 heads, physical units |
| Cadence | 20 Hz (50 ms) |
| Precision | float32 |
| ONNX opset | 17 |
| Step latency | ~2.9 ms/tick, CPU, batch 1 |
| License | MIT |
Intended use
Predicting pedestal density, temperature, rotation, heights and locations from the actuator request, inside FUSE or an equivalent DIII-D control or analysis pipeline. The step graph is designed for the 50 ms control cycle; the sequence graph is for offline scoring and replay.
Not intended for other machines, other radial locations, or as a safety-critical interlock. It has no uncertainty estimate and no out-of-distribution detector.
Quickstart
pip install numpy onnxruntime
from fuse29.runtime import Fuse29Predictor
predictor = Fuse29Predictor.from_pretrained("SCS-Lab/FUSE29-Pedestal-model") # loads seed 0
state = predictor.init_state()
for actuators in shot: # (29,) float32, raw physical units
out, state = predictor.step(actuators, state)
if out.is_hmode:
print(out.ne, out.te_ped, out.neped_prmtan)
Or straight from onnxruntime, with no Python package — the full contract is
about fifteen lines and is written out in docs/ONNX.md.
Files
Per seed (s0/ … s7/):
| File | |
|---|---|
fuse29_step.onnx |
one 50 ms tick, carries state, no length limit |
fuse29_seq.onnx |
whole shot at a fixed 256 ticks |
model_config.json |
I/O contract: names, order, units, sources, shapes |
norms.json |
the statistics baked into the graphs |
actuator_ranges.json |
training-split distribution, for checking your units |
provenance.json |
source checkpoint SHA-256, library versions, artefact hashes |
Plus manifest.json at the root. Seed 0 is the champion and the default.
Inputs
29 DIII-D actuator channels in raw physical units: beam power (MW) and torque,
ECH power, the eighteen shaping F-coil currents (A), both E-coil currents (A),
five gas valve flows (Torr·L/s), and the toroidal field (T). Order is
load-bearing; see docs/IO_CONTRACT.md.
pohm, ip and ipspr15v are deliberately excluded — they are plasma
responses, not commands, and a model predicting the response must not be
handed it.
Outputs
| # | Head | Units |
|---|---|---|
| 0 | ne |
10¹⁹ m⁻³ |
| 1 | te_ped |
keV |
| 2 | ti_ped |
keV |
| 3 | t_rot_ped |
krad/s |
| 4 | neped_prmtan * |
10¹⁹ m⁻³ |
| 5 | teped_prmtan * |
keV |
| 6 | rho_sym * |
ρ_tor |
| 7 | ne_top_loc * |
ρ_tor |
| 8 | hmode |
probability |
* H-mode only. See the limitations below.
Performance
Seed 0, scored with the exported ONNX graph on 4087 held-out shots. Median of per-shot RMSE:
| Head | Median per-shot RMSE | Units |
|---|---|---|
ne |
0.3057 | 10¹⁹ m⁻³ |
te_ped |
0.0605 | keV |
ti_ped |
0.1065 | keV |
t_rot_ped |
4.1497 | krad/s |
neped_prmtan * |
0.3950 | 10¹⁹ m⁻³ |
teped_prmtan * |
0.0783 | keV |
rho_sym * |
0.0113 | ρ_tor |
ne_top_loc * |
0.0134 | ρ_tor |
hmode |
0.9443 accuracy, 0.9476 F1 | — |
ONNX-vs-PyTorch parity is within 2.5e-05 of a head's training standard
deviation across all eight seeds and both graphs. Full protocol:
docs/VALIDATION.md.
Training
~40 900 DIII-D shots, 20 Hz, labels as trailing-bin means with empty bins masked rather than filled. Stratified random 80/10/10 split (seed 20260829). AdamW, lr 2e-4, cosine schedule, 25 epochs, batch 128. Checkpoint selected on lowest total validation loss across all nine heads.
Eight seeds are published; they differ only in weight initialisation and data
ordering. Full recipe in docs/TRAINING.md.
Limitations
- Four heads are undefined in L-mode.
neped_prmtan,teped_prmtan,rho_symandne_top_locwere trained only on H-mode ticks. The graph emits them unconditionally anyway. You must gate on the model's ownhmodeoutput, which is ~94% accurate with errors concentrated at L–H transitions. Seedocs/GATING.md. - Beam power is a live input in this package. The predecessor (
fuse29_s*) could not seepinjbecause a corrupted shot set the divisor to 194 MW. These weights were retrained against winsorised actuator statistics; a half-rangepinjswing now movesneby 0.79. Isolated sweeps are not a real power scan — other actuators stay at recorded values. Seedocs/LIMITATIONS.md. - Accuracy measures interpolation. The split is random, so validation shots come from the same campaigns as training shots. A chronological split scores roughly 1.5–2× worse. Expect degradation outside the training distribution, with no built-in warning.
- No uncertainty. Seed spread measures optimisation variance, not predictive uncertainty, and says nothing about errors all eight seeds share.
- Units have no guard rail.
pinjis MW, not W. A unit error produces confident nonsense rather than an exception; runpredictor.check_units(shot)once during integration. - Fixed 50 ms cadence, all 29 channels required every tick, no missing-channel handling, no dropped-tick recovery.
- Sequence graph is fixed at 256 ticks. Use the step graph for anything longer.
- float32 only; no quantised variants are published or validated.
- DIII-D only, ρ_tor = 0.85 only.
Full discussion in docs/LIMITATIONS.md.