Skip to content

API Reference

Top-Level Exports

import scpn_control

scpn_control.__version__       # "0.23.0"
scpn_control.FusionKernel      # Grad-Shafranov equilibrium solver
scpn_control.RUST_BACKEND      # True if Rust acceleration available
scpn_control.TokamakConfig     # Preset tokamak geometries
scpn_control.StochasticPetriNet
scpn_control.FusionCompiler
scpn_control.CompiledNet
scpn_control.NeuroSymbolicController
scpn_control.kuramoto_sakaguchi_step
scpn_control.order_parameter
scpn_control.KnmSpec
scpn_control.build_knm_paper27
scpn_control.UPDESystem
scpn_control.LyapunovGuard
scpn_control.RealtimeMonitor
scpn_control.PhysicsDebugAssistant

SCPN — AER Control Observation

scpn_control.scpn.observation adapts asynchronous Address-Event Representation spike streams into bounded controller features while preserving the existing mapping-based observation contract. The Python surface provides SpikeEvent, SpikeBuffer, rate/temporal/ISI decoders, and AERControlObservation.to_features(). NeuroSymbolicController.step() accepts either the existing Mapping[str, float] observation or an AERControlObservation; typed AER input is decoded through AERControlObservation.to_feature_mapping() before the same feature-injection path runs. When JSONL controller logging is enabled, the record includes the decoded obs mapping plus aer_admission metadata from SpikeBuffer.admission_report(). The matching Rust implementation lives in control_core::spike_buffer, with optional PyO3 bindings exposed as scpn_control_rs.PySpikeBuffer and scpn_control_rs.aer_decode_*. monotonic_input and out_of_order_event_count provide bounded ingress evidence. Observations may set require_monotonic=True to fail closed before controller injection if upstream AER timestamps violate the monotonic admission contract.

This is an ingress adapter and feature-decoding contract. It does not claim hardware AER signal integrity, target neuromorphic-device deployment, FPGA timing closure, or PCS admission without separate hardware evidence.

SpikeEvent dataclass

SpikeEvent(neuron_id, timestamp_ns)

Single Address-Event Representation spike event.

SpikeBuffer

SpikeBuffer(capacity)

Bounded FIFO ring buffer for AER spike events.

Overflow drops the oldest event and latches overflowed until clear is called. This keeps memory bounded under bursty neuromorphic input.

capacity property

capacity

Maximum number of retained spike events.

overflowed property

overflowed

Return whether the buffer has dropped at least one event.

out_of_order_event_count property

out_of_order_event_count

Return count of events admitted after a later timestamp was seen.

monotonic_input property

monotonic_input

Return whether admitted timestamps were non-decreasing.

push

push(event)

Append event and drop the oldest event if the buffer is full.

extend

extend(events)

Append a sequence of spike events in order.

snapshot

snapshot()

Return retained events without mutating the buffer.

drain_window

drain_window(window_ns, now_ns)

Drain events with timestamps in [now_ns - window_ns, now_ns].

Events older than the window are discarded. Events newer than now_ns are retained for the next control tick.

clear

clear()

Clear buffered events and reset the overflow latch.

admission_report

admission_report()

Return bounded-buffer admission metadata for safety gating.

AERControlObservation dataclass

AERControlObservation(timestamp_ns, spike_stream, decode_window_ns, decode_strategy='rate', n_features=64, feature_normalisation='unit', require_monotonic=False)

Control observation that decodes an AER spike stream to feature arrays.

to_features

to_features()

Drain the active window and return float64 features in [0, 1].

to_feature_mapping

to_feature_mapping(prefix='aer_')

Return decoded features as a dictionary for existing mapping consumers.

admission_report

admission_report()

Return spike-buffer admission metadata for this observation source.

decode_rate

decode_rate(events, window_ns, n_features)

Decode AER events as per-feature event fractions over the active window.

decode_temporal

decode_temporal(events, window_ns, n_features, *, now_ns=None)

Decode by first-spike timing; earlier spikes map closer to one.

decode_isi

decode_isi(events, window_ns, n_features)

Decode by mean inter-spike interval; faster repeated spikes map higher.


SCPN — Geometry-Neutral Replay Schema

scpn_control.scpn.geometry_neutral_replay publishes deterministic geometry-neutral replay reports and schema admission helpers. The v1.1 schema extends the v1 report with optional pulsed-shot context: UUID pulse IDs, capacitor initial energy, trigger timestamp, recovered energy, sorted shot-phase logs, FRC diagnostic scalars, and digest-bound AER admission metadata. Existing v1 reports remain loadable under the v1.1 schema bundle.

Use the dedicated Geometry-Neutral Replay guide for field semantics, example payloads, and claim boundaries.

generate_report

generate_report(*, steps=12, seed=314159)

Generate a deterministic compact SCPN-control replay report.

validate_report

validate_report(report)

Validate the compact replay report contract without external packages.

load_replay_schema

load_replay_schema(version)

Load a bundled geometry-neutral replay JSON Schema document.

register_v1_1_schema

register_v1_1_schema()

Return the bundled v1.1 replay schema after self-identification checks.

assert_v1_replay_loadable_under_v1_1_schema_bundle

assert_v1_replay_loadable_under_v1_1_schema_bundle(report)

Validate a v1 replay report while the v1.1 schema bundle is installed.

build_aer_admission_metadata

build_aer_admission_metadata(*, admission_report, decode_strategy, decode_window_ns, n_features, feature_normalisation='unit', require_monotonic=False, feature_vector=None)

Build replay-safe AER admission metadata from a decoded observation.

attach_aer_admission_metadata

attach_aer_admission_metadata(report, aer_admission)

Attach digest-bound AER admission metadata to a replay v1.1 report.

save_geometry_neutral_replay_report

save_geometry_neutral_replay_report(report, output_path)

Persist a validated geometry-neutral replay report as stable JSON.

load_geometry_neutral_replay_report

load_geometry_neutral_replay_report(path)

Load and validate a geometry-neutral replay report with duplicate-key checks.


Control — Pulsed Scenario Scheduler v2

scpn_control.control.pulsed_scenario_scheduler_v2 owns the reusable pulsed-fusion lifecycle contract that MIF-CORE incubated as MIF-004. The scheduler models the adjacent state ring:

idle -> ramp_up -> flat_top -> burn -> expansion -> dump -> recharge -> cool_down -> idle

The Python surface provides the control-plane API and audit-log contract. The matching Rust kernel lives in control_control::pulsed_scenario for the compiled hot-path lane. When the optional extension is built, scpn_control_rs.PyPulsedScenarioScheduler exposes that Rust kernel directly to Python without changing the pure-Python API. All surfaces use the same state names, action names, guard thresholds, monotone timestamp checks, and transition reasons.

scpn_control.control.pulsed_scenario_scheduler is retained as the SCPN-MIF-CORE compatibility import and re-exports the v2 implementation. The finite-state topology is also captured in lean/SCPNControl/PulsedFSM.lean, including the liveness theorem pulsed_fsm_eventually_returns_to_idle.

The scheduler is a generic pulsed-reactor controller primitive. It is not a facility-validated PCS implementation by itself. Hardware timing, actuator mapping, capacitor-bank plant dynamics, and measured-shot validation remain separate admission surfaces.

pulsed_scenario_scheduler

Compatibility surface for the CONTROL-owned pulsed scenario scheduler.

SCPN-MIF-CORE originally contracted the module path scpn_control.control.pulsed_scenario_scheduler. The production implementation lives in pulsed_scenario_scheduler_v2; this module keeps the cross-repository import stable without duplicating scheduler logic.

CapacitorBankTelemetry dataclass

CapacitorBankTelemetry(voltage_V, voltage_max_V, energy_J)

Capacitor-bank telemetry consumed by lifecycle transition guards.

voltage_fraction property

voltage_fraction

Return bank voltage as a fraction of the declared maximum.

PulsedPlasmaTelemetry dataclass

PulsedPlasmaTelemetry(coil_current_A, temperature_eV, phase_lock_error_rad, reference_error_m, fusion_power_W, radial_velocity_m_s)

Plasma telemetry consumed by lifecycle transition guards.

PulsedScenarioAction

Bases: StrEnum

Command emitted for the active lifecycle state.

PulsedScenarioCommand dataclass

PulsedScenarioCommand(t_s, state, action, reason, transition, dwell_s)

Command emitted by one pulsed-scenario scheduler step.

PulsedScenarioScheduler

PulsedScenarioScheduler(spec)

CONTROL-owned reusable eight-state scheduler for pulsed-fusion shots.

audit_log property

audit_log

Return immutable transition audit entries.

reset

reset()

Return to idle and clear timestamp and audit state.

transition_to

transition_to(next_state, t_s, reason)

Perform a validated manual adjacent transition.

step

step(t_s, plasma, bank)

Evaluate lifecycle guards at t_s and emit the active-state command.

audit_log_jsonl

audit_log_jsonl()

Return the transition audit log as newline-delimited JSON.

PulsedScenarioSpec dataclass

PulsedScenarioSpec(min_precharge_energy_J, ramp_current_A, phase_tolerance_rad, spatial_tolerance_m, burn_temperature_eV, min_fusion_power_W, expansion_velocity_m_s, dump_energy_floor_J, recharge_voltage_fraction, cooldown_temperature_eV, cooldown_current_A, min_burn_duration_s=0.0)

Guard thresholds for a reusable pulsed-fusion shot lifecycle.

PulsedScenarioState

Bases: StrEnum

Canonical pulsed-fusion lifecycle states.

PulsedScenarioTransition dataclass

PulsedScenarioTransition(t_s, from_state, to_state, reason)

Single lifecycle transition audit entry.

to_json

to_json()

Return the stable JSON-serialisable audit representation.

PulsedScenarioState

Bases: StrEnum

Canonical pulsed-fusion lifecycle states.

PulsedScenarioAction

Bases: StrEnum

Command emitted for the active lifecycle state.

PulsedScenarioSpec dataclass

PulsedScenarioSpec(min_precharge_energy_J, ramp_current_A, phase_tolerance_rad, spatial_tolerance_m, burn_temperature_eV, min_fusion_power_W, expansion_velocity_m_s, dump_energy_floor_J, recharge_voltage_fraction, cooldown_temperature_eV, cooldown_current_A, min_burn_duration_s=0.0)

Guard thresholds for a reusable pulsed-fusion shot lifecycle.

PulsedPlasmaTelemetry dataclass

PulsedPlasmaTelemetry(coil_current_A, temperature_eV, phase_lock_error_rad, reference_error_m, fusion_power_W, radial_velocity_m_s)

Plasma telemetry consumed by lifecycle transition guards.

CapacitorBankTelemetry dataclass

CapacitorBankTelemetry(voltage_V, voltage_max_V, energy_J)

Capacitor-bank telemetry consumed by lifecycle transition guards.

voltage_fraction property

voltage_fraction

Return bank voltage as a fraction of the declared maximum.

PulsedScenarioTransition dataclass

PulsedScenarioTransition(t_s, from_state, to_state, reason)

Single lifecycle transition audit entry.

to_json

to_json()

Return the stable JSON-serialisable audit representation.

PulsedScenarioCommand dataclass

PulsedScenarioCommand(t_s, state, action, reason, transition, dwell_s)

Command emitted by one pulsed-scenario scheduler step.

PulsedScenarioScheduler

PulsedScenarioScheduler(spec)

CONTROL-owned reusable eight-state scheduler for pulsed-fusion shots.

audit_log property

audit_log

Return immutable transition audit entries.

reset

reset()

Return to idle and clear timestamp and audit state.

transition_to

transition_to(next_state, t_s, reason)

Perform a validated manual adjacent transition.

step

step(t_s, plasma, bank)

Evaluate lifecycle guards at t_s and emit the active-state command.

audit_log_jsonl

audit_log_jsonl()

Return the transition audit log as newline-delimited JSON.


Control — Capacitor Bank State Model

scpn_control.control.capacitor_bank_state owns the CONTROL-side bounded series-RLC capacitor-bank contract for pulsed-shot admission. It mirrors the MIF-CORE MIF-005 capacitor-bank mathematics at the control boundary: damping regime classification, closed-form free response, Crank-Nicolson stepping, midpoint-sampled discharge waveforms, conservative feasibility guards, and constant-power recharge projection.

The state equation is:

d/dt [v_C, i]^T = [[0, -1/C], [1/L, -R/L]] [v_C, i]^T + [-i_load/C, 0]^T

where C is capacitance in farads, L is inductance in henries, R is series resistance in ohms, v_C is capacitor voltage in volts, i is series current in amperes, and i_load is the prescribed external load current. The numerical step uses a Crank-Nicolson update so natural-response validation can compare against the analytical underdamped, critical, and overdamped solutions.

CapacitorBank.telemetry() adapts the state model to PulsedScenarioScheduler v2 by emitting scheduler-compatible capacitor-bank telemetry with absolute voltage magnitude, declared voltage limit, and stored energy. The matching Rust kernel lives in control_control::capacitor_bank. When the optional extension is built, scpn_control_rs.PyCapacitorBankModel, scpn_control_rs.PyCapacitorBankSpec, and scpn_control_rs.capacitor_bank_free_response() expose the compiled surface directly to Python.

scpn_control.control.capacitor_bank is retained as the SCPN-MIF-CORE compatibility import and re-exports the state-model implementation without duplicating the RLC mathematics.

CapacitorBank.discharge() now reports an explicit total RLC energy ledger for admission and replay: initial total stored energy, remaining total stored energy, remaining capacitor electric energy, remaining inductor magnetic energy, integrated ohmic loss, integrated prescribed-load extraction, absolute residual, relative residual, and a boolean pass/fail flag. The residual contract is:

energy_initial - energy_remaining
  = resistive_loss + load_energy + energy_balance_residual

The ledger uses midpoint quantities from the same Crank-Nicolson step that advances the state, so the residual is a numerical consistency check on the CONTROL admission model rather than a facility hardware protection claim. CapacitorBankState.energy_J remains scheduler-facing capacitor electric energy only.

This model is a bounded control admission and scheduling primitive. It is not a validated facility capacitor-bank driver, insulation model, switch model, or hardware interlock implementation. Facility deployment still requires target hardware timing, protection relays, isolation evidence, and shot-matched validation artefacts.

capacitor_bank

Compatibility surface for the CONTROL-owned capacitor-bank model.

SCPN-MIF-CORE originally contracted the module path scpn_control.control.capacitor_bank. The production implementation lives in capacitor_bank_state; this module keeps the cross-repository import stable without duplicating the RLC mathematics.

CapacitorBank

CapacitorBank(spec, initial_voltage_V=0.0, initial_current_A=0.0)

Mutable bounded series-RLC bank with analytical and numerical stepping.

spec property

spec

Return the immutable bank specification.

state property

state

Return an immutable snapshot of the current bank state.

telemetry

telemetry()

Return scheduler-compatible bank telemetry using absolute voltage magnitude.

reset

reset(initial_voltage_V=0.0, *, initial_current_A=0.0)

Reset bank state at t = 0 with bounded initial voltage.

step

step(dt, *, external_load_current_A=0.0)

Advance the bank one exact zero-order-hold step with optional load current.

The update is the exact state-transition solution of the series-RLC system for a load current held constant over the step, so it reproduces the closed-form :func:free_response to machine precision rather than the second-order Crank-Nicolson truncation it replaces.

discharge

discharge(pulse, dt, n_steps)

Drive the bank with pulse using midpoint-sampled load current.

The bank dynamics advance with the exact zero-order-hold stepper, and each step's ohmic dissipation :math:R\int i^2\,dt and load extraction :math:\int v\,u\,dt are integrated in closed form, so the energy ledger closes to machine precision instead of carrying second-order quadrature error.

feasibility

feasibility(pulse)

Run conservative pulse admissibility guards against current bank state.

recharge_status

recharge_status(t)

Project constant-power recharge state after non-negative time t.

CapacitorBankSpec dataclass

CapacitorBankSpec(capacitance_F, inductance_H, series_resistance_ohm, voltage_max_V, recharge_power_kW=0.0)

Physical parameters for a bounded series RLC capacitor bank.

natural_impedance_ohm property

natural_impedance_ohm

Return :math:Z_0 = \sqrt{L/C}.

undamped_angular_frequency_rad_s property

undamped_angular_frequency_rad_s

Return :math:\omega_0 = 1/\sqrt{LC}.

damping_ratio property

damping_ratio

Return the dimensionless series-RLC damping ratio.

regime property

regime

Classify the bank damping regime from the physical coefficients.

CapacitorBankState dataclass

CapacitorBankState(t, voltage_V, current_A, di_dt_A_s, capacitance_F)

Instantaneous bank state in SI units.

energy_J property

energy_J

Return capacitor stored energy :math:0.5 C V^2.

EnergyReport dataclass

EnergyReport(energy_delivered_J, energy_initial_J, energy_remaining_J, capacitor_energy_remaining_J, inductor_energy_remaining_J, resistive_loss_J, load_energy_J, energy_balance_residual_J, energy_balance_relative_error, energy_balance_passed, peak_voltage_V, peak_current_A, discharge_duration_s, rlc_regime)

Energy bookkeeping returned by a discharge simulation.

PulseSpec dataclass

PulseSpec(peak_current_A, duration_s, waveform='half_sine')

Prescribed load-current waveform for bounded discharge simulations.

RLCRegime

Bases: StrEnum

Canonical damping regimes for the series RLC bank model.

free_response

free_response(spec, v0, i0, t)

Evaluate the closed-form homogeneous series-RLC response at time t.

RLCRegime

Bases: StrEnum

Canonical damping regimes for the series RLC bank model.

CapacitorBankSpec dataclass

CapacitorBankSpec(capacitance_F, inductance_H, series_resistance_ohm, voltage_max_V, recharge_power_kW=0.0)

Physical parameters for a bounded series RLC capacitor bank.

natural_impedance_ohm property

natural_impedance_ohm

Return :math:Z_0 = \sqrt{L/C}.

undamped_angular_frequency_rad_s property

undamped_angular_frequency_rad_s

Return :math:\omega_0 = 1/\sqrt{LC}.

damping_ratio property

damping_ratio

Return the dimensionless series-RLC damping ratio.

regime property

regime

Classify the bank damping regime from the physical coefficients.

CapacitorBankState dataclass

CapacitorBankState(t, voltage_V, current_A, di_dt_A_s, capacitance_F)

Instantaneous bank state in SI units.

energy_J property

energy_J

Return capacitor stored energy :math:0.5 C V^2.

PulseSpec dataclass

PulseSpec(peak_current_A, duration_s, waveform='half_sine')

Prescribed load-current waveform for bounded discharge simulations.

EnergyReport dataclass

EnergyReport(energy_delivered_J, energy_initial_J, energy_remaining_J, capacitor_energy_remaining_J, inductor_energy_remaining_J, resistive_loss_J, load_energy_J, energy_balance_residual_J, energy_balance_relative_error, energy_balance_passed, peak_voltage_V, peak_current_A, discharge_duration_s, rlc_regime)

Energy bookkeeping returned by a discharge simulation.

free_response

free_response(spec, v0, i0, t)

Evaluate the closed-form homogeneous series-RLC response at time t.

CapacitorBank

CapacitorBank(spec, initial_voltage_V=0.0, initial_current_A=0.0)

Mutable bounded series-RLC bank with analytical and numerical stepping.

spec property

spec

Return the immutable bank specification.

state property

state

Return an immutable snapshot of the current bank state.

telemetry

telemetry()

Return scheduler-compatible bank telemetry using absolute voltage magnitude.

reset

reset(initial_voltage_V=0.0, *, initial_current_A=0.0)

Reset bank state at t = 0 with bounded initial voltage.

step

step(dt, *, external_load_current_A=0.0)

Advance the bank one exact zero-order-hold step with optional load current.

The update is the exact state-transition solution of the series-RLC system for a load current held constant over the step, so it reproduces the closed-form :func:free_response to machine precision rather than the second-order Crank-Nicolson truncation it replaces.

discharge

discharge(pulse, dt, n_steps)

Drive the bank with pulse using midpoint-sampled load current.

The bank dynamics advance with the exact zero-order-hold stepper, and each step's ohmic dissipation :math:R\int i^2\,dt and load extraction :math:\int v\,u\,dt are integrated in closed form, so the energy ledger closes to machine precision instead of carrying second-order quadrature error.

feasibility

feasibility(pulse)

Run conservative pulse admissibility guards against current bank state.

recharge_status

recharge_status(t)

Project constant-power recharge state after non-negative time t.


Control — Pulsed-Shot MPC Admission Adapter

scpn_control.control.fusion_neural_mpc.PulsedShotMPCAdapter wraps the CONTROL-owned gradient MPC surface and admits its first action through the pulsed-scenario scheduler and capacitor-bank feasibility guard. It is a control-boundary adapter over existing MPC output, not a new equilibrium solver or a duplicate solver lane.

The adapter applies three deterministic checks:

  • Non-burn scheduler states replace selected burn-action components with the configured safe action.
  • burn state evaluates a PulseSpec against CapacitorBank.feasibility() unless the caller disables that policy.
  • Every step records an explainable decision dictionary with scheduler state, capacitor feasibility text, constraint slack, MPC objective, whether a safe action was applied, and a digest-bound admission evidence payload.

The matching Rust kernel lives in control_control::mpc::MPController as plan_pulsed(). When the optional PyO3 extension is rebuilt, scpn_control_rs.PyMpcController.plan_pulsed() exposes the same admission fields to Python, including evidence_schema_version, action_sha256, safe_action_sha256, burn_action_mask_sha256, peak_current_A, and admission_digest.

Use the dedicated Pulsed MPC Adapter guide for examples, runtime boundaries, and benchmark evidence commands.

PulsedShotMPCDecision dataclass

PulsedShotMPCDecision(action, mpc_objective, constraint_slack, scheduler_state, bank_feasibility, reason, bank_feasible, safe_action_applied, burn_components_masked, peak_current_A, evidence_schema_version, action_sha256, safe_action_sha256, burn_action_mask_sha256, admission_digest)

Admitted pulsed-shot MPC action and its control-boundary rationale.

PulsedShotMPCAdapter

PulsedShotMPCAdapter(nmpc, scheduler, bank, *, burn_action_mask=None, safe_action=None, pulse_duration_s=0.001, pulse_waveform='half_sine', refuse_burn_when_uncharged=True)

Gate an existing MPC action through pulsed-shot lifecycle and bank guards.

step

step(state, ref=None, context=None, *, pulse=None)

Return an MPC action admitted by scheduler and capacitor-bank guards.

explain_last_decision

explain_last_decision()

Return the latest scheduler, bank, and MPC admission summary.


Control — Multi-Shot Campaign Orchestrator

scpn_control.control.multi_shot_campaign runs repeated pulsed-shot admission over the scheduler, capacitor-bank telemetry, and replay v1.1 metadata contracts. It accepts explicit shot telemetry, records command and transition logs, requires the canonical pulsed lifecycle by default, and emits a digest-bound campaign report. When supplied, per-shot pulsed_mpc_admission_digest values are validated as lowercase SHA-256 digests and preserved in the campaign report plus replay v1.1 extension fields.

The matching Rust kernel lives in control_control::multi_shot_campaign. When the optional PyO3 extension is rebuilt, scpn_control_rs.PyMultiShotCampaignOrchestrator.run_table() exposes the Rust surface through table-shaped NumPy inputs, including optional per-shot pulsed_mpc_admission_digests.

Use the dedicated Multi-Shot Campaign guide for examples, claim boundaries, and benchmark commands.

Release admission is handled by validation.validate_multi_shot_campaign_evidence. The gate admits the Python/PyO3 benchmark report and the Rust benchmark report only when both carry the pulsed-MPC digest chain, benchmark context, stable SHA-256 payload seals, and a complete Python, PyO3, and Rust surface set. The top-level scpn-control validate command runs this gate by default before a release evidence JSON can be admitted.

CampaignShotSample dataclass

CampaignShotSample(t_s, plasma, bank=None)

One timestamped plasma and bank telemetry sample for a pulsed shot.

CampaignShotPlan dataclass

CampaignShotPlan(shot_id, samples, initial_bank_voltage_V, initial_bank_current_A=0.0, pulsed_mpc_admission_digest=None)

Input contract for one shot in a multi-shot campaign.

CampaignCommandLog dataclass

CampaignCommandLog(t_s, state, action, reason, transition, dwell_s)

Stable command log row emitted by the scheduler.

from_command classmethod

from_command(command)

Convert a scheduler command to a JSON-stable row.

to_json

to_json()

Return the JSON representation.

CampaignShotResult dataclass

CampaignShotResult(shot_id, pulse_id, passed, failure_reason, terminal_state, transition_states, command_log, shot_phase_log, capacitor_state_initial_J, capacitor_state_final_J, energy_recovered_J, trigger_timestamp_ns, pulsed_mpc_admission_digest=None)

Admission result for one pulsed shot.

to_json

to_json()

Return the JSON representation.

replay_v1_1_extension

replay_v1_1_extension()

Return fields compatible with geometry-neutral replay v1.1 extensions.

MultiShotCampaignOrchestrator

MultiShotCampaignOrchestrator(campaign_id, scheduler_spec, bank_spec, *, require_complete_lifecycle=True)

Run deterministic multi-shot admission over scheduler and bank contracts.

run

run(shots)

Run all shots and return a digest-bound campaign report.


Control — PREEMPT_RT Runtime Admission

scpn_control.core.runtime_admission emits a schema-versioned admission report before native hardware-campaign execution. It binds the observed Linux kernel, PREEMPT_RT evidence, current process affinity, requested execution cores, scheduler policy, CPU governors, heartbeat configuration, and memory-lock limits to the campaign summary. run-hardware-campaign --runtime-admission-policy require fails closed before native execution if the host is not production-admissible.

The optional PyO3 counterpart scpn_control_rs.runtime_admission_snapshot() exposes native-side kernel/core parsing when the Rust extension is installed. validation.validate_runtime_admission_evidence admits persisted runtime-admission benchmark reports into the top-level release gate only when the report carries command, CPU-affinity, host-load, isolation, latency, SHA-256 payload, and production-claim-boundary evidence.

Use the dedicated PREEMPT_RT Runtime Admission guide for policy semantics and operator examples.

RuntimeAdmissionRequest dataclass

RuntimeAdmissionRequest(execution_backend='auto', pacing_mode='sleep', transport_backend='std', formal_mode='async_drop', tick_interval_s=0.001, core_snn=1, core_z3=2, core_net=3, core_hb=4, heartbeat_port=0, native_backend_available=False, require_preempt_rt=False, require_realtime_scheduler=False, require_performance_governor=False, require_heartbeat=False, min_memlock_bytes=DEFAULT_MIN_MEMLOCK_BYTES)

Operator-requested runtime contract for a native control campaign.

requested_cores property

requested_cores

The requested CPU cores (snn, z3, net, hb) for pinning.

as_dict

as_dict()

Return the runtime-admission request as a JSON-serialisable mapping.

RuntimeAdmissionProbe dataclass

RuntimeAdmissionProbe(generated_ns, platform_system, kernel_release, is_linux, preempt_rt, realtime_sysfs, affinity, available_parallelism, scheduler_policy, scheduler_priority, governors, memlock_soft_bytes, memlock_hard_bytes, native_snapshot=None)

Observed host runtime properties used for admission.

as_dict

as_dict()

Return the runtime-environment snapshot as a JSON-serialisable mapping.

collect_runtime_admission

collect_runtime_admission(request)

Collect and evaluate current host runtime admission evidence.

evaluate_runtime_admission

evaluate_runtime_admission(request, probe)

Evaluate a runtime admission request against an observed probe.


Validation — Benchmark Regression Gates

validation/validate_benchmark_regression_gates.py admits persisted benchmark evidence before the preflight gate accepts a regression baseline. The gate does not run benchmarks and does not create timing evidence. It validates validation/reports/benchmark_regression_gates.json against the referenced latency reports, SHA-256 digests, metric paths, bounded thresholds, sample counts, hardware-context metadata, and explicit non-HIL claim boundaries.

The gate is intended to catch stale, tampered, overclaimed, or regressed local benchmark evidence before release preflight continues. Hardware-in-the-loop, target-device, cloud-GPU, or plant real-time claims remain blocked until those specific benchmark artefacts are generated and admitted separately.


Core — Native C++ Solver Bridge

scpn_control.core.hpc_bridge exposes the optional native Grad-Shafranov solver bridge. Native compilation is disabled unless SCPN_ALLOW_NATIVE_BUILD=1 is set. When enabled, the bridge admits only the package-local solver.cpp whose SHA-256 digest matches solver_manifest.json, resolves the compiler to an absolute regular executable, strips dynamic-loader injection variables from the build environment, rejects symlinked solver inputs and output targets, compiles to a temporary package-local file, and publishes the shared library atomically after the compiler produced a regular file.

The source and checksum manifest are shipped as scpn_control.core package data. Compiled bin/libscpn_solver.so or bin/scpn_solver.dll outputs are operator-local build products and are not committed.

External runtime solver libraries remain blocked unless SCPN_ALLOW_EXTERNAL_SOLVER_LIB=1 is set for a vetted absolute path. The default path searches package-local solver locations only.


Physics Debug Assistance

scpn_control.physics_debug provides a local-first advisory assistant for physics validation gaps. The default provider policy admits loopback endpoints only; facility or external gateways must be explicitly allowlisted. build_local_provider() supplies loopback profiles for common onsite gateway protocols: chat-completions-compatible, Ollama-style chat, direct JSON, and text-generation endpoints. Reports are schema-versioned advisory evidence with secret redaction, falsifiable hypothesis checks, campaign risk controls, and risk-bound prompt-injection neutralization for untrusted evidence text before provider prompting. Prompt-guard findings are recorded in the tamper-evident payload digest. build_guardrail_provider() adds an optional hallucination guardrail gateway with a director-ai default profile and explicit alternate profiles for lab-owned guardrail solutions. Guardrail block decisions fail closed before report persistence; allow findings are bound into the report digest together with the SHA-256 digest of the reviewed provider draft. The guardrail request also binds the provider metadata, safety policy, and guardrail policy digests so reviews cannot be replayed across another provider or relaxed policy. High-severity guardrail findings must use block actions, and risk controls must meet the configured guardrail policy before persistence. They are not validated physics truth, controller-parameter promotion, or facility safety approval. run_provider_quorum() runs multiple providers in local-first order and admits only hypotheses corroborated by the required provider count over the same gap and evidence set. PhysicsDebugSafetyPolicy binds the human-review requirement, caps advisory confidence, and rejects provider text that attempts controller promotion, actuation, review bypass, or approval claims.

ProviderPolicy dataclass

ProviderPolicy(allowed_endpoint_prefixes=('http://127.0.0.1', 'http://localhost', 'https://127.0.0.1', 'https://localhost'), allow_remote_providers=False, max_prompt_chars=20000, max_response_chars=50000)

Security policy for model-provider access.

PhysicsDebugGuardrailPolicy dataclass

PhysicsDebugGuardrailPolicy(required=False, block_actions=('block',), allowed_decisions=('allow', 'block'), blocking_severities=('high', 'critical'), minimum_risk_controls=1)

Admission policy for optional physics-debug hallucination guardrails.

payload

payload()

Return a JSON-safe guardrail policy payload.

PhysicsDebugEvidence dataclass

PhysicsDebugEvidence(evidence_id, evidence_type, source, summary, sha256=None)

Evidence item supplied to the physics debugging assistant.

PhysicsDebugGap dataclass

PhysicsDebugGap(gap_id, severity, description, evidence_ids)

Physics validation gap to analyse.

PhysicsDebugSafetyPolicy dataclass

PhysicsDebugSafetyPolicy(human_review_required=True, max_advisory_confidence=0.95, forbidden_action_phrases=DEFAULT_FORBIDDEN_ACTION_PHRASES)

Fail-closed admission policy for advisory physics-debug output.

payload

payload()

Return a JSON-safe policy payload for report binding.

HTTPChatProvider dataclass

HTTPChatProvider(provider_name, endpoint, model, protocol='chat-completions', headers=dict(), timeout_seconds=30.0, transport=None)

Allowlisted HTTP chat gateway for local or facility-approved providers.

local_onsite property

local_onsite

Return whether the endpoint is local or onsite-loopback by construction.

metadata

metadata()

Return non-secret provider metadata for reports.

complete_json

complete_json(prompt, policy)

Return a JSON object from the configured provider gateway.

PhysicsDebugGuardrailProvider dataclass

PhysicsDebugGuardrailProvider(provider_name, endpoint, model, profile='director-ai', protocol='direct-json', headers=dict(), timeout_seconds=30.0, transport=None)

Allowlisted guardrail gateway for advisory physics-debug reports.

local_onsite property

local_onsite

Return whether the guardrail endpoint is local by construction.

metadata

metadata()

Return non-secret guardrail metadata for report binding.

review

review(*, prompt, evidence, gaps, provider_metadata, provider_output, policy, safety_policy, guardrail_policy)

Return a validated guardrail review for a provider draft.

PhysicsDebugAssistant dataclass

PhysicsDebugAssistant(policy=ProviderPolicy(), safety_policy=PhysicsDebugSafetyPolicy(), guardrail_policy=PhysicsDebugGuardrailPolicy())

Evidence-first assistant for physics gap analysis.

analyze

analyze(*, evidence, gaps, provider, guardrail_provider=None)

Ask an allowlisted provider for advisory hypotheses and campaigns.

build_local_provider

build_local_provider(*, family, model, provider_name, host='127.0.0.1', port=None, path=None, transport=None, headers=None, timeout_seconds=30.0)

Build a loopback-only provider profile for onsite model gateways.

build_guardrail_provider

build_guardrail_provider(*, model, provider_name, profile='director-ai', host='127.0.0.1', port=None, path=None, transport=None, headers=None, timeout_seconds=30.0)

Build a loopback-only guardrail profile for physics debug reviews.

build_physics_debug_report

build_physics_debug_report(*, provider, evidence, gaps, provider_output, redactions=None, safety_policy=None, prompt_guard_findings=None, guardrail_policy=None, guardrail=None)

Build a schema-versioned advisory physics debugging report.

run_provider_quorum

run_provider_quorum(*, evidence, gaps, providers, policy=None, safety_policy=None, guardrail_policy=None, guardrail_provider=None, min_providers=2, min_local_providers=1)

Run multiple providers and admit only corroborated advisory evidence.

validate_physics_debug_report

validate_physics_debug_report(payload)

Validate a tamper-evident advisory physics debugging report.

validate_physics_debug_quorum_report

validate_physics_debug_quorum_report(payload)

Validate a tamper-evident provider-quorum physics debug report.

write_physics_debug_report

write_physics_debug_report(payload, json_path)

Persist a validated advisory physics debugging report as JSON.

write_physics_debug_quorum_report

write_physics_debug_quorum_report(payload, json_path)

Persist a validated provider-quorum physics debug report as JSON.


Control — Federated Disruption Prediction

scpn_control.control.federated_disruption supports FedAvg and FedProx training across named tokamak clients without centralising facility arrays. create_facility_clients_from_arrays() is the production ingestion boundary for per-facility X_train, y_train, X_test, and y_test arrays. It enforces the shared 8-feature disruption contract and binary labels before a client joins the federation.

DifferentialPrivacyConfig enables facility-update clipping, Gaussian noise, and a serialisable privacy ledger. The shipped benchmark validation/benchmark_federated_disruption.py publishes deterministic synthetic multi-facility evidence in validation/reports/federated_disruption_benchmark.json and validation/reports/federated_disruption_benchmark.md. Those artefacts test federation, heterogeneity, and differential privacy contracts; they do not claim measured cross-facility validation without external shot databases.

DifferentialPrivacyConfig dataclass

DifferentialPrivacyConfig(max_update_norm=1.0, noise_multiplier=1.0, delta=1e-05, seed=20240531)

Facility-level differential privacy for federated client updates.

PrivacyLedgerEntry dataclass

PrivacyLedgerEntry(round_index, participating_clients, epsilon_spent, cumulative_epsilon, delta, max_update_norm, noise_multiplier, clipped_clients)

Per-round facility-level privacy accounting record.

FacilityBenchmarkSummary dataclass

FacilityBenchmarkSummary(aggregation, machines, n_rounds, mean_accuracy, mean_loss, per_machine_accuracy, privacy_epsilon, privacy_delta, evidence_kind)

Deterministic benchmark summary for a federated disruption campaign.

FederatedConfig dataclass

FederatedConfig(n_rounds=10, local_epochs=5, learning_rate=0.01, aggregation='fedavg', mu_proximal=0.01, min_clients=2, machines=(lambda: ['DIII-D', 'JET', 'KSTAR'])(), dp_config=None)

Configuration for federated disruption prediction training.

MachineClient

MachineClient(machine, X_train, y_train, X_test, y_test, learning_rate=0.01)

Local training client for a single tokamak.

get_data_size

get_data_size()

Return the number of local training samples held by this federated client.

local_train

local_train(global_weights, n_epochs, mu_proximal=0.0)

SGD on local data, starting from global_weights.

When mu_proximal > 0, adds the FedProx penalty (mu/2)||w - w_global||^2 to the loss gradient.

local_evaluate

local_evaluate(weights)

Binary classification metrics on local test set.

FederatedServer

FederatedServer(config, seed=42)

Orchestrates federated training across machine clients.

aggregate

aggregate(client_updates)

FedAvg: weighted average of model weights by dataset size.

McMahan et al., "Communication-Efficient Learning of Deep Networks from Decentralized Data", AISTATS 2017.

fedprox_aggregate

fedprox_aggregate(client_updates, global_weights, mu)

FedProx aggregation with proximal regularisation.

The proximal term is applied during local training (not aggregation), so aggregation itself is weighted averaging — the difference from FedAvg is in the client-side gradient update. This method exists for API symmetry; the mu parameter documents the proximal weight used.

run_round

run_round(clients)

Single federated round: distribute → local train → aggregate.

train

train(clients, n_rounds=None)

Full federated training loop.

Returns per-round metrics including per-client accuracy, loss, n_samples.

privacy_summary

privacy_summary()

Return cumulative facility-level DP spend for the current server.

get_state

get_state()

Return a serialisable snapshot of server state.

from_state classmethod

from_state(state)

Reconstruct server from serialised state.

create_facility_clients_from_arrays

create_facility_clients_from_arrays(datasets, *, learning_rate=0.01)

Create clients from per-facility arrays without centralising raw shots.

Each facility payload must contain X_train, y_train, X_test, and y_test. The constructor enforces the shared 8-feature disruption contract and binary label boundary before the data can enter a federation.

run_synthetic_multifacility_benchmark

run_synthetic_multifacility_benchmark(*, machines=('DIII-D', 'JET', 'KSTAR', 'EAST'), n_rounds=4, local_epochs=3, aggregation='fedprox', dp_config=None, seed=20240531)

Run a deterministic synthetic multi-facility disruption benchmark.

This benchmark exercises the production federation, heterogeneity, and privacy-accounting contracts. It is not measured cross-facility validation.


Control — Quantum Disruption Bridge

scpn_control.control.quantum_disruption_bridge is a fail-closed facade for optional quantum-enhanced disruption prediction. Quantum circuit and provider ownership stays in scpn-quantum-control; SCPN-CONTROL owns the control feature contract, lazy optional import boundary, bounded claim metadata, and tamper-evident advisory reports. The bridge maps the CONTROL 8-feature disruption vector to the ITER 11-feature contract only when missing ITER fields are either supplied explicitly or declared as bounded centre defaults. Reports are not facility validation, controller promotion, or publication-safe evidence without external disruption databases and benchmark artefacts.

quantum_disruption_kernel_matrix() emits a bounded amplitude-encoding kernel report with symmetry, diagonal, and [0, 1] admission checks. The callable quantum owner path uses scpn_quantum_control.control.q_disruption_iter lazily; when that optional dependency is unavailable the report fails closed with status="quantum-unavailable" and no quantum score. Every bridge report also records advisory admission evidence: CONTROL feature digests, ITER mapping digests, default-use reasons, and the external evidence still required before facility or publication claims are admissible. Bridge and kernel reports carry schema-versioned advisory certificates that bind report kind, repository ownership, claim boundary, downstream non-admission policy, and the content digest before the outer payload digest is accepted. The facade also publishes a machine-readable dependency contract that names the scpn-quantum-control backend module, required classifier surface, Qiskit core dependencies, optional provider dependency families, report schemas, feature contract, and non-admission policy for future backend hardening. Generated bridge and kernel reports embed that dependency contract and bind its digest into the advisory certificate so archived reports cannot be replayed against a different quantum backend contract. When the optional backend exposes its own scpn_control_bridge_dependency_contract() callable, CONTROL compares it against the expected contract, records backend-contract attestation evidence, and fails closed before report creation if the backend advertises a conflicting contract. Bridge reports also include schema-versioned advisory decision evidence that records whether the score came from the quantum backend or the classical fallback, applies deterministic low/elevated/high risk-band thresholds, records backend-contract validation state, fixes the control action to blocked, and binds the decision digest into the advisory certificate.

QuantumDisruptionBridgeConfig dataclass

QuantumDisruptionBridgeConfig(allow_center_defaults=False, require_quantum_backend=False, seed=20240531, backend_profile='statevector', quantum_module=QUANTUM_MODULE, claim_status='bounded_model', source_mode='control-facade')

Configuration for the optional quantum disruption bridge.

QuantumFeatureMapping dataclass

QuantumFeatureMapping(raw_iter_features, normalized_iter_features, control_feature_names, iter_feature_names, defaults_used=tuple(), unmapped_control_features=('dBp_dt', 'n1_rms'), claim_status='bounded_model', publication_safe=False)

CONTROL-to-ITER feature mapping with provenance.

payload

payload()

Return a JSON-safe mapping payload.

map_control_features_to_iter

map_control_features_to_iter(control_features, *, extra_iter_features=None, config=None)

Map the CONTROL 8-feature disruption contract to the ITER 11-feature contract.

quantum_disruption_kernel_matrix

quantum_disruption_kernel_matrix(samples_a, samples_b=None, *, config=None)

Return a bounded amplitude-encoding kernel report for CONTROL feature samples.

quantum_disruption_dependency_contract

quantum_disruption_dependency_contract()

Return the CONTROL-to-QUANTUM disruption bridge dependency contract.

run_quantum_disruption_bridge

run_quantum_disruption_bridge(control_features, *, extra_iter_features=None, config=None)

Run the optional quantum disruption bridge and return an advisory report.

validate_quantum_disruption_dependency_contract

validate_quantum_disruption_dependency_contract(payload)

Validate the CONTROL-to-QUANTUM disruption bridge dependency contract.

validate_quantum_disruption_bridge_report

validate_quantum_disruption_bridge_report(payload)

Validate a tamper-evident quantum disruption bridge report.

validate_quantum_disruption_kernel_report

validate_quantum_disruption_kernel_report(payload)

Validate a tamper-evident quantum disruption kernel report.


Core — Physics Solvers

FusionKernel

FusionKernel validates its JSON configuration before grid construction: the root must be an object, duplicate JSON keys are rejected, dimensions and grid resolution must be physical, physics.plasma_current_target must be positive finite, and physics.vacuum_permeability must be positive finite when supplied.

FusionKernel

FusionKernel(config_path)

Non-linear Grad-Shafranov equilibrium solver.

Parameters

config_path : str | Path Path to a JSON configuration file describing the reactor geometry, coil set, physics parameters and solver settings.

Attributes

Psi : FloatArray Poloidal flux on the (Z, R) grid. J_phi : FloatArray Toroidal current density on the (Z, R) grid. B_R, B_Z : FloatArray Radial and vertical magnetic field components (set after solve).

load_config

load_config(path)

Load reactor configuration from a JSON file.

Parameters

path : str | Path Filesystem path to the configuration JSON.

initialize_grid

initialize_grid()

Build the computational (R, Z) mesh from the loaded config.

setup_accelerator

setup_accelerator()

Initialise the optional C++ HPC acceleration bridge.

build_coilset_from_config

build_coilset_from_config()

Build a free-boundary CoilSet from the active JSON configuration.

solve

solve(*, boundary_variant=None, coils=None, preserve_initial_state=False, boundary_flux=None, max_outer_iter=20, tol=0.0001, optimize_shape=False, tikhonov_alpha=0.0001)

Solve using the requested boundary variant.

The default is taken from solver.boundary_variant in the active config.

solve_fixed_boundary

solve_fixed_boundary(preserve_initial_state=False, boundary_flux=None)

Explicit entry point for the fixed-boundary variant.

calculate_vacuum_field

calculate_vacuum_field()

Compute the vacuum poloidal flux from the external coil set.

Uses elliptic integrals (toroidal Green's function) for each coil.

Returns

FloatArray Vacuum flux Psi_vac on the (NZ, NR) grid.

find_x_point

find_x_point(Psi)

Locate the X-point (magnetic null) in the lower divertor region.

Parameters

Psi : FloatArray Poloidal flux array on the (NZ, NR) grid.

Returns

tuple[tuple[float, float], float] ((R_x, Z_x), Psi_x) — position and flux value at the X-point.

mtanh_profile staticmethod

mtanh_profile(psi_norm, params)

Evaluate a modified-tanh pedestal profile (vectorised).

Parameters

psi_norm : FloatArray Normalised poloidal flux (0 at axis, 1 at separatrix). params : dict[str, float] Profile shape parameters with keys ped_top, ped_width, ped_height, core_alpha.

Returns

FloatArray Profile value; zero outside the plasma region.

mtanh_profile_derivative staticmethod

mtanh_profile_derivative(psi_norm, params)

Evaluate d(mtanh_profile)/dpsi_norm for H-mode Newton linearisation.

update_plasma_source_nonlinear

update_plasma_source_nonlinear(Psi_axis, Psi_boundary)

Compute the toroidal current density J_phi from the GS source.

Uses J_phi = R p'(psi) + FF'(psi) / (mu0 R) with either L-mode (linear) or H-mode (mtanh) profiles, then renormalises to match the target plasma current.

Parameters

Psi_axis : float Poloidal flux at the magnetic axis (O-point). Psi_boundary : float Poloidal flux at the separatrix (X-point or limiter).

Returns

FloatArray Updated J_phi on the (NZ, NR) grid.

solve_equilibrium

solve_equilibrium(preserve_initial_state=False, boundary_flux=None)

Run the full Picard-iteration equilibrium solver.

Iterates: topology analysis -> source update -> elliptic solve -> under-relaxation until the residual drops below the configured convergence threshold or the maximum iteration count is reached.

When solver.solver_method is "anderson", Anderson acceleration is applied every few Picard steps to speed up convergence.

Returns

dict[str, Any] {"psi": FloatArray, "converged": bool, "iterations": int, "residual": float, "residual_history": list[float], "gs_residual": float, "gs_residual_best": float, "gs_residual_history": list[float], "wall_time_s": float, "solver_method": str}

Parameters

preserve_initial_state : bool, optional Keep current interior self.Psi values and only enforce boundary conditions before the iterative solve. Default is False. boundary_flux : FloatArray | None, optional Explicit boundary map to enforce during solve. Must have shape (NZ, NR) when provided.

Raises

RuntimeError If the solver produces NaN or Inf and solver.fail_on_diverge is enabled in the active configuration.

compute_b_field

compute_b_field()

Derive the magnetic field components from the solved Psi.

optimize_coil_currents

optimize_coil_currents(coils, target_flux, tikhonov_alpha=0.0001, *, x_point_flux_target=None, divertor_flux_targets=None)

Find coil currents that best satisfy free-boundary target constraints.

Solves a bounded linear least-squares problem that can include:

  • boundary-flux targets at target_flux_points
  • X-point isoflux and null-field constraints
  • divertor strike-point isoflux constraints

    min_I || A I - b ||^2 + alpha * ||I||^2 s.t. -I_max <= I <= I_max (per coil)

where A stacks the active constraint blocks.

Parameters

coils : CoilSet Coil geometry and optional objective targets. target_flux : FloatArray, shape (n_pts,) Desired poloidal flux at target_flux_points. Can be empty when only X-point and/or divertor constraints are active. tikhonov_alpha : float Regularisation strength to penalise large currents. x_point_flux_target : float or None Scalar target flux at coils.x_point_target. divertor_flux_targets : FloatArray or None Flux targets at coils.divertor_strike_points.

Returns

FloatArray, shape (n_coils,) Optimised coil currents [A].

solve_free_boundary

solve_free_boundary(coils, max_outer_iter=20, tol=0.0001, optimize_shape=False, tikhonov_alpha=0.0001, objective_tolerances=None)

Experimental external-coil outer loop around the fixed-boundary GS solve.

Iterates between updating boundary flux from coils and solving the internal GS equation. When optimize_shape=True and the coil set has target_flux_points, an additional outer loop optimises the coil currents to match the desired plasma boundary shape.

This helper should not be interpreted as a complete production-grade free-boundary solver. The current project standard for closing the free-boundary roadmap item is higher: shape control, X-point geometry, and divertor-configuration support must all be demonstrated.

Parameters

coils : CoilSet External coil set. max_outer_iter : int Maximum outer-loop iterations for the experimental coil-coupled path. tol : float Convergence tolerance on max |delta psi|. optimize_shape : bool When True, run coil-current optimisation at each outer step. tikhonov_alpha : float Tikhonov regularisation for coil optimisation. objective_tolerances : dict or None Optional convergence gates for free-boundary target objectives. Supported keys are shape_rms, shape_max_abs, x_point_position, x_point_gradient, x_point_flux, divertor_rms, and divertor_max_abs. When omitted, the method falls back to free_boundary.objective_tolerances in the config, if present.

Returns

dict {"outer_iterations": int, "final_diff": float, "coil_currents": AnyFloatArray}

phase_sync_step

phase_sync_step(theta, omega, *, dt=0.001, K=None, alpha=None, zeta=None, psi_driver=None, psi_mode=None, actuation_gain=None)

Reduced-order plasma sync kernel (phase reduction).

dθ_i/dt = ω_i + K·R·sin(ψ_r − θ_i − α) + ζ·sin(Ψ − θ_i)

Ψ is exogenous when psi_mode="external" (no dotΨ equation). This is the reviewer's ζ sin(Ψ−θ) injection for plasma sync stability.

phase_sync_step_lyapunov

phase_sync_step_lyapunov(theta, omega, *, n_steps=100, dt=0.001, K=None, zeta=None, psi_driver=None, psi_mode=None)

Multi-step phase sync with Lyapunov stability tracking.

Returns final state, R trajectory, V trajectory, and λ exponent. λ < 0 ⟹ stable convergence toward Ψ.

save_results

save_results(filename='equilibrium_nonlinear.npz')

Save the equilibrium state to a compressed NumPy archive.

Parameters

filename : str Output file path.

Global Design Scanner

GlobalDesignExplorer provides the bounded scalar design metrics consumed by the disruption-contract episode path. It estimates Q, fusion power, neutron wall load, and a relative cost proxy from validated tokamak design inputs.

GlobalDesignExplorer

GlobalDesignExplorer(config=None)

Evaluate bounded tokamak design points for control-contract episodes.

The explorer provides the small production surface required by run_disruption_episode. It is a deterministic bounded model, not a replacement for a systems-code design scan or neutronics campaign.

Parameters

config Preset name or scalar configuration mapping. Preset names currently select the default bounded model and are retained for compatibility with older callers that passed labels such as "dummy".

evaluate_design

evaluate_design(r_maj, b_t, ip)

Evaluate one tokamak design point.

Parameters

r_maj Major radius in metres. b_t Toroidal magnetic field in tesla. ip Plasma current in mega-amperes.

Returns

dict[str, float] Bounded design metrics: Q, P_fusion_MW, neutron_wall_load_MW_m2, cost_index, minor_radius_m, volume_m3, greenwald_fraction, and beta_p_proxy.

DesignScannerConfig dataclass

DesignScannerConfig(aspect_ratio=_DEFAULT_ASPECT_RATIO, elongation=_DEFAULT_ELONGATION, density_fraction=_DEFAULT_DENSITY_FRACTION, confinement_multiplier=_DEFAULT_CONFINEMENT_MULTIPLIER, auxiliary_power_mw=_DEFAULT_AUXILIARY_POWER_MW)

Configuration constants for GlobalDesignExplorer.

Parameters

aspect_ratio Major-radius to minor-radius ratio used to infer the plasma minor radius. elongation Plasma elongation for volume and neutron-wall-load estimates. density_fraction Fraction of the Greenwald density used for the bounded power estimate. confinement_multiplier Multiplier applied to the IPB98-like confinement-time proxy. auxiliary_power_mw External heating and current-drive power used in the Q proxy.

from_mapping classmethod

from_mapping(config)

Build a scanner configuration from scalar overrides.

TokamakConfig

TokamakConfig dataclass

TokamakConfig(name, R0, a, B0, Ip, kappa, delta, n_e, T_e, P_aux)

Tokamak equilibrium operating point.

Units: lengths [m], field [T], current [MA], density [1e19 m^-3], temperature [keV], power [MW].

aspect_ratio property

aspect_ratio

Aspect ratio R0 / a (major over minor radius).

epsilon property

epsilon

Inverse aspect ratio a / R0.

model_json_schema classmethod

model_json_schema()

Return the JSON Schema for serialized tokamak configurations.

iter classmethod

iter()

Return the ITER baseline H-mode configuration (ITER Research Plan 2018).

sparc classmethod

sparc()

SPARC V2C. Creely et al., J. Plasma Phys. 86, 865860502 (2020).

diiid classmethod

diiid()

DIII-D typical H-mode. Luxon, Nucl. Fusion 42, 614 (2002).

jet classmethod

jet()

JET with ITER-like wall. Joffrin et al., Nucl. Fusion 59 (2019).

TransportSolver

TransportSolver

TransportSolver(config_path, *, nr=50, multi_ion=False, transport_model='gyro_bohm', external_gk_allow_gyrobohm_fallback=False, tglf_native_allow_gyrobohm_fallback=False, allow_constant_transport_fallback=False, allow_simplified_bootstrap_fallback=False, allow_legacy_approximations=False)

Bases: FusionKernel

1.5D Integrated Transport Code.

Solves Heat and Particle diffusion equations on flux surfaces, coupled self-consistently with the 2D Grad-Shafranov equilibrium.

When multi_ion=True, the solver evolves separate D/T fuel densities, He-ash transport with pumping (configurable tau_He), independent electron temperature Te, coronal-equilibrium tungsten radiation (Pütterich et al. 2010), and per-cell Bremsstrahlung.

energy_balance_error property

energy_balance_error

Relative energy conservation error from the last evolution step.

particle_balance_error property

particle_balance_error

Relative particle conservation error from the last evolution step.

Only meaningful in multi-ion mode. Returns 0.0 otherwise.

set_neoclassical

set_neoclassical(R0, a, B0, A_ion=2.0, Z_eff=1.5, q0=1.0, q_edge=3.0)

Configure Chang-Hinton neoclassical transport model.

When set, update_transport_model uses the Chang-Hinton formula instead of the constant chi_base = 0.5.

chang_hinton_chi_profile

chang_hinton_chi_profile()

Backward-compatible Chang-Hinton profile helper.

Older parity tests call this no-arg method on a partially-initialized transport object. Keep the method as a thin adapter over the module function so those tests remain stable.

inject_impurities

inject_impurities(flux_from_wall_per_sec, dt)

Models impurity influx from PWI erosion.

Simple diffusion model: Source at edge, diffuses inward.

calculate_bootstrap_current

calculate_bootstrap_current(R0, B_pol)

Calculate bootstrap current using full Sauter closure by default.

If neoclassical configuration is missing, this method fails closed unless allow_simplified_bootstrap_fallback=True was explicitly enabled.

update_transport_model

update_transport_model(P_aux)

Gyro-Bohm + neoclassical transport model with EPED-like pedestal.

When neoclassical params are set, uses: - Chang-Hinton neoclassical chi as additive floor - Gyro-Bohm anomalous transport (calibrated c_gB) - EPED-like pedestal model for H-mode boundary condition

Fails closed when neoclassical parameters are not configured, unless allow_constant_transport_fallback=True is explicitly enabled.

evolve_profiles

evolve_profiles(dt, P_aux, enforce_conservation=False, ped_te=None, ped_ti=None)

Advance Ti by one time step using Crank-Nicolson implicit diffusion.

The scheme is unconditionally stable, allowing dt up to ~1.0 s without NaN. The full equation solved is:

(T^{n+1} - T^n)/dt = 0.5*[L_h(T^{n+1}) + L_h(T^n)] + S - Sink
Parameters

dt : float Time step [s]. P_aux : float Auxiliary heating power [MW]. enforce_conservation : bool When True, raise :class:PhysicsError if the per-step energy conservation error exceeds 1%. ped_te : PedestalProfile, optional Pedestal profile model for electron temperature. ped_ti : PedestalProfile, optional Pedestal profile model for ion temperature.

map_profiles_to_2d

map_profiles_to_2d()

Project the 1D radial profiles back onto the 2D Grad-Shafranov grid, including neoclassical bootstrap current.

compute_confinement_time

compute_confinement_time(P_loss_MW)

Compute the energy confinement time from stored energy.

τ_E = W_stored / P_loss, where W_stored = ∫ 3/2 n (Ti+Te) dV and the volume element is estimated from the 1D radial profiles using cylindrical approximation.

Parameters

P_loss_MW : float Total loss power [MW]. Must be > 0.

Returns

float Energy confinement time [s].

run_self_consistent

run_self_consistent(P_aux, n_inner=100, n_outer=10, dt=0.01, psi_tol=0.001)

Run self-consistent GS <-> transport iteration.

This implements the standard integrated-modelling loop used by codes such as ASTRA and JINTRAC: evolve the 1D transport for n_inner steps, project profiles onto the 2D grid, re-solve the Grad-Shafranov equilibrium, and repeat until the poloidal-flux change drops below psi_tol.

Algorithm
  1. Run transport for n_inner steps (evolve Ti/Te/ne).
  2. Call :meth:map_profiles_to_2d to update J_phi on the 2D grid.
  3. Re-solve the Grad-Shafranov equilibrium with the updated source.
  4. Check psi convergence: ||Psi_new - Psi_old|| / ||Psi_old|| < psi_tol.
  5. Repeat until converged or n_outer iterations exhausted.
Parameters

P_aux : float Auxiliary heating power [MW]. n_inner : int Number of transport evolution steps per outer iteration. n_outer : int Maximum number of outer (GS re-solve) iterations. dt : float Transport time step [s]. psi_tol : float Relative psi convergence tolerance.

Returns

dict {"T_avg": float, "T_core": float, "tau_e": float, "n_outer_converged": int, "psi_residuals": list[float], "Ti_profile": ndarray, "ne_profile": ndarray, "converged": bool}

run_to_steady_state

run_to_steady_state(P_aux, n_steps=500, dt=0.01, adaptive=False, tol=0.001, self_consistent=False, sc_n_inner=100, sc_n_outer=10, sc_psi_tol=0.001)

Run transport evolution until approximate steady state.

Parameters

P_aux : float Auxiliary heating power [MW]. n_steps : int Number of evolution steps. dt : float Time step [s] (initial value when adaptive=True). adaptive : bool Use Richardson-extrapolation adaptive time stepping. tol : float Error tolerance for adaptive stepping. self_consistent : bool When True, delegate to :meth:run_self_consistent which iterates GS <-> transport to convergence. The remaining sc_* parameters are forwarded. sc_n_inner : int Transport steps per outer GS iteration (self-consistent mode). sc_n_outer : int Maximum outer GS iterations (self-consistent mode). sc_psi_tol : float Relative psi convergence tolerance (self-consistent mode).

Returns

dict {"T_avg": float, "T_core": float, "tau_e": float, "n_steps": int, "Ti_profile": ndarray, "ne_profile": ndarray} When adaptive=True, also includes dt_final, dt_history, error_history. When self_consistent=True, returns the :meth:run_self_consistent dict instead.

Plasma Power Terms

bosch_hale_dt_reactivity

bosch_hale_dt_reactivity(T_keV)

Compute the D-T fusion reactivity [m^3/s].

Uses the NRL Plasma Formulary fit (Bosch & Hale 1992), valid for 0.2 < T < 100 keV; the temperature is floored at 0.2 keV.

tungsten_radiation_rate

tungsten_radiation_rate(Te_keV)

Compute the coronal-equilibrium radiation rate coefficient L_z(Te) for tungsten [W·m^3].

Piecewise power-law fit to Pütterich et al. (2010) / ADAS data:

  • Te < 1 keV: L_z ~ 5e-31 * Te^0.5 (line radiation dominant)
  • 1 <= Te < 5: L_z ~ 5e-31 (plateau, shell opening)
  • 5 <= Te < 20: L_z ~ 2e-31 * Te^0.3 (rising continuum)
  • Te >= 20: L_z ~ 8e-31 (fully ionised Bremsstrahlung)

bremsstrahlung_power_density

bremsstrahlung_power_density(ne_1e19, Te_keV, Z_eff)

Compute the bremsstrahlung power density [W/m^3].

P_brem = 5.35e-37 * Z_eff * ne^2 * sqrt(Te) with ne in m^-3 and Te in keV (NRL Plasma Formulary 2019, p. 58).

Radial Diffusion Numerics

thomas_solve

thomas_solve(a, b, c, d)

Solve a tridiagonal system in O(n) with the Thomas algorithm.

Solves A x = d where A is tridiagonal with sub-diagonal a, main diagonal b, and super-diagonal c. Near-zero or non-finite pivots are floored so a degenerate row cannot propagate NaNs.

Parameters

a : array, length n-1 Sub-diagonal. b : array, length n Main diagonal. c : array, length n-1 Super-diagonal. d : array, length n Right-hand side.

Returns

x : array, length n Solution vector.

explicit_diffusion_rhs

explicit_diffusion_rhs(T, chi, rho, drho, a_minor)

Compute the explicit cylindrical diffusion operator L_h(T).

L_h(T) = (1/a^2) * (1/rho) d/drho(rho chi dT/drho), evaluated with half-grid diffusivities and central differences on the interior. Returns an array of the same length as T (boundary points left at zero).

build_cn_tridiag

build_cn_tridiag(chi, dt, rho, drho, a_minor)

Build the Crank-Nicolson LHS tridiagonal coefficients.

The implicit system is (I - 0.5*dt*L_h) T^{n+1} = (I + 0.5*dt*L_h) T^n + dt*(S - Sink).

Returns (a, b, c) sub/main/super diagonals for the interior points, padded to full grid size (boundary conditions applied separately).

Anomalous Transport Coefficient Models

gyro_bohm_chi_profile

gyro_bohm_chi_profile(rho, Ti, Te, q, R0, a, B0, A_ion, c_gB)

Gyro-Bohm anomalous transport diffusivity profile [m^2/s].

chi_gB = c_gB * rho_s^2 * c_s / (a q R) where rho_s = sqrt(T_i m_i) / (e B) is the ion sound gyroradius and c_s = sqrt(T_e / m_i) the ion sound speed. Temperatures are floored at 0.01 keV and the safety factor at 0.5 so a degenerate edge cell cannot produce a non-finite diffusivity; every cell is floored at 0.01 m^2/s.

Parameters

rho : array Normalised radius [0, 1]. Ti : array Ion temperature [keV]. Te : array Electron temperature [keV]. q : array Safety factor profile. R0 : float Major radius [m]. a : float Minor radius [m]. B0 : float Toroidal field [T]. A_ion : float Ion mass number (2 = deuterium). c_gB : float Calibrated gyro-Bohm coefficient.

Returns

chi_gB : array Gyro-Bohm ion thermal diffusivity [m^2/s].

gk_flux_surface_transport

gk_flux_surface_transport(*, solver, rho, Te, Ti, ne, params, solver_label, catch_execution_errors, allow_gyrobohm_fallback, gyro_bohm_fallback)

Run a gyrokinetic solver on every flux surface and return (chi_i, chi_e, D).

Shared driver for the external_gk and tglf_native transport models. On each interior cell it assembles the local gradients (R/L_T, R/L_n, magnetic shear) into a :class:GKLocalParams, runs solver, and accepts the result only when it converged with finite non-negative fluxes. Otherwise it fails closed unless allow_gyrobohm_fallback is set, in which case the cell falls back to the gyro-Bohm estimate. The plasma core edge (rho <= 0.05) and vacuum cells (non-finite or vanishing density) are floored to 0.01.

Parameters

solver : GKSolverBase Gyrokinetic solver exposing run_from_params. rho, Te, Ti, ne : array Normalised radius and the electron/ion temperature [keV] and density [10^19 m^-3] profiles. params : dict Geometry and shaping parameters (R0, a, B0, q_profile, and optional Z_eff, kappa, delta). solver_label : str Name used in the fail-closed error messages ("external_gk" / "tglf_native"). catch_execution_errors : bool When True (external solvers), a raised solver exception is caught and either re-raised as a fail-closed error or absorbed by the fallback. allow_gyrobohm_fallback : bool When True, unconverged/failed cells fall back to gyro_bohm_fallback instead of raising. gyro_bohm_fallback : callable Zero-argument callable returning the gyro-Bohm chi profile, indexed per cell only when a fallback is taken.

Returns

chi_i, chi_e, D_n : array Ion and electron thermal diffusivities and the particle diffusivity [m^2/s]; the caller assigns chi_e and D_n back onto its state.

Auxiliary Heating Source Deposition

aux_heating_source_profiles

aux_heating_source_profiles(P_aux_MW, rho, ne, dV, *, profile_width, electron_fraction)

Deposit an auxiliary-heating power into ion/electron temperature sources.

The deposition profile is a Gaussian exp(-rho^2 / profile_width) normalised over the plasma volume so the reconstructed power equals P_aux_MW. From the per-cell power density the temperature source is dT/dt = (2/3) P / (n e_keV). A non-positive or non-finite power, or a degenerate volume normalisation, yields zero sources (fail-soft) with a zeroed balance record.

Parameters

P_aux_MW : float Requested total auxiliary heating power [MW]. rho : array Normalised radius [0, 1]. ne : array Electron density [10^19 m^-3]. dV : array Toroidal volume element per radial cell [m^3]. profile_width : float Gaussian deposition width parameter (floored at 1e-6). electron_fraction : float Fraction of the power deposited on electrons (clipped to [0, 1]); the remainder heats the ions.

Returns

s_heat_i, s_heat_e : array Ion and electron temperature source terms [keV/s]. balance : dict Power-balance telemetry (target vs reconstructed ion/electron/total [MW]).

Multi-Ion Species Evolution

evolve_multi_ion_species

evolve_multi_ion_species(*, n_D, n_T, n_He, Ti, Te, n_impurity, dV, drho, D_species, tau_He, dt)

Advance the D/T/He-ash densities by one time-step (explicit diffusion + sources).

The fusion, fuel-consumption, and helium-pumping source terms are evaluated once from the incoming densities and held constant across the CFL sub-steps (dt_CFL = 0.4 drho^2 / D_species). Deuterium/tritium are consumed by burn with an edge recycling floor and axis Neumann condition; helium is produced by burn and removed by pumping with time tau_He. The electron density follows from quasineutrality (D + T + 2·He + Z_W·impurity), and the effective charge and tungsten line radiation are derived from the updated state.

Parameters

n_D, n_T, n_He : array Deuterium, tritium, and helium-ash densities [10^19 m^-3]. Ti, Te : array Ion and electron temperatures [keV]. n_impurity : array Tungsten impurity density [10^19 m^-3]. dV : array Toroidal volume element per radial cell [m^3]. drho : float Normalised radial grid spacing. D_species : float Anomalous particle diffusivity [m^2/s]. tau_He : float Helium-ash pumping time [s]. dt : float Time-step [s].

Returns

SpeciesEvolutionResult The updated densities and per-step diagnostics.

SpeciesEvolutionResult dataclass

SpeciesEvolutionResult(n_D, n_T, n_He, ne, Z_eff, particle_balance_error, S_He, P_rad_line)

Outputs of one multi-ion species evolution step.

Attributes

n_D, n_T, n_He : array Updated deuterium, tritium, and helium-ash densities [10^19 m^-3]. ne : array Electron density from quasineutrality [10^19 m^-3]. Z_eff : float Volume-mean effective charge, clipped to [1, 10]. particle_balance_error : float Relative particle-inventory conservation error for this step. S_He : array Helium-ash production rate [10^19 m^-3 / s]. P_rad_line : array Tungsten line-radiation power density [W/m^3].

Runtime State Sanitization

sanitize_with_fallback

sanitize_with_fallback(arr, fallback, *, floor=None, ceil=None)

Replace non-finite entries with fallback and enforce optional bounds.

Parameters

arr : array Profile to harden; not mutated (a copy is returned). fallback : array Replacement values, taken element-wise where arr is non-finite. floor : float, optional Lower clamp applied after replacement. ceil : float, optional Upper clamp applied after replacement.

Returns

out : array The hardened profile. recovered : int Number of non-finite entries that were replaced.

Transport Radial-Grid Geometry

rho_volume_element

rho_volume_element(rho, drho, r_min, r_max)

Toroidal volume element per radial cell [m^3].

Each cell of the normalised radial grid maps to a toroidal shell of volume dV = (2 pi R0)(2 pi a^2 rho drho) where R0 is the major radius and a the minor radius derived from the machine bore.

Parameters

rho : array Normalised radial coordinate in [0, 1]. drho : float Uniform grid spacing of rho. r_min : float Inner major-radius bore of the plasma [m]. r_max : float Outer major-radius bore of the plasma [m].

Returns

array Volume element per radial cell [m^3].

estimate_plasma_surface_area_m2

estimate_plasma_surface_area_m2(R0, a, kappa)

Estimate plasma surface area for Martin L-H threshold scaling.

Approximates the last-closed-flux-surface area of an elongated torus as the product of the toroidal circumference 2 pi R0 and the poloidal perimeter of an ellipse with semi-axes a and kappa a (RMS-radius perimeter).

Parameters

R0 : float Major radius [m]; floored at 1e-6 for degenerate geometry. a : float Minor radius [m]; floored at 1e-6. kappa : float Plasma elongation; floored at 1e-6.

Returns

float Estimated plasma surface area [m^2].

is_canonical_radial_grid

is_canonical_radial_grid(rho, nr, drho)

Return True if rho/drho form a valid normalised radial grid.

A valid grid has exactly nr finite, strictly increasing points spanning [0, 1] with a finite positive spacing.

Parameters

rho : array Candidate normalised radial coordinate. nr : int Expected number of grid points. drho : float Expected grid spacing.

Returns

bool Whether the grid is canonical.

canonical_radial_grid

canonical_radial_grid(nr)

Return the canonical normalised grid and its spacing.

Parameters

nr : int Number of grid points; must be at least 2.

Returns

rho : array linspace(0, 1, nr). drho : float Uniform spacing 1 / (nr - 1).

Raises

ValueError If nr is less than 2.

Scaling Laws

ipb98y2_tau_e

ipb98y2_tau_e(Ip, BT, ne19, Ploss, R, kappa, epsilon, M=2.5, *, coefficients=None)

Evaluate the IPB98(y,2) confinement time scaling law.

Parameters

Ip : float Plasma current [MA]. BT : float Toroidal magnetic field [T]. ne19 : float Line-averaged electron density [10^19 m^-3]. Ploss : float Loss power [MW]. Must be > 0. R : float Major radius [m]. kappa : float Elongation (κ). epsilon : float Inverse aspect ratio a/R. M : float Effective ion mass [AMU]. Default 2.5 (D-T). coefficients : dict, optional Pre-loaded coefficient dict. If None, loaded from disk.

Returns

float Predicted thermal energy confinement time τ_E [s].

Raises

ValueError If any input is non-finite or non-positive.

compute_h_factor

compute_h_factor(tau_actual, tau_predicted)

Compute the H-factor (enhancement factor over scaling law).

Parameters

tau_actual : float Positive measured or simulated confinement time [s]. tau_predicted : float Positive IPB98(y,2) predicted confinement time [s].

Returns

float H98(y,2) = tau_actual / tau_predicted.

Raises

ValueError If either confinement time is non-finite or non-positive.

GEQDSK I/O

GEqdsk dataclass

GEqdsk(description='', nw=0, nh=0, rdim=0.0, zdim=0.0, rcentr=0.0, rleft=0.0, zmid=0.0, rmaxis=0.0, zmaxis=0.0, simag=0.0, sibry=0.0, bcentr=0.0, current=0.0, fpol=(lambda: array([], dtype=float64))(), pres=(lambda: array([], dtype=float64))(), ffprime=(lambda: array([], dtype=float64))(), pprime=(lambda: array([], dtype=float64))(), qpsi=(lambda: array([], dtype=float64))(), psirz=(lambda: array([], dtype=float64))(), rbdry=(lambda: array([], dtype=float64))(), zbdry=(lambda: array([], dtype=float64))(), rlim=(lambda: array([], dtype=float64))(), zlim=(lambda: array([], dtype=float64))())

Container for all data in a G-EQDSK file.

r property

r

1-D array of R grid values.

z property

z

1-D array of Z grid values.

psi_norm property

psi_norm

Normalised poloidal flux ψ_N ∈ [0, 1] (axis=0, boundary=1).

psi_to_norm

psi_to_norm(psi)

Map raw ψ to normalised ψ_N.

to_config

to_config(name='eqdsk', *, external_profiles=False)

Convert to FusionKernel JSON config dict.

Parameters

external_profiles : bool When True, include pprime/ffprime arrays so the GS solver uses the GEQDSK source profiles instead of its parametric model.

read_geqdsk

read_geqdsk(path)

Read a G-EQDSK file and return a :class:GEqdsk container.

Handles both whitespace-separated and fixed-width Fortran formats, including the common case where values run together without spaces (e.g. 2.385E+00-1.216E+01).

Parameters

path : str or Path Path to the G-EQDSK file.

Returns

GEqdsk Parsed equilibrium data.

write_geqdsk

write_geqdsk(eq, path)

Write a :class:GEqdsk to a G-EQDSK file.

Parameters

eq : GEqdsk Equilibrium data. path : str or Path Output file path.

Uncertainty Quantification

quantify_uncertainty

quantify_uncertainty(scenario, n_samples=10000, seed=None)

Monte Carlo uncertainty quantification for fusion performance.

Samples scaling-law coefficients from their Gaussian posteriors and propagates through the confinement and fusion power models.

Parameters

scenario : PlasmaScenario Plasma parameters (held fixed). n_samples : int Number of Monte Carlo samples (default 10,000). seed : int, optional Random seed for reproducibility.

Returns

UQResult — central estimates + error bars + percentiles.

quantify_full_chain

quantify_full_chain(scenario, n_samples=5000, seed=None, chi_gB_sigma=0.3, pedestal_sigma=0.2, boundary_sigma=0.02)

Full-chain Monte Carlo uncertainty propagation: equilibrium -> transport -> fusion power -> gain.

Now uses correlated sampling for IPB98 coefficients.

UQClaimEvidence dataclass

UQClaimEvidence(schema_version, source, source_id, scenario_source, prior_source, propagation_chain, sensitivity_source, model_id, seed, n_samples, tau_E_s, P_fusion_MW, Q, tau_E_sigma, P_fusion_sigma, Q_sigma, tau_E_percentiles_ordered, P_fusion_percentiles_ordered, Q_percentiles_ordered, finite_outputs, fuel_ion_fraction, dP_dn_MW_per_1e19m3, dP_dT_MW_per_keV, tau_E_relative_error, P_fusion_relative_error, Q_relative_error, sigma_relative_error, relative_tolerance, sigma_relative_tolerance, calibrated_uq_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for UQ claims.

uq_claim_evidence

uq_claim_evidence(scenario, result, *, source, source_id, scenario_source, prior_source, propagation_chain, sensitivity_source, seed, model_id='bounded_full_chain_uq', reference_tau_E=None, reference_P_fusion=None, reference_Q=None, reference_tau_E_sigma=None, relative_tolerance=0.05, sigma_relative_tolerance=0.1)

Build fail-closed evidence for bounded or calibrated UQ claims.

assert_uq_calibrated_claim_admissible

assert_uq_calibrated_claim_admissible(evidence)

Raise when UQ evidence is insufficient for a calibrated predictive claim.

save_uq_claim_evidence

save_uq_claim_evidence(evidence, path)

Persist UQ claim evidence as deterministic JSON.

JAX-Accelerated Transport Primitives

Requires pip install "scpn-control[jax]". GPU execution automatic when jaxlib has CUDA/ROCm.

thomas_solve

thomas_solve(a, b, c, d, *, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Tridiagonal solve with automatic JAX/GPU dispatch.

Parameters

a : sub-diagonal, length n-1 b : main diagonal, length n c : super-diagonal, length n-1 d : right-hand side, length n use_jax : attempt JAX backend (falls back to NumPy if unavailable)

diffusion_rhs

diffusion_rhs(T, chi, rho, drho, *, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Cylindrical diffusion operator L_h(T) with JAX/GPU dispatch.

crank_nicolson_step

crank_nicolson_step(T, chi, source, rho, drho, dt, T_edge=0.1, *, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Single Crank-Nicolson transport step with JAX/GPU dispatch.

Parameters

T : temperature profile, length n chi : diffusivity profile, length n source : net heating source, length n rho : radial grid, length n drho : grid spacing dt : timestep T_edge : edge boundary condition (Dirichlet), keV

batched_crank_nicolson

batched_crank_nicolson(T_batch, chi, source, rho, drho, dt, T_edge=0.1, *, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Batched transport step via jax.vmap for ensemble/sensitivity runs.

Parameters

T_batch : (batch, n) initial temperature profiles chi, source, rho, drho, dt, T_edge : shared across batch

Returns

T_new : (batch, n) updated profiles

Differentiable Transport Facade

Requires pip install "scpn-control[jax]" for gradient evaluation. The NumPy path is deterministic for parity checks and non-JAX deployments, but transport_loss_gradient() fails closed without JAX. transport_parameter_gradients() extends the same traced Crank-Nicolson contract to source schedules, returning JAX gradients for both transport coefficients and additive heating, fuelling, or impurity-source inputs. differentiable_transport_rollout() advances a bounded multi-step source schedule with the same four-channel boundary contract. transport_rollout_source_gradients() returns fail-closed JAX gradients for that full source schedule so controller tuning can optimise time-distributed heating, fuelling, and impurity-source inputs without finite differences. The rollout gradient path keeps the loss inside the traced JAX graph and enables JAX x64 before importing jax.numpy, so persisted dtype evidence is not silently downgraded. audit_transport_rollout_source_gradients() and assert_transport_rollout_source_gradients_consistent() compare those rollout gradients against sampled NumPy finite-difference perturbations. audit_transport_parameter_gradients() and assert_transport_parameter_gradients_consistent() compare those JAX gradients against sampled independent finite-difference perturbations before controller-tuning admission. transport_coefficients_from_neural_closure() maps bounded neural transport closure outputs into the four-channel coefficient order used by the facade: electron heat, ion heat, electron particle diffusivity, and a declared impurity diffusivity fraction. gyrokinetic_transport_closure_profiles() wraps the reduced gyrokinetic transport profile evaluator as bounded closure provenance, and transport_coefficients_from_gyrokinetic_closure() maps that closure into the same four-channel coefficient order without promoting the reduced GK model to an externally validated transport claim. transport_campaign_metadata() records backend, dtype, radial grid, timestep, boundary conditions, closure provenance, gradient tolerance, and optional equilibrium-grid shape for reproducible controller-tuning campaigns. save_transport_campaign_metadata() and load_transport_campaign_metadata() persist the same contract as schema-versioned JSON and fail closed on malformed or physically inconsistent replay metadata. assert_transport_campaign_metadata_replay() compares archived campaign metadata with a candidate setup and raises on backend, grid, boundary, closure, gradient-tolerance, or equilibrium-shape drift before controller tuning reruns. transport_differentiability_evidence() and assert_transport_differentiability_claim_admissible() add a tamper-evident admission envelope over campaign metadata and gradient-audit results. That envelope requires JAX backend evidence, passed sampled finite-difference gradient audit, stable SHA-256 digests for the campaign and audit payloads, and an optional link to the safety-critical controller proof artifact digest. Admission revalidates finite non-negative audit losses and errors, tolerance agreement with campaign metadata, unique in-domain sampled audit indices, pass/fail consistency with maximum audit error, and ordered latency percentiles before persisted controller-tuning evidence is accepted. Latency reports also bind runtime provenance for later CPU/GPU comparison: Python version, platform, machine class, JAX and jaxlib versions, default backend, visible JAX devices, and x64 state. transport_full_fidelity_readiness_evidence() and assert_transport_full_fidelity_claim_ready() add the stricter promotion gate for full-fidelity differentiable-transport claims. The gate binds campaign metadata, one-step gradient-latency evidence, rollout source-gradient latency evidence, audit digests, controller formal-proof digests, equilibrium-coupled metadata, and an admitted external reference artifact. The repository benchmark binds the canonical payload SHA-256 from validation/reports/scpn_z3_formal.json when that bounded Z3 formal report is present and passing. Without all of those inputs the claim remains bounded local differentiability evidence. equilibrium_weighted_transport_rollout_tracking_loss() extends the optional Grad-Shafranov flux-map weighting from one transport step to a full source rollout. equilibrium_weighted_transport_rollout_source_gradient() returns fail-closed JAX gradients with respect to both the source schedule and the equilibrium flux map for controller-tuning studies.

differentiable_transport_step

differentiable_transport_step(profiles, chi, sources, rho, dt, edge_values, *, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Advance four transport channels by one differentiable radial step.

Channel order is electron temperature, ion temperature, electron density, and impurity density. The radial coordinate is a strictly increasing, uniformly spaced normalised axis from core-side interior to edge. The core boundary uses the inherited zero-gradient condition, and each channel uses its supplied Dirichlet edge value.

transport_tracking_loss

transport_tracking_loss(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Return weighted one-step transport tracking loss for controller tuning.

transport_loss_gradient

transport_loss_gradient(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None)

Return the tracking loss and JAX gradient with respect to chi.

transport_parameter_gradients

transport_parameter_gradients(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None)

Return JAX gradients with respect to chi and source schedules.

This is the controller-tuning primitive for differentiable auxiliary heating, fuelling, and impurity-source schedules. It keeps the same four-channel Crank-Nicolson, source-term, core zero-gradient, and edge Dirichlet contracts as :func:differentiable_transport_step; unlike :func:transport_loss_gradient, it exposes gradients for both turbulent transport coefficients and additive source terms.

TransportParameterGradients dataclass

TransportParameterGradients(loss, chi_gradient, source_gradient)

JAX gradients of transport tracking loss for tunable transport inputs.

differentiable_transport_rollout

differentiable_transport_rollout(initial_profiles, chi, source_sequence, rho, dt, edge_values, *, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Advance a time-series of four-channel transport source schedules.

The returned array has shape (n_steps, 4, n_rho). Transport coefficients are held fixed over the rollout, while source_sequence supplies differentiable additive heating, fuelling, and impurity-source schedules at each step. This is a bounded controller-tuning primitive, not an externally validated integrated-transport campaign.

transport_rollout_tracking_loss

transport_rollout_tracking_loss(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, *, weights=None, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Return weighted multi-step transport tracking loss.

transport_rollout_source_gradients

transport_rollout_source_gradients(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, *, weights=None)

Return JAX gradients for a multi-step transport source schedule.

TransportRolloutSourceGradients dataclass

TransportRolloutSourceGradients(loss, source_gradient, final_profiles)

JAX gradients of multi-step transport loss for source schedules.

audit_transport_rollout_source_gradients

audit_transport_rollout_source_gradients(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None)

Compare JAX rollout source gradients with sampled finite differences.

assert_transport_rollout_source_gradients_consistent

assert_transport_rollout_source_gradients_consistent(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None)

Return rollout source-gradient audit evidence or fail closed.

TransportRolloutGradientAudit dataclass

TransportRolloutGradientAudit(loss, epsilon, tolerance, checked_indices, source_max_abs_error, passed)

Finite-difference audit of multi-step source-rollout gradients.

audit_transport_parameter_gradients

audit_transport_parameter_gradients(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None)

Audit JAX transport gradients against independent finite differences.

The audit is intentionally sampled rather than exhaustive so it can run in controller-tuning admission checks. It evaluates deterministic interior radial points for every channel by default and compares JAX gradients for both turbulent transport coefficients and additive source schedules against independently perturbed NumPy losses.

assert_transport_parameter_gradients_consistent

assert_transport_parameter_gradients_consistent(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None)

Return the gradient audit or fail closed when consistency is violated.

TransportGradientAudit dataclass

TransportGradientAudit(loss, epsilon, tolerance, checked_indices, chi_max_abs_error, source_max_abs_error, passed)

Finite-difference audit of differentiable transport tuning gradients.

benchmark_transport_parameter_gradient_latency

benchmark_transport_parameter_gradient_latency(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None, warmup_runs=1, timed_runs=5)

Measure audited JAX gradient-admission latency for controller tuning.

The measured path is intentionally the fail-closed admission contract: JAX gradients for transport coefficients and source schedules plus sampled independent finite-difference audit. The report is local timing evidence, not a real-time control-loop guarantee.

TransportGradientLatencyReport dataclass

TransportGradientLatencyReport(schema_version, backend, dtype, n_rho, channel_count, warmup_runs, timed_runs, p50_ms, p95_ms, max_ms, runtime_metadata, audit, claim_status)

Latency evidence for audited differentiable transport tuning gradients.

save_transport_gradient_latency_report

save_transport_gradient_latency_report(report, path)

Persist differentiable transport gradient-latency evidence as JSON.

benchmark_transport_rollout_source_gradient_latency

benchmark_transport_rollout_source_gradient_latency(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None, warmup_runs=1, timed_runs=5)

Measure audited multi-step source-rollout gradient latency.

The measured path is the controller-admission contract for source schedules: JAX rollout gradients with a sampled independent NumPy finite-difference audit. The report is local timing evidence, not a real-time control-loop or externally validated transport claim.

TransportRolloutGradientLatencyReport dataclass

TransportRolloutGradientLatencyReport(schema_version, backend, dtype, n_rho, n_steps, channel_count, warmup_runs, timed_runs, p50_ms, p95_ms, max_ms, runtime_metadata, audit, claim_status)

Latency evidence for audited multi-step source-rollout gradients.

save_transport_rollout_gradient_latency_report

save_transport_rollout_gradient_latency_report(report, path)

Persist rollout source-gradient latency evidence as JSON.

transport_coefficients_from_neural_closure

transport_coefficients_from_neural_closure(closure, *, impurity_diffusivity_fraction=1.0, chi_floor=0.0)

Map a neural transport closure into four facade coefficient channels.

The facade channel order is electron temperature, ion temperature, electron density, and impurity density. Neural transport closures provide electron heat, ion heat, and particle diffusivity profiles; the electron-density channel uses the particle diffusivity, while the impurity-density channel uses a declared bounded fraction of the same particle diffusivity until species-resolved impurity transport is externally validated.

gyrokinetic_transport_closure_profiles

gyrokinetic_transport_closure_profiles(model, rho, profiles)

Wrap a reduced gyrokinetic profile closure for controller tuning.

model must provide the existing evaluate_profile(rho, profiles) contract and return ion heat, electron heat, and particle diffusivity profiles. This adapter validates only the CONTROL boundary and does not promote the reduced GK model to an externally validated transport claim.

transport_coefficients_from_gyrokinetic_closure

transport_coefficients_from_gyrokinetic_closure(closure, *, impurity_diffusivity_fraction=1.0, chi_floor=0.0)

Map a reduced gyrokinetic closure into four facade channels.

GyrokineticTransportClosureResult dataclass

GyrokineticTransportClosureResult(chi_e, chi_i, d_e, channel_weights, source, weights_checksum)

Reduced gyrokinetic closure profiles for differentiable transport.

TransportCampaignMetadata dataclass

TransportCampaignMetadata(backend, dtype, channel_order, n_rho, rho_min, rho_max, rho_spacing, dt, core_boundary, edge_boundary, edge_values, closure_source, closure_weights_checksum, gradient_tolerance, equilibrium_grid_shape)

Validated provenance for a differentiable transport tuning campaign.

transport_campaign_metadata

transport_campaign_metadata(profiles, chi, sources, rho, dt, edge_values, *, backend, closure=None, gradient_tolerance=None, equilibrium_psi=None)

Return serialisable provenance for differentiable transport campaigns.

save_transport_campaign_metadata

save_transport_campaign_metadata(metadata, path)

Persist transport campaign metadata as schema-versioned JSON.

load_transport_campaign_metadata

load_transport_campaign_metadata(path)

Load and validate schema-versioned transport campaign metadata JSON.

assert_transport_campaign_metadata_replay

assert_transport_campaign_metadata_replay(archived, profiles, chi, sources, rho, dt, edge_values, *, backend, closure=None, gradient_tolerance=None, equilibrium_psi=None)

Validate that a candidate transport setup matches archived metadata.

This guard is intended for replaying differentiable transport tuning campaigns. It fails closed on backend, dtype, grid, timestep, boundary, closure-provenance, gradient-tolerance, or equilibrium-shape drift before a controller rerun can silently compare against a different physics setup.

TransportDifferentiabilityEvidence dataclass

TransportDifferentiabilityEvidence(schema_version, backend, campaign_sha256, gradient_audit_sha256, gradient_tolerance, audit_kind, audit_passed, n_rho, channel_order, equilibrium_coupled, controller_formal_artifact_sha256, claim_status)

Tamper-evident admission evidence for differentiable transport gradients.

transport_differentiability_evidence

transport_differentiability_evidence(metadata, audit, *, controller_formal_artifact_sha256=None)

Build tamper-evident evidence for audited differentiable transport.

assert_transport_differentiability_claim_admissible

assert_transport_differentiability_claim_admissible(evidence, metadata, audit)

Fail closed unless differentiable transport evidence matches inputs.

TransportFullFidelityReadinessEvidence dataclass

TransportFullFidelityReadinessEvidence(schema_version, backend, campaign_sha256, gradient_latency_report_sha256, gradient_audit_sha256, rollout_latency_report_sha256, rollout_audit_sha256, external_reference_artifact_sha256, external_reference_admitted, controller_formal_artifact_sha256, n_rho, rollout_steps, channel_order, equilibrium_coupled, full_fidelity_claim_admissible, blocked_reasons, claim_status)

Fail-closed readiness evidence for full-fidelity transport claims.

transport_full_fidelity_readiness_evidence

transport_full_fidelity_readiness_evidence(metadata, gradient_report, *, rollout_report=None, external_reference_artifact_sha256=None, external_reference_admitted=False, controller_formal_artifact_sha256=None)

Build fail-closed readiness evidence for full-fidelity transport claims.

Local gradient audits and timing reports are necessary but not sufficient for a full-fidelity claim. This certificate requires a JAX campaign, equilibrium coupling, one-step and rollout audit reports, a controller proof digest, and an independently admitted external reference artefact.

assert_transport_full_fidelity_claim_ready

assert_transport_full_fidelity_claim_ready(evidence, metadata, gradient_report, *, rollout_report=None)

Fail closed unless readiness evidence admits a full-fidelity claim.

equilibrium_radial_weights

equilibrium_radial_weights(equilibrium_psi, n_rho)

Return positive mean-one radial weights from a Grad-Shafranov flux map.

equilibrium_weighted_transport_tracking_loss

equilibrium_weighted_transport_tracking_loss(profiles, chi, sources, target_profiles, rho, dt, edge_values, equilibrium_psi, *, weights=None, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Return transport tracking loss weighted by GS-equilibrium flux geometry.

equilibrium_weighted_transport_loss_gradient

equilibrium_weighted_transport_loss_gradient(profiles, chi, sources, target_profiles, rho, dt, edge_values, equilibrium_psi, *, weights=None)

Return JAX gradients of equilibrium-weighted transport loss.

The returned gradients are with respect to the transport coefficients and the supplied equilibrium flux map. If the flux map was produced inside an outer JAX graph by the Grad-Shafranov solver, this loss is compatible with further chain-rule propagation through that equilibrium solve.

EquilibriumWeightedTransportGradient dataclass

EquilibriumWeightedTransportGradient(loss, chi_gradient, equilibrium_gradient, radial_weights)

JAX gradient of equilibrium-weighted transport tracking loss.

equilibrium_weighted_transport_rollout_tracking_loss

equilibrium_weighted_transport_rollout_tracking_loss(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, equilibrium_psi, *, weights=None, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Return multi-step transport rollout loss weighted by GS flux geometry.

equilibrium_weighted_transport_rollout_source_gradient

equilibrium_weighted_transport_rollout_source_gradient(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, equilibrium_psi, *, weights=None)

Return JAX gradients of GS-weighted rollout loss.

The returned gradients are with respect to the full source schedule and the supplied equilibrium flux map. If the flux map was produced inside an outer JAX graph by the Grad-Shafranov solver, this loss is compatible with chain-rule propagation through that equilibrium solve.

EquilibriumWeightedTransportRolloutGradient dataclass

EquilibriumWeightedTransportRolloutGradient(loss, source_gradient, equilibrium_gradient, radial_weights, final_profiles)

JAX gradient of equilibrium-weighted multi-step transport rollout loss.

End-to-End Differentiable Scenario

differentiable_scenario_gradient() couples a bounded analytic Solov'ev-form equilibrium parametrisation to the differentiable transport rollout, so a controller-tuning loss is differentiable with respect to both the source schedule and the equilibrium shape parameters. The flux parametrisation is a differentiable equilibrium surface, not a Grad-Shafranov PDE solve. Gradient APIs fail closed without JAX, and assert_scenario_claim_admissible() keeps a full-fidelity claim bounded until the gradient audit, latency evidence, and physics-traceability checks pass. Persisted readiness reports are validated with validation.validate_differentiable_scenario.validate_differentiable_scenario_report(); the checked-in report remains blocked on physics traceability rather than promoting the analytic surface to a facility-grade scenario claim.

scenario_equilibrium_flux

scenario_equilibrium_flux(equilibrium_params, r_grid, z_grid)

Sample the analytic Solov'ev flux c1 R^4/8 + c2 Z^2 on the control grid.

Returns a (n_z, n_r) flux map; equilibrium_params is (c1, c2).

differentiable_scenario_loss

differentiable_scenario_loss(equilibrium_params, initial_profiles, chi, source_sequence, target_history, rho, r_grid, z_grid, dt, edge_values, *, weights=None, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Return the equilibrium-coupled multi-step transport tracking loss.

The equilibrium parameters drive the analytic flux map that weights the differentiable transport rollout against target_history.

differentiable_scenario_gradient

differentiable_scenario_gradient(equilibrium_params, initial_profiles, chi, source_sequence, target_history, rho, r_grid, z_grid, dt, edge_values, *, weights=None)

Return JAX gradients of the coupled loss for equilibrium and sources.

The returned gradients propagate through the transport rollout and the radial weighting back to both the source schedule and the analytic equilibrium parameters, so a controller can tune the equilibrium shape and the actuator schedule jointly. Fails closed unless JAX is available.

DifferentiableScenarioGradient dataclass

DifferentiableScenarioGradient(loss, equilibrium_param_gradient, source_gradient, radial_weights, final_profiles)

Gradients of the coupled scenario loss for controller tuning.

audit_differentiable_scenario_gradient

audit_differentiable_scenario_gradient(equilibrium_params, initial_profiles, chi, source_sequence, target_history, rho, r_grid, z_grid, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None)

Compare the coupled JAX gradients against sampled finite differences.

The audit perturbs every equilibrium parameter and a deterministic subset of source entries, central-differencing the NumPy forward loss so the JAX chain-rule gradient through the equilibrium is verified independently.

assert_differentiable_scenario_gradient_consistent

assert_differentiable_scenario_gradient_consistent(equilibrium_params, initial_profiles, chi, source_sequence, target_history, rho, r_grid, z_grid, dt, edge_values, *, weights=None, epsilon=1e-05, tolerance=0.0005, sample_indices=None)

Return the scenario gradient audit or fail closed on a finite-difference gap.

DifferentiableScenarioGradientAudit dataclass

DifferentiableScenarioGradientAudit(loss, epsilon, tolerance, checked_param_indices, checked_source_indices, param_max_abs_error, source_max_abs_error, passed)

Finite-difference audit of the coupled scenario gradients.

scenario_campaign_metadata

scenario_campaign_metadata(equilibrium_params, initial_profiles, chi, source_sequence, rho, r_grid, z_grid, dt, edge_values, *, backend, gradient_tolerance=None)

Return serialisable provenance for a differentiable scenario campaign.

ScenarioCampaignMetadata dataclass

ScenarioCampaignMetadata(schema_version, backend, dtype, n_rho, n_steps, equilibrium_param_count, flux_grid_shape, dt, gradient_tolerance, jax_enable_x64, equilibrium_params, inputs_sha256)

Validated provenance for a differentiable scenario tuning campaign.

differentiable_scenario_readiness_evidence

differentiable_scenario_readiness_evidence(metadata, audit, *, latency_p95_ms=None, traceability_passed=False)

Build fail-closed readiness evidence for a coupled-scenario claim.

A passing gradient audit on its own is bounded evidence. A full-fidelity scenario claim additionally needs a JAX campaign, measured latency, and an external traceability pass; missing pieces are reported as blocked reasons.

assert_scenario_claim_admissible

assert_scenario_claim_admissible(evidence)

Fail closed unless the coupled-scenario readiness evidence is admissible.

ScenarioReadinessEvidence dataclass

ScenarioReadinessEvidence(schema_version, backend, campaign_sha256, gradient_audit_sha256, gradient_tolerance, audit_passed, latency_p95_ms, traceability_passed, claim_admissible, blocked_reasons, claim_status)

Fail-closed readiness evidence for a coupled-scenario claim.

Neural Equilibrium

NeuralEquilibriumAccelerator.pretrain_from_synthetic_equilibria() trains JAX-compatible PCA plus MLP weights on bounded synthetic Solovev-like equilibria for pretraining. The corresponding real EFIT fine-tuning entry point fine_tune_from_efit_reconstructions() fails closed unless the persisted P-EFIT or documented-public-reference artefact validator passes.

NeuralEquilibriumAccelerator

NeuralEquilibriumAccelerator(config=None)

PCA + MLP surrogate for Grad-Shafranov equilibrium.

Can be trained from SPARC GEQDSK files (preferred) or from a FusionKernel config with coil perturbations.

evaluate_surrogate

evaluate_surrogate(X_test, Y_test_raw)

Evaluate on test set. Returns dict with mse, max_error, gs_residual.

pretrain_from_synthetic_equilibria

pretrain_from_synthetic_equilibria(n_samples=2048, *, seed=20240531, ridge_alpha=1e-06, save_path=None)

Pretrain the surrogate on bounded synthetic equilibria.

Uses PCA compression followed by a deterministic ridge-regression readout. The saved weights are compatible with the existing JAX inference path because the readout is represented as a one-layer MLP.

fine_tune_from_efit_reconstructions

fine_tune_from_efit_reconstructions(geqdsk_paths, *, reference_artifact_root=None, require_reference_artifacts=True, n_perturbations=5, seed=42)

Fine-tune on real EFIT/P-EFIT artefacts only after evidence admission.

train_from_geqdsk

train_from_geqdsk(geqdsk_paths, n_perturbations=25, seed=42)

Train on real SPARC GEQDSK equilibria with perturbations.

For each GEQDSK file, generates n_perturbations by scaling p'/FF' profiles, yielding n_files × n_perturbations training pairs.

Input features (12-dim): [I_p, B_t, R_axis, Z_axis, pprime_scale, ffprime_scale, simag, sibry, kappa, delta_upper, delta_lower, q95]

Output: flattened ψ(R,Z) → PCA coefficients

Uses 70/15/15 train/val/test split with val-loss early stopping (patience=20) and combined MSE + lambda_gs * GS residual loss.

predict

predict(features)

Predict ψ(R,Z) from input features.

Parameters

features : AnyFloatArray Shape (n_features,) or (batch, n_features).

Returns

AnyFloatArray Shape (nh, nw) or (batch, nh, nw).

save_weights

save_weights(path=DEFAULT_WEIGHTS_PATH)

Save model to .npz (no pickle dependency).

load_weights

load_weights(path=DEFAULT_WEIGHTS_PATH)

Load model from .npz.

benchmark

benchmark(features, n_runs=100)

Time inference over n_runs and return stats.

NeuralEquilibriumClaimEvidence dataclass

NeuralEquilibriumClaimEvidence(schema_version, model_id, source, source_id, weights_path, weights_sha256, reference_source, reference_dataset_id, reference_artifact_sha256, reference_equilibria_count, grid_shape, feature_names, n_components, explained_variance, synthetic_test_mse, synthetic_gs_residual, psi_rmse_Wb, pressure_rmse_Pa, q_profile_rmse, boundary_rmse_m, axis_position_error_m, psi_tolerance_Wb, pressure_tolerance_Pa, q_profile_tolerance, boundary_tolerance_m, axis_position_tolerance_m, facility_claim_allowed, claim_status)

Serialisable admission evidence for neural-equilibrium predictive claims.

generate_synthetic_equilibrium_dataset

generate_synthetic_equilibrium_dataset(n_samples, *, grid_shape=(65, 65), seed=20240531)

Generate bounded Solovev-like equilibria for pretraining.

The generated targets are synthetic Grad-Shafranov-shaped flux maps for pretraining only. They are not matched EFIT or P-EFIT validation evidence.

neural_equilibrium_claim_evidence

neural_equilibrium_claim_evidence(pretraining, *, weights_path, source, source_id, model_id='neural_equilibrium_pca_mlp', reference_artifact_path=None)

Build fail-closed evidence for neural-equilibrium predictive claims.

Synthetic pretraining may support bounded pretraining and inference-plumbing statements only. Facility or predictive claims require a persisted P-EFIT/documented-reference artefact that validates against the same weight checksum and declares psi, pressure, q-profile, boundary, and magnetic-axis error tolerances.

assert_neural_equilibrium_facility_claim_admissible

assert_neural_equilibrium_facility_claim_admissible(evidence)

Return evidence only when strict matched-reference admission passed.

save_neural_equilibrium_claim_evidence

save_neural_equilibrium_claim_evidence(evidence, path)

Persist neural-equilibrium claim evidence as deterministic JSON.

pretrain_neural_equilibrium_synthetic

pretrain_neural_equilibrium_synthetic(*, n_samples=2048, save_path=REPO_ROOT / 'weights' / 'neural_equilibrium_synthetic_pretrain.npz', grid_shape=(65, 65), n_components=20, seed=20240531)

Run bounded synthetic pretraining (convenience entry point).

PretrainingResult dataclass

PretrainingResult(n_samples, n_components, explained_variance, train_mse, val_mse, test_mse, test_max_error, gs_residual, train_time_s, weights_path, evidence_kind, campaign)

Synthetic neural-equilibrium pretraining summary.

SyntheticEquilibriumCampaign dataclass

SyntheticEquilibriumCampaign(n_samples, grid_shape, seed, feature_names, validity_domain)

Metadata for bounded synthetic equilibrium pretraining data.

Equilibrium Shape & Profile Metrics

Reusable, reconstruction-agnostic extraction of macroscopic descriptors (R0/minor radius/elongation/triangularity, li(3), poloidal beta, and q95) from a poloidal-flux map and the fitted p'/FF' profiles. Used by the real-time EFIT inverse and shareable by the free-boundary kernel and kinetic EFIT. The poloidal field uses the per-radian convention B_pol = |grad psi| / R (Ampere-consistent); q95 requires contourpy for the flux-surface contour.

EquilibriumShape dataclass

EquilibriumShape(R0, a, kappa, delta_upper, delta_lower, q95, beta_pol, li)

Macroscopic equilibrium descriptors extracted from a flux map.

compute_equilibrium_shape

compute_equilibrium_shape(psi, R, Z, p_coeffs, ff_coeffs, ip, vacuum_rb_phi)

Full shape/profile metric set, or None when the flux map has no plasma.

boundary_geometry

boundary_geometry(boundary)

Geometric (R0, a, kappa, delta_upper, delta_lower) from boundary points.

poloidal_field

poloidal_field(psi, R, Z)

Poloidal-field magnitude |B_pol| = |grad psi| / R on the grid.

Uses the per-radian flux convention consistent with the Grad-Shafranov source Delta* psi = -mu0 R^2 p' - FF' (so the closed line integral of B_pol equals mu0 Ip by Ampere's law). This is the physically-correct absolute field; note the magnetic diagnostic response (DiagnosticResponse) uses a 1/(2 pi) scaled sensor convention internally, which is self-consistent for the inverse fit.

internal_inductance

internal_inductance(psi, R, Z, ip, r0)

Normalised internal inductance li(3) = 2 int B_pol^2 dV / (mu0^2 Ip^2 R0).

poloidal_beta

poloidal_beta(psi, psi_N, R, Z, p_coeffs, psi_axis, a, ip)

Poloidal beta 8 pi^2 a^2 <p> / (mu0 Ip^2) from the fitted pressure profile.

<p> is the plasma-volume-averaged pressure; negative pressure (an unphysical magnetic-only profile fit) is clipped to zero for the average.

safety_factor_q95

safety_factor_q95(psi, psi_N, R, Z, ff_coeffs, psi_axis, vacuum_rb_phi, *, surface=0.95)

Edge safety factor q95 = (F/2pi) * contour-integral dl/(R^2 B_pol).

Uses the toroidal flux function from the vacuum field plus the FF' integral, and a flux-surface contour line integral at psi_N = surface. Returns NaN when the contour engine is unavailable or the surface has no closed contour.

cylindrical_q95

cylindrical_q95(a, kappa, r0, ip, vacuum_rb_phi)

Elongation-corrected cylindrical edge safety factor (contour-free estimate).

q ~ 2 pi a^2 F (1 + kappa^2) / (2 mu0 R0^2 |Ip|) with F = R0 B_phi0; used as the q95 fallback when the flux-surface contour engine is unavailable.

pressure_grid

pressure_grid(psi_N, p_coeffs, psi_axis)

Pressure p(psi_N) = psi_axis * sum_k p_k (1 - psi_N^{k+1})/(k+1) (p=0 at edge).

Integrates the fitted p'(psi_N) from the boundary inward using dpsi = -psi_axis dpsi_N (psi_N = 1 - psi/psi_axis).

plasma_boundary

plasma_boundary(psi, R, Z)

Trace the last closed flux surface as angle-sorted (R, Z) points.

The plasma is the positive-flux region (fixed-boundary convention); the boundary is the outer ring of plasma cells. Returns an empty (0, 2) array when no positive-flux plasma exists.

largest_flux_contour

largest_flux_contour(psi_N, R, Z, level)

Longest closed-ish psi_N = level contour as (R, Z) points, or None.

Uses contourpy for a sub-grid contour (smooth shape/triangularity); returns None when contourpy is unavailable or the level has no contour.

JAX-Accelerated Neural Equilibrium

Requires pip install "scpn-control[jax]". GPU and autodiff via jax.grad.

jax_neural_eq_predict

jax_neural_eq_predict(features, mlp_weights, pca_params, norm_params, grid_shape=(129, 129))

Predict psi(R,Z) from input features using JAX-accelerated pipeline.

Parameters

features : shape (n_features,) or (batch, n_features) mlp_weights : ((W0, W1, ...), (b0, b1, ...)) pca_params : (pca_mean, pca_components) norm_params : (input_mean, input_std) grid_shape : (nh, nw) output grid dimensions

Returns

psi : shape (nh, nw) or (batch, nh, nw)

jax_neural_eq_predict_batched

jax_neural_eq_predict_batched(features_batch, mlp_weights, pca_params, norm_params, grid_shape=(129, 129))

Batched equilibrium prediction via jax.vmap.

Parameters

features_batch : shape (batch, n_features)

Returns

psi_batch : shape (batch, nh, nw)

load_weights_as_jax

load_weights_as_jax(path)

Load neural equilibrium .npz weights into JAX-compatible structures.

Returns

(mlp_weights, pca_params, norm_params, grid_shape) mlp_weights: ((W0, W1, ...), (b0, b1, ...)) pca_params: (pca_mean, pca_components) norm_params: (input_mean, input_std) grid_shape: (nh, nw)

Neural Transport

cross_validate_neural_transport() benchmarks the active surrogate against the analytic critical-gradient reference across fixed regime cases and a canonical profile, so shipped weights can be checked against a deterministic local baseline instead of only reporting synthetic training RMSE.

neural_transport_closure_profiles() packages profile transport coefficients for controller and differentiable-transport coupling. It validates finite strictly ordered profile inputs, fails closed when neural weights are required but unavailable, and records whether coefficients came from loaded weights or the analytic fallback.

NeuralTransportModel

NeuralTransportModel(weights_path=None, *, auto_discover=True, allow_weight_load_fallback=False, allow_legacy_weight_load_fallback=False)

Neural transport surrogate with analytic fallback.

On construction, attempts to load MLP weights from weights_path. If loading fails (file missing, wrong format), the model transparently falls back to :func:critical_gradient_model.

Parameters

weights_path : str or Path, optional Path to a .npz file containing MLP weights. The file must contain arrays: w1, b1, w2, b2, w3, b3, input_mean, input_std, output_scale.

Examples

model = NeuralTransportModel() # fallback mode inp = TransportInputs(grad_ti=8.0) fluxes = model.predict(inp) fluxes.chi_i > 0 True model.is_neural False

predict

predict(inp)

Predict turbulent transport fluxes for given local parameters.

Uses the neural MLP if weights are loaded, otherwise falls back to the analytic critical-gradient model.

Parameters

inp : TransportInputs Local plasma parameters at a single radial point.

Returns

TransportFluxes Predicted heat and particle diffusivities.

predict_profile

predict_profile(rho, te, ti, ne, q_profile, s_hat_profile, r_major=6.2)

Predict transport coefficients on the full radial profile.

Computes normalised gradients from the profile arrays via central finite differences, then evaluates the surrogate at each radial point. When the MLP is loaded the entire profile is evaluated in a single batched forward pass (no Python loop).

Parameters

rho : FloatArray Normalised radius grid (0 to 1), shape (N,). te, ti : FloatArray Electron/ion temperature profiles [keV], shape (N,). ne : FloatArray Electron density profile [10^19 m^-3], shape (N,). q_profile : FloatArray Safety factor profile, shape (N,). s_hat_profile : FloatArray Magnetic shear profile, shape (N,). r_major : float Major radius [m] for gradient normalisation.

Returns

chi_e, chi_i, d_e : FloatArray Transport coefficient profiles, each shape (N,).

NeuralTransportClosureResult dataclass

NeuralTransportClosureResult(chi_e, chi_i, d_e, channel_weights, source, weights_checksum)

Profile transport closure with provenance for controller coupling.

Parameters

chi_e, chi_i, d_e : FloatArray Electron heat, ion heat, and particle diffusivity profiles. channel_weights : FloatArray Per-radius normalised contribution weights for chi_e, chi_i, and d_e with shape (3, N). source : str "neural" when loaded weights are active, otherwise "analytic_fallback". weights_checksum : str or None Loaded neural-weight checksum, or None for analytic fallback.

NeuralTransportClaimEvidence dataclass

NeuralTransportClaimEvidence(schema_version, model_id, source, source_id, surrogate_mode, weights_path, weights_sha256, internal_weights_checksum, feature_schema, reference_source, reference_dataset_id, reference_artifact_sha256, reference_sample_count, local_case_count, local_max_abs_error, local_channel_agreement, local_per_channel_relative_rmse, local_profile_per_channel_relative_rmse, chi_i_rmse_m2_s, chi_e_rmse_m2_s, d_e_rmse_m2_s, chi_i_relative_mae, unstable_branch_accuracy, chi_i_rmse_tolerance_m2_s, chi_e_rmse_tolerance_m2_s, d_e_rmse_tolerance_m2_s, chi_i_relative_mae_tolerance, unstable_branch_accuracy_min, quantitative_claim_allowed, claim_status)

Serialisable admission evidence for neural-transport surrogate claims.

neural_transport_closure_profiles

neural_transport_closure_profiles(rho, te, ti, ne, q_profile, s_hat_profile, *, model=None, r_major=6.2, require_neural=False, allow_fallback=False, allow_legacy_fallback=False)

Return bounded neural-transport closure profiles with provenance.

The function packages :meth:NeuralTransportModel.predict_profile for controller and differentiable-transport coupling. It validates the radial grid and plasma profiles before evaluation, fails closed when neural weights are required but unavailable, and records whether the result came from the neural surrogate or the analytic critical-gradient fallback.

cross_validate_neural_transport

cross_validate_neural_transport(model=None, *, benchmark_cases=None, profile_points=24)

Compare the current transport surrogate to the analytic reference.

Returns pointwise and profile-level error metrics against the :func:critical_gradient_model benchmark. This is a reference check, not a claim of agreement with full QLKNN-10D or first-principles turbulence solvers.

neural_transport_claim_evidence

neural_transport_claim_evidence(validation_result, *, source, source_id, model_id='neural_transport_qlknn_facade', weights_path=None, reference_artifact_path=None)

Build fail-closed evidence for neural-transport quantitative claims.

assert_neural_transport_quantitative_claim_admissible

assert_neural_transport_quantitative_claim_admissible(evidence)

Return evidence only when strict matched-reference admission passed.

save_neural_transport_claim_evidence

save_neural_transport_claim_evidence(evidence, path)

Persist neural-transport claim evidence as deterministic JSON.

MHD Stability

run_full_stability_check

run_full_stability_check(qp, beta_t=None, Ip_MA=None, a=None, B0=None, j_bs=None, j_total=None)

Run all available MHD stability criteria and return a summary.

Mercier, ballooning, and Kruskal-Shafranov are always evaluated. Troyon requires beta_t, Ip_MA, a, and B0. NTM requires j_bs, j_total, and a.

Parameters

qp : QProfile — pre-computed safety-factor profile beta_t : float, optional — total toroidal beta (dimensionless) Ip_MA : float, optional — plasma current [MA] a : float, optional — minor radius [m] B0 : float, optional — toroidal field on axis [T] j_bs : array, optional — bootstrap current density [A/m^2] j_total : array, optional — total current density [A/m^2]

Returns

StabilitySummary

IMAS Adapter

EquilibriumIDS dataclass

EquilibriumIDS(r, z, psi, j_tor, ip, b0, r0, time=0.0)

Minimal subset of IMAS equilibrium IDS for scpn-control interop.

Fields match equilibrium.time_slice[0].profiles_2d[0].

from_geqdsk

from_geqdsk(filepath)

Import from GEQDSK file via scpn_control.core.eqdsk.

from_kernel

from_kernel(kernel, time=0.0)

Extract EquilibriumIDS from a solved FusionKernel instance.

HPC Bridge

HPCBridge loads compiled Grad-Shafranov solver libraries only from absolute dynamic-library paths. Package-local solver libraries are trusted by default. External paths provided through SCPN_SOLVER_LIB require the additional operator gate SCPN_ALLOW_EXTERNAL_SOLVER_LIB=1; without that gate the bridge fails closed before calling the dynamic loader.

HPCBridge

HPCBridge(lib_path=None)

Interface between Python and the compiled C++ Grad-Shafranov solver.

Loads the shared library (libscpn_solver.so / scpn_solver.dll) at construction time. If the library is not found the bridge gracefully degrades — :meth:is_available returns False and the caller falls back to Python.

Parameters

lib_path : str, optional Explicit path to the shared library. When None (default) the bridge searches trusted package-local locations only. SCPN_SOLVER_LIB must be an absolute dynamic-library path and is accepted only when it resolves under the package-local solver directories, unless the operator also sets SCPN_ALLOW_EXTERNAL_SOLVER_LIB=1 for a vetted external library.

is_available

is_available()

Return True if the compiled solver library was loaded.

close

close()

Release the C++ solver instance, if one was created.

initialize

initialize(nr, nz, r_range, z_range, boundary_value=0.0)

Create the C++ solver instance for the given grid dimensions.

set_boundary_dirichlet

set_boundary_dirichlet(boundary_value=0.0)

Set a fixed Dirichlet boundary value for psi edges, if supported.

solve

solve(j_phi, iterations=100)

Run the C++ solver for iterations sweeps.

Returns None if the library is not loaded (caller should fall back to a Python solver).

solve_into

solve_into(j_phi, psi_out, iterations=100)

Run the C++ solver and write results into psi_out in-place.

solve_until_converged

solve_until_converged(j_phi, max_iterations=1000, tolerance=1e-06, omega=1.8)

Run solver until convergence, if native API is available.

Returns (psi, iterations_used, final_delta). If the library is unavailable or uninitialized, returns None.

solve_until_converged_into

solve_until_converged_into(j_phi, psi_out, max_iterations=1000, tolerance=1e-06, omega=1.8)

Run convergence API and write results into psi_out in-place.

Gyrokinetic Transport (v0.16.0)

GyrokineticTransportModel

GyrokineticTransportModel(n_modes=16, include_etg=False)

Drop-in replacement for Gyro-Bohm transport scaling.

Public evaluation points use rho in [0, 1] and positive finite local geometry, temperature, density, safety-factor, and charge inputs.

evaluate

evaluate(rho, profiles)

Evaluate transport coefficients at a single radial point.

evaluate_profile

evaluate_profile(rho, profiles)

Evaluate full radial profile over a finite one-dimensional rho grid in [0, 1].

GK Solver Interface (v0.17.0)

GKSolverBase

Bases: ABC

Abstract base for gyrokinetic solvers.

Concrete subclasses must implement prepare_input, run, and is_available.

prepare_input abstractmethod

prepare_input(params)

Write solver-specific input deck to a temporary directory.

Returns the path to the input file or directory.

run abstractmethod

run(input_path, *, timeout_s=30.0)

Execute the solver and parse output into GKOutput.

is_available abstractmethod

is_available()

Return True if the solver binary/library is installed.

run_from_params

run_from_params(params, *, timeout_s=30.0)

Prepare and run in one call.

GKLocalParams dataclass

GKLocalParams(R_L_Ti, R_L_Te, R_L_ne, q, s_hat, alpha_MHD=0.0, Te_Ti=1.0, Z_eff=1.5, nu_star=0.1, beta_e=0.01, epsilon=0.1, kappa=1.0, delta=0.0, rho=0.5, R0=6.2, a=2.0, B0=5.3, n_e=10.0, T_e_keV=8.0, T_i_keV=8.0)

Local plasma parameters at a single flux surface.

The first 11 fields match the GyrokineticsParams convention already used by gyrokinetic_transport.py. The remaining fields add Miller geometry and dimensional quantities needed by external GK codes.

GKOutput dataclass

GKOutput(chi_i, chi_e, D_e, D_i=0.0, gamma=(lambda: empty(0))(), omega_r=(lambda: empty(0))(), k_y=(lambda: empty(0))(), dominant_mode='stable', converged=True, electromagnetic=False)

Gyrokinetic solver output for a single flux surface.

Fluxes are in physical units [m^2/s]. Growth rate arrays are normalised to c_s / a.

Native Linear GK Solver (v0.17.0)

solve_linear_gk

solve_linear_gk(species_list=None, geom=None, vgrid=None, R0=2.78, a=1.0, B0=2.0, q=1.4, s_hat=0.78, Z_eff=1.0, nu_star=0.01, n_ky_ion=16, n_ky_etg=0, n_theta=64, n_period=2, electromagnetic=False, beta_e=0.0, alpha_MHD=0.0)

Full k_y scan of the linear GK eigenvalue solver.

Parameters

species_list : list of GKSpecies Plasma species. Default: [deuterium, adiabatic electron]. geom : MillerGeometry Flux-tube geometry. Default: circular with given (R0, a, q, s_hat). vgrid : VelocityGrid Velocity-space grid. Default: (16, 24). n_ky_ion, n_ky_etg : int Number of k_y points on ion and electron scales. electromagnetic : bool Enable finite-beta EM corrections (A_∥, delta-B_∥). Default False. beta_e : float Electron beta = 2 mu_0 n_e T_e / B_0^2. alpha_MHD : float Normalised pressure gradient alpha = -q^2 R dp/dr / (B^2/2mu_0).

quasilinear_fluxes_from_spectrum

quasilinear_fluxes_from_spectrum(result, ion, R0=2.78, a=1.0, B0=2.0, saturation='mixing_length', electron=None)

Convert linear GK spectrum to physical transport fluxes.

Parameters

result : LinearGKResult Output from solve_linear_gk(). ion : GKSpecies Main ion species (for gyro-Bohm normalisation). R0, a, B0 : float Geometry and field for dimensional conversion. saturation : str Saturation model: "mixing_length" (default). electron : GKSpecies | None Electron species for Te/Ti ratio. Defaults to ion temperature (Te/Ti=1).

GK Hybrid Validation (v0.17.0)

OODDetector

OODDetector(*, mahalanobis_threshold=4.0, soft_sigma_threshold=2.0, ensemble_disagreement_threshold=0.3, training_mean=None, training_cov_inv=None)

Three-method OOD detector for surrogate transport models.

Parameters

mahalanobis_threshold : float Mahalanobis distance above which an input is flagged OOD. Default 4.0 corresponds to ~99.99% chi-squared quantile for 10D. soft_sigma_threshold : float Number of training-set standard deviations for soft range check. ensemble_disagreement_threshold : float Relative std across ensemble predictions above which to flag OOD.

mahalanobis_distance

mahalanobis_distance(x)

Compute Mahalanobis distance from training mean.

check_mahalanobis

check_mahalanobis(x)

Classify a point as in- or out-of-distribution by Mahalanobis distance.

Parameters

x The query point in the gyrokinetic input space.

Returns

OODResult The Mahalanobis distance, the OOD flag (distance above threshold), and a normalised confidence.

check_range

check_range(x)

Check hard bounds and soft sigma bounds.

check_ensemble

check_ensemble(predictions)

Check disagreement across ensemble predictions.

Parameters

predictions : array, shape (K, 3) Ensemble of [chi_i, chi_e, D_e] predictions from K models.

check

check(x, ensemble_predictions=None)

Run all applicable checks and return combined result.

GKScheduler

GKScheduler(config=None)

Spot-check scheduler for hybrid surrogate+GK transport.

step

step(rho, chi_i, ood_results=None)

Decide whether to run GK spot-checks this step.

Parameters

rho : array Radial grid. chi_i : array Current ion diffusivity profile (from surrogate). ood_results : list of OODResult or None Per-surface OOD detector results (adaptive mode).

Returns

SpotCheckRequest or None None if no validation needed this step.

reset

reset()

Reset the scheduler step counter and cached previous chi_i.

GKCorrector

GKCorrector(nr, config=None)

Apply corrections to surrogate transport based on GK spot-checks.

max_correction_factor property

max_correction_factor

Largest absolute multiplicative correction |alpha - 1| applied.

mean_correction_factor property

mean_correction_factor

Mean absolute multiplicative correction |alpha - 1| applied.

update

update(records, rho)

Incorporate new spot-check results into correction factors.

Interpolates correction factors from spot-check surfaces to the full radial grid with temporal EMA smoothing.

correct

correct(chi_i, chi_e, D_e)

Apply stored correction factors to surrogate profiles.

Ballooning Solver (v0.16.0)

BallooningEquation

BallooningEquation(s, alpha, theta_max=20 * pi, n_theta=2001)

Second-order ODE for ideal MHD ballooning stability in the s-alpha model.

f

f(theta)

Field-line bending term coefficient.

g

g(theta)

Curvature drive term coefficient.

solve

solve()

Solve via Newcomb shooting: ξ crossing zero signals instability.

No zero crossing within [0, θ_max] means stable.

BallooningStabilityAnalysis

Performs ballooning stability analysis given a QProfile.

analyze

analyze(q_profile)

Extract (s, alpha) at each radial point and return the per-radius stability margin.

Positive margin means stable. margin = alpha_crit(s) - alpha_actual.

find_marginal_stability

find_marginal_stability(s, alpha_min=0.0, alpha_max=2.0, tol=0.001)

Binary search for alpha_crit at fixed shear s.

Returns the critical alpha (first stability boundary).

Current Diffusion (v0.16.0)

CurrentDiffusionSolver

CurrentDiffusionSolver(rho, R0, a, B0)

Implicit Crank-Nicolson solver for poloidal flux diffusion.

Governing equation (1D in ρ, cylindrical limit): ∂ψ/∂t = (η_∥/μ₀) · [∂²ψ/∂ρ² + (1/ρ)·∂ψ/∂ρ] + R₀·η_∥·j_source Reference: Jardin (2010), Ch. 8, Eq. 8.8.

η_∥ from neoclassical_resistivity() — Sauter 1999/2002 + Hirshman 1981. Crank-Nicolson discretisation — θ=½ implicit-explicit split, second-order accurate in time and space.

step

step(dt, Te, ne, Z_eff, j_bs, j_cd, j_ext=None)

Advance ψ by one timestep dt [s].

Source term: j_source = j_bs + j_cd + j_ext Boundary conditions: axis (ρ=0) — regularity (L'Hôpital); edge (ρ=1) — Dirichlet ψ=const.

Current Drive (v0.16.0)

ECCDSource

ECCDSource(P_ec_MW, rho_dep, sigma_rho, eta_cd=_ETA_ECCD_DEFAULT)

Electron Cyclotron Current Drive.

Default η_cd: Fisch & Boozer 1980, PRL 45, 720 (theory); range 0.02–0.05 A/W depending on launch angle and T_e. For angle-resolved efficiency, use eccd_efficiency().

P_absorbed

P_absorbed(rho)

Absorbed power per unit rho [W] using a grid-normalised finite-width deposition kernel.

j_cd

j_cd(rho, ne_19, Te_keV)

Driven current density [A/m^2].

j_cd = η_cd · P_abs / (n_e · T_e) Fisch & Boozer 1980, PRL 45, 720, normalised form.

NBISource

NBISource(P_nbi_MW, E_beam_keV, rho_tangency, sigma_rho=0.15, A_beam=_A_BEAM_D, Z_beam=_Z_BEAM_D)

Neutral Beam Injection.

Current-drive efficiency formula: Ehst & Karney 1991, Nucl. Fusion 31, 1933. Slowing-down time: Stix 1972, Plasma Physics 14, 367, Eq. 6.

P_heating

P_heating(rho)

Beam heating power per unit rho [W] using a grid-normalised finite-width deposition kernel.

j_cd

j_cd(rho, ne_19, Te_keV, Ti_keV, Z_eff=1.5)

Beam-driven current density [A/m^2].

Fast-ion density from steady-state balance: n_fast = P_heat · τ_s / E_beam τ_s from Stix 1972, Eq. 6 (nbi_slowing_down_time). j_cd = e · n_fast · v_∥ / Z_beam Ehst & Karney 1991, Nucl. Fusion 31, 1933.

Note: Ti_keV enters via Z_eff-dependent collisionality; here held constant.

CurrentDriveMix

CurrentDriveMix(a=1.0)

Superposition of multiple current-drive sources.

add_source

add_source(source)

Add a current-drive source to the mix.

Parameters

source An ECCD, NBI, or LHCD source.

total_j_cd

total_j_cd(rho, ne, Te, Ti)

Sum the driven current density over all sources.

Parameters

rho Normalised-radius grid. ne Electron-density profile in 10¹⁹ m⁻³. Te Electron-temperature profile in keV. Ti Ion-temperature profile in keV (used by NBI sources).

Returns

AnyFloatArray The total driven current-density profile in A/m².

total_heating_power

total_heating_power(rho)

Sum the absorbed heating-power density over all sources.

Parameters

rho Normalised-radius grid.

Returns

AnyFloatArray The total absorbed power-density profile in W/m³.

total_driven_current

total_driven_current(rho, ne, Te, Ti)

I_cd = ∫ j_cd · 2π r dr [A].

r = ρ · a, dr = dρ · a → dA = 2π ρ a² dρ

NTM Dynamics (v0.16.0)

RationalSurface dataclass

RationalSurface(rho, r_s, m, n, q, shear)

Interpolated q = m/n rational surface.

Attributes

rho Normalised minor-radius coordinate of the rational surface. r_s Minor-radius position of the rational surface in metres. m Poloidal mode number. n Toroidal mode number. q Rational safety-factor value m / n. shear Local magnetic shear (rho / q) dq / d rho.

NTMIslandDynamics

NTMIslandDynamics(r_s, m, n, a, R0, B0, Delta_prime_0=None, s_hat=1.0, q_s=2.0)

Modified Rutherford Equation solver for a single NTM island.

Rutherford 1973, Phys. Fluids 16, 1903 — original equation. La Haye 2006, Phys. Plasmas 13, 055501 — full MRE with coefficients.

Parameters

r_s Minor-radius position of the rational surface in metres. m Poloidal mode number. n Toroidal mode number. a Plasma minor radius in metres. R0 Major radius in metres. B0 Toroidal magnetic field in tesla. Delta_prime_0 Classical tearing index at vanishing island width in m^-1. When omitted, the model uses the Rutherford cylindrical estimate -2 m / r_s. s_hat Local magnetic shear at the rational surface. q_s Safety factor at the rational surface.

Raises

ValueError If geometric ordering, mode numbers, or scalar domains are invalid.

delta_prime_model

delta_prime_model(w)

Return classical Delta prime with finite-island-width saturation.

Rutherford 1973, Phys. Fluids 16, 1903; La Haye 2006, Eq. 3.

Parameters

w Island half-width in metres.

Returns

float Classical tearing index in m^-1.

Raises

ValueError If w is not finite and positive.

dw_dt

dw_dt(w, j_bs, j_phi, j_cd, eta, w_d=0.001, w_pol=0.0005, d_cd=0.05, pressure_gradient=0.0, B_pol=None, rho_theta_i=None, beta_pol=None)

RHS of the Modified Rutherford Equation.

τ_R * (dw/dt) / r_s = Δ' r_s (Rutherford 1973) + a1(j_bs/j_φ)(r_s/w)/(1+(w_d/w)²) (La Haye 2006, Eq. 4) - a2(w_pol²/w³) (Sauter 1997, Eq. 19) - a4(w_d²/w²)(s/s_ref) (Sauter 1997, Eq. 20) - a3(j_cd/j_φ)(r_s/w)f_ECCD (La Haye 2006, Eq. 5; Carrera 1986, Eq. 16)

Parameters

w : float Island half-width [m]. j_bs : float Bootstrap current density [A/m^2]. j_phi : float Total toroidal current density [A/m^2]. j_cd : float ECCD current density [A/m^2]. eta : float Resistivity [Ω·m]. w_d : float Ion banana width ρ_θi √(2 β_pol) [m]; overridden if rho_theta_i and beta_pol are given. Sauter 1997, Eq. 21. w_pol : float Polarization width threshold [m]. Sauter 1997, Eq. 19. d_cd : float ECCD deposition half-width [m]. Carrera 1986, Eq. 16. pressure_gradient : float dp/dr [Pa/m]; used for GGJ Δ' correction when non-zero. B_pol : float | None Poloidal field [T]; required for GGJ when pressure_gradient ≠ 0. rho_theta_i : float | None Poloidal ion Larmor radius [m]; if given with beta_pol, overrides w_d. beta_pol : float | None Poloidal beta; if given with rho_theta_i, overrides w_d.

Returns

float dw/dt [m/s].

evolve

evolve(w0, t_span, dt, j_bs, j_phi, j_cd, eta, w_d=0.001, w_pol=0.0005, d_cd=0.05, pressure_gradient=0.0, B_pol=None, rho_theta_i=None, beta_pol=None)

Integrate island width over time with fourth-order Runge-Kutta.

All physics keyword arguments are forwarded to dw_dt unchanged.

Parameters

w0 Initial island half-width in metres. t_span Start and end time in seconds. dt Integration step in seconds. j_bs Bootstrap current density in A m^-2. j_phi Total toroidal current density in A m^-2. j_cd ECCD current density in A m^-2. eta Plasma resistivity in ohm-metres. w_d Ion banana-width stabilisation scale in metres. w_pol Polarisation stabilisation width in metres. d_cd ECCD deposition half-width in metres. pressure_gradient Pressure gradient in Pa m^-1 for optional GGJ correction. B_pol Poloidal magnetic field in tesla for optional GGJ correction. rho_theta_i Poloidal ion Larmor radius in metres. beta_pol Poloidal beta used with rho_theta_i to override w_d.

Returns

tuple[FloatArray, FloatArray] Time grid in seconds and island half-width history in metres.

Raises

ValueError If the time span, step size, initial width, or forwarded physics arguments are outside their admitted domains.

NTMController

NTMController(w_onset=0.02, w_target=0.005)

ECCD request state machine for NTM suppression.

Parameters

w_onset Island width in metres above which the controller requests ECCD power. w_target Island width in metres below which the controller deactivates.

Raises

ValueError If the thresholds are not finite positive values or if w_target is not smaller than w_onset.

step

step(w, rho_rs, max_power=20.0)

Return requested ECCD power and update controller state.

Parameters

w Current island half-width in metres. rho_rs Normalised rational-surface radius used as the ECCD target. max_power Maximum requested ECCD power in MW.

Returns

float Requested ECCD power in MW.

Raises

ValueError If w or max_power is negative/non-finite, or if rho_rs lies outside [0, 1].

eccd_stabilization_factor

eccd_stabilization_factor(d_cd, w)

Return the ECCD deposition efficiency factor f_ECCD(w, d_cd).

The model uses Carrera et al. 1986, Phys. Fluids 29, 899, Eq. 16: f_ECCD = (w / d_cd) * exp(-w**2 / (4 d_cd**2)).

Parameters

d_cd ECCD deposition half-width in metres. w Magnetic-island half-width in metres.

Returns

float Dimensionless coupling factor. Non-positive widths return 0.0.

Raises

ValueError If either input is non-finite.

find_rational_surfaces

find_rational_surfaces(q, rho, a, m_max=5, n_max=3)

Locate rational surfaces by zero-crossing interpolation.

Parameters

q One-dimensional positive safety-factor profile. rho One-dimensional strictly increasing normalised minor-radius grid. a Plasma minor radius in metres. m_max Largest poloidal mode number to scan. n_max Largest toroidal mode number to scan.

Returns

list[RationalSurface] Rational surfaces sorted by normalised radius.

Raises

ValueError If the profiles are not finite one-dimensional matching arrays, if rho is not strictly increasing, or if the geometry/mode bounds are non-physical.

bootstrap_from_local

bootstrap_from_local(ne_19, Te_keV, Ti_keV, q, rho, R0, a, B0, z_eff, dne_dr, dTe_dr, dTi_dr)

Return local Sauter bootstrap current density.

Sauter et al. 1999, Phys. Plasmas 6, 2834, Eqs. 14-16:

j_bs = -(p_e/B_pol) * [L31*d(ln p_e)/dr + L32*d(ln T_e)/dr
                       + L34*(T_i/T_e)*d(ln T_i)/dr]

The local collisionality and trapped-particle fractions are evaluated from the same fits used in :mod:scpn_control.core.neoclassical.

Parameters

ne_19 Electron density in 10**19 m^-3. Te_keV Electron temperature in keV. Ti_keV Ion temperature in keV. q Safety factor at the evaluated radius. rho Normalised minor-radius coordinate. R0 Major radius in metres. a Minor radius in metres. B0 Toroidal magnetic field in tesla. z_eff Effective ion charge. dne_dr Electron-density radial gradient in 10**19 m^-4. dTe_dr Electron-temperature radial gradient in keV m^-1. dTi_dr Ion-temperature radial gradient in keV m^-1.

Returns

float Bootstrap current density in A m^-2. The on-axis and vanishing poloidal-field limits return 0.0 because the local fit is singular there.

Raises

ValueError If scalar inputs are non-finite, if required positive quantities are not positive, or if B0 is zero.

Sawtooth Model (v0.16.0)

SawtoothCycler

SawtoothCycler(rho, R0, a, s_crit=0.1, trigger_model='shear', porcelli_params=None)

Tracks and triggers sawtooth crashes.

step

step(dt, q, shear, T, n)

Advance the sawtooth cycle one step and fire a crash if triggered.

Parameters

dt Time step in seconds. q Safety-factor profile. shear Magnetic-shear profile. T Temperature profile. n Density profile.

Returns

SawtoothEvent or None The crash event when the Porcelli trigger fires, otherwise None.

kadomtsev_crash

kadomtsev_crash(rho, T, n, q, R0, a)

Apply Kadomtsev full-reconnection crash.

Returns (T_new, n_new, q_new, rho_1, rho_mix).

Helical flux proxy: dψ/dρ = ρ(1/q − 1), integrated from axis. rho_mix is where ψ returns to zero outside the q=1 surface. Inside rho_mix, T, n are volume-averaged and q is reset to 1.01.

Kadomtsev 1975, Sov. J. Plasma Phys. 1, 389. Porcelli et al. 1996, PPCF 38, 2163, §2.

SOL Model (v0.16.0)

TwoPointSOL

TwoPointSOL(R0, a, q95, B_pol, kappa=1.0)

Two-point model for the Scrape-Off Layer.

Parallel heat conduction: q_∥ = κ_0 T_u^{7/2} / (7 L_∥). Stangeby 2000, Eq. 5.69.

solve

solve(P_SOL_MW, n_u_19, f_rad=0.0)

Solve two-point model for upstream and target conditions.

Upstream temperature from conduction integral (Stangeby 2000, Eq. 5.69): T_u = (3.5 · L_∥ · q_∥ / κ_0)^{2/7}

Target heat flux: q_target = P_SOL / (4π R λ_q f_exp). Stangeby 2000, Ch. 5.

Integrated Scenario (v0.16.0)

IntegratedScenarioSimulator

IntegratedScenarioSimulator(config)

Time-dependent tokamak shot simulator.

Operator splitting sequence per step() (Strang 1968): a) half-step transport diffusion b) full-step sources c) full-step MHD events d) half-step transport diffusion

State variables carried on 1D rho grid: Te, Ti, ne, psi (→ q, j).

initialize

initialize(profiles=None)

Set up the transport solver and return the initial state.

Parameters

profiles Optional initial profiles ("Te", "Ti", "ne", "psi") on the radial grid; defaults are used for any absent key.

Returns

ScenarioState The scenario state at t_start.

step

step()

Advance one time step using Strang operator splitting.

Strang 1968, SIAM J. Numer. Anal. 5, 506: a) half-step transport b) full-step sources c) full-step MHD events d) half-step transport

run

run()

Run the scenario from start to end and collect the state trace.

Initialises the solver if needed, then steps to t_end.

Returns

list[ScenarioState] The scenario state at each time step.

to_json

to_json(path)

Write the current scenario state to a JSON file.

Parameters

path Destination JSON path.

audit_scenario_coupling

audit_scenario_coupling(states, config, *, scenario_name='integrated_scenario', current_relative_tolerance=1e-09, energy_relative_step_limit=5.0)

Audit a scenario replay for finite, bounded, traceable module coupling.

The audit is deliberately a bounded controller-facing contract. It proves replay consistency, finite state exchange, declared timestep semantics, and conservation-bound diagnostics for a completed trajectory. It is not a measured-discharge or external integrated-modelling validation claim.

save_scenario_coupling_report

save_scenario_coupling_report(audit, path)

Persist a scenario-coupling audit report with deterministic JSON layout.

iter_baseline_scenario

iter_baseline_scenario()

Return the ITER baseline (15 MA, full heating) scenario configuration.

run_integrated_scenario_closed_loop

run_integrated_scenario_closed_loop(config=None, *, target_w_thermal_j=None, feedback_gain_mw=25.0, p_aux_bounds_mw=(0.0, 20.0), max_steps=None)

Run a bounded controller-to-integrated-scenario closed loop.

The loop uses :class:~scpn_control.control.scenario_scheduler.FeedforwardController to combine a constant scenario schedule with a thermal-energy feedback trim. The command is applied to ScenarioConfig.P_aux_MW before each :class:~scpn_control.core.integrated_scenario.IntegratedScenarioSimulator plant step, then the completed replay is audited with :func:~scpn_control.core.integrated_scenario.audit_scenario_coupling.

This is a deterministic repository wiring contract, not measured-discharge validation or facility-control evidence.

closed_loop_scenario_result_to_dict

closed_loop_scenario_result_to_dict(result)

Serialise a closed-loop scenario result to JSON-safe data.


SCPN — Petri Net Compiler

StochasticPetriNet

verify_liveness() reports random-campaign transition-fire coverage only when the sampled Petri-net walk also preserves finite [0, 1] markings before any controller-style clipping. If a firing step produces an out-of-range or non-finite marking, the report includes marking_bounds_valid=false, records the first violating transition, and returns live=false.

StochasticPetriNet

StochasticPetriNet()

Stochastic Petri Net with sparse matrix representation.

Usage::

net = StochasticPetriNet()
net.add_place("P_red",   initial_tokens=1.0)
net.add_place("P_green", initial_tokens=0.0)
net.add_transition("T_r2g", threshold=0.5)
net.add_arc("P_red", "T_r2g", weight=1.0)
net.add_arc("T_r2g", "P_green", weight=1.0)
net.compile()

n_places property

n_places

Number of places in the net.

n_transitions property

n_transitions

Number of transitions in the net.

place_names property

place_names

Place names in insertion order.

transition_names property

transition_names

Transition names in insertion order.

is_compiled property

is_compiled

Whether the net has been compiled.

last_validation_report property

last_validation_report

The most recent validation report, or None if not yet validated.

add_place

add_place(name, initial_tokens=0.0)

Add a place (state variable) with token density in [0, 1].

add_transition

add_transition(name, threshold=0.5, delay_ticks=0)

Add a transition (logic gate) with a firing threshold.

add_arc

add_arc(source, target, weight=1.0, inhibitor=False)

Add a directed arc between a Place and a Transition (either direction).

Valid arcs

Place -> Transition (input arc, stored in W_in) Transition -> Place (output arc, stored in W_out)

Parameters

inhibitor : bool, default False If True, arc is encoded as a negative weight inhibitor input arc. Only valid for Place -> Transition edges. Controller artifact export and runtime admission reject this negative matrix encoding until the artifact topology carries inhibitor arcs explicitly.

Raises ValueError for same-kind connections or unknown nodes.

compile

compile(validate_topology=False, strict_validation=False, allow_inhibitor=False)

Build sparse W_in and W_out matrices from the arc list.

Parameters

validate_topology : bool, default False Compute and store topology diagnostics in :attr:last_validation_report. strict_validation : bool, default False If True, raise ValueError when topology diagnostics contain dead nodes or unseeded place cycles. allow_inhibitor : bool, default False Allow negative Place -> Transition weights for inhibitor arcs. If False, compile fails when inhibitor arcs are present. This mode is suitable for structure/formal-analysis paths; controller artifacts fail closed on negative input weights.

validate_topology

validate_topology()

Return topology diagnostics without mutating compiled matrices.

Diagnostics: - dead_places: places with zero total degree. - dead_transitions: transitions with zero total degree. - unseeded_place_cycles: place-level SCC cycles with no initial token mass. - input_weight_overflow_transitions: transitions whose positive input arc-weight sum exceeds 1.0.

get_initial_marking

get_initial_marking()

Return (n_places,) float64 vector of initial token densities.

get_thresholds

get_thresholds()

Return (n_transitions,) float64 vector of firing thresholds.

get_delay_ticks

get_delay_ticks()

Return (n_transitions,) int64 vector of transition delay ticks.

summary

summary()

Human-readable summary of the net.

verify_boundedness

verify_boundedness(n_steps=500, n_trials=100)

Verify that markings stay in [0, 1] under random firing.

Runs n_trials random trajectories of n_steps each, checking that all markings remain in [0, 1] at every step.

Returns

dict {"bounded": bool, "max_marking": float, "min_marking": float, "n_trials": int, "n_steps": int}

verify_liveness

verify_liveness(n_steps=200, n_trials=1000)

Verify that all transitions fire at least once in random campaigns.

The random campaign also validates every raw post-firing marking before controller-style clipping. A campaign that reaches a non-finite marking or a marking outside [0, 1] is reported as not live because the transition-fire percentages were measured on an invalid walk.

Returns

dict {"live": bool, "transition_fire_pct": dict[str, float], "min_fire_pct": float, "marking_bounds_valid": bool, "marking_violation": dict[str, object] | None}

Formal Verification

FormalPetriNetVerifier uses exact explicit-state reachability over the compiled Petri-net transition relation. backend="auto" records that explicit-state backend and does not relabel the result as z3 just because the optional solver package is importable. backend="z3" is an explicit opt-in and requires the optional z3-solver package; SMT-specific report artefacts remain on the separate z3 model-checking surface below.

FormalPetriNetVerifier

FormalPetriNetVerifier(net, *, backend='auto')

Bounded formal verifier for compiled StochasticPetriNet objects.

This verifier performs exact explicit-state bounded reachability over rational markings; it does not execute an SMT solver. backend='auto' and backend='explicit-state' both resolve to the explicit-state engine and are labelled as such. z3-backed evidence is produced by the separate Z3BoundedModelChecker (scpn_control.scpn.z3_model_checking); this verifier never stamps a z3 backend it did not execute.

analyze_reachability

analyze_reachability(*, max_depth)

Enumerate all markings reachable within max_depth firings.

prove_marking_bounds

prove_marking_bounds(bounds, *, max_depth)

Prove that all reachable markings satisfy per-place bounds.

prove_place_invariants

prove_place_invariants(invariants, *, max_depth)

Prove algebraic place invariants and bounded reachable consistency.

prove_transition_liveness

prove_transition_liveness(*, max_depth)

Prove bounded transition liveness by checking every transition fires on some path.

verify_temporal_specs

verify_temporal_specs(specs, *, max_depth)

Verify bounded temporal safety/liveness specifications.

verify_ctl_specs

verify_ctl_specs(specs, *, max_depth)

Verify bounded CTL formulas over the compiled Petri-net relation.

verify_ltl_specs

verify_ltl_specs(specs, *, max_depth)

Verify bounded LTL formulas over all finite firing paths.

verify_formal_contracts

verify_formal_contracts(net, *, max_depth, marking_bounds, temporal_specs=None, backend='auto')

Run reachability, marking-bound, liveness, and temporal proof obligations.

PlaceInvariant dataclass

PlaceInvariant(name, weights)

Algebraic P-invariant over named places.

The verifier proves that sum(weights[p] * marking[p]) is unchanged by every transition and then checks the bounded reachable graph against the same invariant value. This is a structural proof obligation, not a sampled simulation assertion.

CTLFormula dataclass

CTLFormula(name, operator, target, params)

Bounded CTL formula over a compiled SCPN transition system.

Supported operators are the certification-relevant bounded subset that can produce exact counterexamples over the finite firing-depth transition relation: AG safety, EF reachability, and AG_EF recurrence.

certificate_name property

certificate_name

Return the certificate-stable formula name.

ag_bounded classmethod

ag_bounded(name, bounds)

Create AG marking-bound safety: all paths always satisfy bounds.

ef_fires classmethod

ef_fires(name, transition)

Create EF transition reachability: some path eventually fires.

ag_not_comarked classmethod

ag_not_comarked(name, place_a, place_b, *, threshold=0.0)

Create AG mutual-exclusion safety for two marked places.

ag_ef_marked classmethod

ag_ef_marked(name, place, *, threshold=0.0)

Create AG EF recurrence: every reachable state can recover marking.

LTLFormula dataclass

LTLFormula(name, operator, target, params)

Bounded LTL formula over all finite firing paths of a compiled SCPN.

certificate_name property

certificate_name

Return the certificate-stable formula name.

globally_bounded classmethod

globally_bounded(name, bounds)

Create G marking-bound safety over every bounded path.

eventually_fires classmethod

eventually_fires(name, transition)

Create bounded F transition reachability.

globally_fire_leads_to_marking classmethod

globally_fire_leads_to_marking(name, trigger_transition, target_place, *, threshold=0.0, within=1)

Create G(trigger -> F<=within marked) response checking.

AlwaysBounded dataclass

AlwaysBounded(name, bounds)

Temporal safety spec: all reachable markings keep named places in bounds.

AlwaysEventuallyMarked dataclass

AlwaysEventuallyMarked(name, place, threshold=0.0)

Bounded recurrence spec: every reachable path can reach a marked place.

EventuallyFires dataclass

EventuallyFires(name, transition)

Bounded liveness spec: a transition fires on at least one reachable path.

FireLeadsToMarking dataclass

FireLeadsToMarking(name, trigger_transition, target_place, threshold=0.0, within=1)

Bounded response spec: a transition firing is followed by a marked place.

NeverCoMarked dataclass

NeverCoMarked(name, place_a, place_b, threshold=0.0)

Temporal safety spec: two places are never simultaneously marked above threshold.

Formal Safety Certificate

The bounded safety certificate captures a formal verification report together with its admission policy and an optional artifact binding; the bundle aggregates independent certificates for a controller release gate. Every payload is schema-versioned, self-digested, and fail-closed.

SafetyCertificatePolicy dataclass

SafetyCertificatePolicy(name, min_depth=0, require_artifact_binding=False, require_ctl=False, require_ltl=False, required_checked_specs=())

Admission policy for generated formal safety certificates.

certification_gate classmethod

certification_gate(*, name='bounded-ctl-ltl-artifact-gate', min_depth=1, required_checked_specs=())

Create a fail-closed policy requiring artifact binding plus CTL and LTL evidence.

SafetyCertificateBundlePolicy dataclass

SafetyCertificateBundlePolicy(name, min_certificates=1, required_policy_name=None, require_same_artifact=False, require_same_backend=False)

Admission policy for a bundle of independent safety certificates.

build_safety_certificate_payload

build_safety_certificate_payload(report, *, ctl_report=None, ltl_report=None, artifact_sha256=None, issuer='scpn-control', policy=None)

Build a schema-versioned tamper-evident formal safety certificate.

build_safety_certificate_bundle_payload

build_safety_certificate_bundle_payload(certificates, *, policy=None)

Build a schema-versioned tamper-evident safety certificate bundle.

build_safety_certificate_bundle_artifact

build_safety_certificate_bundle_artifact(*, bundle_uri, bundle_sha256, producer, created_at)

Build a schema-versioned reference to a persisted certificate bundle.

generate_safety_certificate

generate_safety_certificate(net, *, max_depth, marking_bounds, json_path, markdown_path, temporal_specs=None, ctl_specs=None, ltl_specs=None, artifact_path=None, artifact_sha256=None, issuer='scpn-control', backend='explicit-state', policy=None)

Run proof obligations and persist a bound formal safety certificate.

The workflow resolves one verifier backend, runs base Petri-net safety, liveness, optional CTL, and optional LTL obligations through that same transition relation, binds the certificate to optional controller artifact bytes, and writes JSON/Markdown evidence in one call.

validate_safety_certificate_payload

validate_safety_certificate_payload(payload)

Validate a schema-versioned formal safety certificate payload.

validate_safety_certificate_bundle_payload

validate_safety_certificate_bundle_payload(payload)

Validate a schema-versioned formal safety certificate bundle.

validate_safety_certificate_bundle_artifact

validate_safety_certificate_bundle_artifact(artifact, *, artifact_root=None)

Validate a persisted certificate-bundle artifact reference.

admit_safety_certificate_bundle_artifact

admit_safety_certificate_bundle_artifact(artifact, *, artifact_root)

Load and validate the bundle referenced by a certificate-bundle artifact.

write_safety_certificate

write_safety_certificate(report, *, json_path, markdown_path, ctl_report=None, ltl_report=None, artifact_sha256=None, issuer='scpn-control', policy=None)

Persist a formal safety certificate as JSON and Markdown artifacts.

write_safety_certificate_bundle

write_safety_certificate_bundle(certificates, *, json_path, markdown_path, policy=None)

Persist a formal safety certificate bundle as JSON and Markdown artifacts.

Runtime-Bound Safety Certificate

RuntimeTarget dataclass

RuntimeTarget(name, architecture, runtime, toolchain)

The firmware / runtime target a certified controller is admitted onto.

as_dict

as_dict()

Return the runtime target as a JSON-serialisable mapping.

digest

digest()

Return the SHA-256 digest of the runtime target.

TimingEnvelope dataclass

TimingEnvelope(control_period_us, worst_case_response_us, deadline_us, proof_firing_depth)

Declared bounded-runtime assumptions the certificate is valid under.

proof_firing_depth is the bounded transition depth the formal proof covered; the timing fields assert each control tick completes inside its deadline within the control period. The envelope is schedulable only when 0 < worst_case_response_us <= deadline_us <= control_period_us.

schedulable property

schedulable

Whether worst_case_response_us <= deadline_us <= control_period_us.

as_dict

as_dict()

Return the timing envelope as a JSON-serialisable mapping.

digest

digest()

Return the SHA-256 digest of the timing envelope.

ControllerRuntimeBinding dataclass

ControllerRuntimeBinding(controller_id, controller_config, petri_topology_sha256, snn_parameters, solver_mode, runtime_target, timing_envelope)

The exact deployable controller identity a certificate is matched to.

canonical

canonical()

Canonical, JSON-serialisable view used for the binding digest.

digest

digest()

Return the SHA-256 digest of the canonical controller binding.

CertificateReplayResult dataclass

CertificateReplayResult(topology_matches, holds_matches, checked_specs_match, formal_digest_matches, detail=list())

Outcome of re-proving the obligations a certificate claims.

passed property

passed

Whether the topology, holds, checked specs, and formal digest all matched.

compute_petri_topology_digest

compute_petri_topology_digest(net)

Return a canonical SHA-256 of a compiled net's topology (not its tokens).

The signature covers the place ordering, each transition's name, firing threshold and delay, and the compiled input/output incidence (which encodes arc weights, including inhibitor arcs as negative input weights). Initial token densities are deliberately excluded: the certificate binds the control structure, not a transient marking.

issue_runtime_safety_certificate

issue_runtime_safety_certificate(net, binding, *, formal_certificate, issuer='scpn-control')

Issue a runtime-bound certificate only when proof and binding agree.

Fails closed when the binding's declared topology does not match the net the proof ran on, when the embedded formal certificate is malformed, or when it does not hold.

validate_runtime_safety_certificate_payload

validate_runtime_safety_certificate_payload(payload)

Validate a runtime safety certificate's structure, digests, and bindings.

replay_runtime_safety_certificate

replay_runtime_safety_certificate(net, certificate, *, reverify)

Re-prove the certified obligations and compare them to the certificate.

reverify must re-run the formal proof on the same net and binding and return a fresh scpn-control.safety-certificate.v1 payload. The replay passes only when the live net topology still matches the certificate's binding, the fresh proof still holds, and it covers the same checked specs and produces the same formal digest.

assert_runtime_certificate_admissible

assert_runtime_certificate_admissible(certificate, *, live_binding, live_runtime_target, replay)

Fail closed unless a certificate may back a facility-facing safety claim.

Admission requires, all on the declared stack: the certificate's binding digest matches the live controller binding, its declared runtime target matches the live target, its timing envelope matches the live binding, and a fresh proof replay reproduced the certified obligations. Validation (called first) already guarantees the certificate holds, and a TimingEnvelope cannot be constructed unschedulable, so those guarantees are upstream rather than re-checked here. Any failure raises; on success the validated certificate is returned.

Z3BoundedModelChecker

Z3BoundedModelChecker(net)

Bounded SMT model checker for compiled StochasticPetriNet graphs.

prove_marking_bounds

prove_marking_bounds(bounds, *, max_depth, weight_bounds=None)

Prove marking bounds by asking Z3 for a reachable counterexample.

verify_temporal_specs

verify_temporal_specs(specs, *, max_depth, weight_bounds=None)

Verify supported bounded temporal specifications with Z3.

verify_ctl_specs

verify_ctl_specs(specs, *, max_depth)

Verify bounded CTL formulas with the Z3 transition relation.

verify_ltl_specs

verify_ltl_specs(specs, *, max_depth)

Verify bounded LTL formulas with the Z3 transition relation.

verify_trigger_response_latency

verify_trigger_response_latency(*, trigger_transition, response_transition, max_latency_ns, tick_period_ns=10.0, max_depth, weight_bounds=None)

Verify a bounded trigger->response latency contract in transition steps.

The bound is interpreted as: at most ceil(max_latency_ns / tick_period_ns) non-idle transition steps from a trigger fire to the first subsequent response fire. Idle stalls are intentionally discharged by the separate no-stall SymbiYosys contract rather than conflated with trigger-response latency.

build_symbiyosys_contract

build_symbiyosys_contract(*, max_depth, weight_bounds=None, trigger_transition=None, response_transition=None, max_latency_ns=50.0, tick_period_ns=10.0, no_stall_window_ns=50.0)

Build a bounded symbolic contract for SymbiYosys/SMTBMC review.

The emitted contract uses assume bounds for hot-swappable transition weights and assert obligations for bounded trigger->response latency and bounded non-stall progression.

Z3 Formal Report I/O

verify_z3_formal_contracts

verify_z3_formal_contracts(net, *, max_depth, marking_bounds, temporal_specs=None, weight_bounds=None)

Run bounded Z3 safety and temporal proof obligations.

build_z3_formal_report_payload

build_z3_formal_report_payload(report)

Return the schema-versioned JSON payload for a bounded Z3 report.

build_blocked_z3_formal_report_payload

build_blocked_z3_formal_report_payload(reason)

Return a schema-versioned blocked report for unavailable SMT evidence.

validate_z3_formal_report_payload

validate_z3_formal_report_payload(payload)

Validate a schema-versioned Z3 formal evidence report.

load_z3_formal_report

load_z3_formal_report(path)

Load and validate a duplicate-key-safe Z3 formal evidence report.

write_z3_formal_report

write_z3_formal_report(report, *, json_path, markdown_path)

Persist a bounded SMT report as JSON and Markdown evidence artifacts.

FusionCompiler

FusionCompiler

FusionCompiler(bitstream_length=1024, seed=42, *, lif_tau_mem=1000000.0, lif_noise_std=0.0, lif_dt=1.0, lif_resistance=1.0, lif_refractory_period=0)

Compiles a StochasticPetriNet into a CompiledNet.

Parameters

bitstream_length : Number of bits per stochastic stream (default 1024). seed : Base RNG seed for reproducibility.

with_reactor_lif_defaults classmethod

with_reactor_lif_defaults(bitstream_length=1024, seed=42, *, lif_dt=1.0, lif_resistance=1.0, lif_refractory_period=0)

Create a compiler with reactor-like LIF defaults.

Keeps the legacy constructor defaults untouched for compatibility while exposing a realistic preset for new control experiments.

traceable_runtime_kwargs staticmethod

traceable_runtime_kwargs(*, runtime_backend='auto', allow_runtime_backend_fallback=False, allow_legacy_runtime_backend_fallback=False)

Recommended controller kwargs for traceable runtime loops.

compile

compile(net, firing_mode='binary', firing_margin=0.05, *, allow_inhibitor=False, validate_topology=False, strict_topology=False)

Compile the Petri Net into sc_neurocore artifacts.

Parameters

net : compiled StochasticPetriNet. firing_mode : "binary" (default) or "fractional". firing_margin : margin for fractional firing (ignored in binary mode). allow_inhibitor : enable inhibitor arc compilation. This is not a controller-artifact runtime contract; artifact export rejects negative inhibitor weights until topology serialization carries inhibitor arcs explicitly. validate_topology : run topology diagnostics during compile. strict_topology : raise if topology diagnostics detect issues.

Steps
  1. Extract dense W_in (nT x nP) and W_out (nP x nT).
  2. Create one LIF neuron per transition (pure threshold comparator).
  3. Pre-encode weight matrices as packed uint64 bitstreams.
  4. Return CompiledNet with all artifacts.

CompiledNet

CompiledNet dataclass

CompiledNet(n_places, n_transitions, place_names, transition_names, W_in, W_out, W_in_packed=None, W_out_packed=None, neurons=list(), bitstream_length=1024, thresholds=(lambda: array([], dtype=float64))(), transition_delay_ticks=(lambda: array([], dtype=int64))(), initial_marking=(lambda: array([], dtype=float64))(), seed=42, firing_mode='binary', firing_margin=0.05, lif_tau_mem=1000000.0, lif_noise_std=0.0, lif_dt=1.0, lif_resistance=1.0, lif_refractory_period=0)

Compiled Petri Net ready for sc_neurocore execution.

Holds both the dense float matrices (for validation / fallback) and pre-packed uint64 weight bitstreams (for the stochastic path).

has_stochastic_path property

has_stochastic_path

Whether the compiled net has a packed stochastic-bitstream path.

dense_forward

dense_forward(W_packed, input_probs)

Stochastic matrix-vector product via AND + popcount.

Parameters

W_packed : (n_out, n_in, n_words) uint64 — pre-packed weight bitstreams. input_probs : (n_in,) float64 — input probabilities in [0, 1].

Returns

output : (n_out,) float64 — stochastic estimate of W @ input_probs.

dense_forward_float

dense_forward_float(W, inputs)

Float-path validation: simple W @ inputs.

lif_fire

lif_fire(currents)

Run LIF threshold detection on all transitions.

Binary mode: f_t = 1 if current >= threshold else 0 Fractional mode: f_t = clip((current - threshold) / margin, 0, 1)

Parameters

currents : (n_transitions,) float64 — weighted-sum activations.

Returns

fired : (n_transitions,) float64 vector. Binary mode → values in {0.0, 1.0}. Fractional mode → values in [0.0, 1.0].

summary

summary()

Return a one-line summary of the compiled net's size and mode.

export_artifact

export_artifact(name='controller', dt_control_s=0.001, readout_config=None, injection_config=None)

Build an Artifact from compiled state + user-provided config.

Parameters

name : artifact name. dt_control_s : control tick period (s). readout_config : dict with actions, gains, abs_max, slew_per_s lists. Required for a complete artifact. injection_config : list of place-injection dicts.

Raises

ValueError If the compiled input matrix contains negative inhibitor weights. Controller artifacts do not yet carry inhibitor topology, so export fails closed instead of serializing ambiguous negative weights.

Inhibitor arcs remain a structure/formal-analysis feature. A caller may compile them only with allow_inhibitor=True, which stores the guard as a negative input-matrix entry for analysis paths that still inspect the original Petri-net structure. Controller artifacts and controller runtime do not encode inhibitor topology yet: CompiledNet.export_artifact(), load_artifact(), and NeuroSymbolicController reject negative dense w_in weights instead of serializing ambiguous inhibitor semantics.

NeuroSymbolicController

NeuroSymbolicController rejects nonzero sc_bitflip_rate unless allow_fault_injection=True is supplied explicitly and the process environment sets SCPN_ALLOW_CONTROLLER_FAULT_INJECTION=1. Bit-flip mutation is a double-gated fault-injection test mode, not a production control default.

Controller JSONL logging requires an explicit log_root whenever log_path is provided. Relative and absolute log paths must resolve under that root and use a .jsonl suffix before any file is opened. Log appends use a constrained append helper that rejects symlink targets where the platform exposes no-follow open semantics.

Runtime-bound safety admission is available through the explicit runtime_safety_certificate, runtime_safety_binding, runtime_safety_target, and runtime_safety_replay constructor arguments. Supplying any one of these requires all four. The controller first checks that the runtime binding's Petri topology digest matches the loaded .scpnctl artifact, then delegates to assert_runtime_certificate_admissible. This keeps ordinary local experiments unchanged while making safety-critical construction fail closed unless artifact, binding, target, and proof replay evidence all match.

NeuroSymbolicController

NeuroSymbolicController(artifact, seed_base, targets, scales, sc_n_passes=8, sc_bitflip_rate=0.0, allow_fault_injection=False, sc_binary_margin=None, sc_antithetic=True, enable_oracle_diagnostics=True, feature_axes=None, runtime_profile='adaptive', runtime_backend='auto', allow_runtime_backend_fallback=False, allow_legacy_runtime_backend_fallback=False, rust_backend_min_problem_size=1, sc_antithetic_chunk_size=2048, runtime_safety_certificate=None, runtime_safety_binding=None, runtime_safety_target=None, runtime_safety_replay=None)

Reference controller with oracle float and stochastic paths.

Parameters

artifact : loaded .scpnctl.json artifact. seed_base : 64-bit base seed for deterministic stochastic execution. targets : control setpoint targets. scales : normalisation scales. allow_fault_injection : explicit opt-in for stochastic bit-flip fault injection. Nonzero sc_bitflip_rate is rejected unless this is true. runtime_safety_certificate : optional runtime-bound formal certificate. When provided, runtime_safety_binding, runtime_safety_target, and runtime_safety_replay are also required and must pass admission before the controller instance is usable.

max_delay_ticks property

max_delay_ticks

Largest transition firing delay in ticks (0 if all transitions are immediate).

A transition whose delay exceeds a run's length can never fire within that run, so the readout follows the injected features without any symbolic-transition contribution. Benchmarks disclose this so a non-firing symbolic lane cannot be mistaken for a contributing one.

readout_reads_transition_outputs property

readout_reads_transition_outputs

True if any readout place receives tokens from a transition (production arc).

When the readout reads only injected/input places, the timed transitions cannot influence the control output — the symbolic lane is a bypassed branch. Reading a place that a transition produces into is what makes the symbolic lane load-bearing. Benchmarks use this to disclose genuine contribution honestly rather than asserting it.

runtime_backend_name property

runtime_backend_name

Name of the active controller runtime backend.

runtime_profile_name property

runtime_profile_name

Name of the active controller runtime profile.

runtime_safety_admitted property

runtime_safety_admitted

Whether a runtime safety certificate was admitted for this instance.

marking property writable

marking

Current Petri-net marking as a list of per-place token densities.

reset

reset()

Restore initial marking and zero previous actions.

step

step(obs, k, log_path=None, log_root=None)

Execute one control tick.

obs accepts the existing mapping contract or a typed AERControlObservation decoded through the same feature-injection path.

Steps
  1. extract_features(obs) → 4 unipolar features
  2. _inject_places(features)
  3. _oracle_step() — float path (optional)
  4. _sc_step(k) — deterministic stochastic path
  5. _decode_actions() — gain × differencing, slew + abs clamp
  6. Optional JSONL logging

step_traceable

step_traceable(obs_vector, k, log_path=None, log_root=None)

Execute one control tick from a fixed-order observation vector.

The vector order is self._axis_obs_keys. This avoids per-step key lookups/dict allocation in tight control loops.

Contracts

scpn_control.scpn.contracts owns the shared numeric kernels used by both the public contract helpers and the live controller runtime. extract_features() and NeuroSymbolicController.step() use the same signed-error component kernel; decode_actions() and the controller readout use the same slew-rate and absolute-limit action-vector kernel. This keeps standalone contract checks and compiled controller execution aligned.

The scpn_control.scpn package root re-exports FeatureAxisSpec, the shared feature/action kernels, and the runtime-safety certificate dataclasses and helpers so installed-package consumers do not need to reach into private module paths for controller construction and certificate admission.

ControlObservation

Bases: TypedDict

Plant observation at a single control tick.

ControlAction

Bases: TypedDict

Actuator command output for a single control tick.

Keys are dynamic and depend on the Petri Net readout configuration.

ControlTargets dataclass

ControlTargets(R_target_m=6.2, Z_target_m=0.0)

Setpoint targets for the control loop.

extract_features

extract_features(obs, targets, scales, feature_axes=None, passthrough_keys=None)

Map observation → unipolar [0, 1] feature sources.

By default returns keys x_R_pos, x_R_neg, x_Z_pos, x_Z_neg. Custom feature mappings can be supplied via feature_axes for arbitrary observation dictionaries.

feature_error_components

feature_error_components(obs_values, targets, scales, *, axis_names=None, out_pos=None, out_neg=None)

Return positive and negative unipolar feature components.

This is the shared signed-error kernel for the public contract helper and the live controller runtime. Each component is computed from (target - observation) / scale, clamped to [-1, 1], then split into positive and negative [0, 1] channels.

decode_actions

decode_actions(marking, actions_spec, gains, abs_max, slew_per_s, dt, prev)

Decode marking → actuator commands with gain, slew-rate, and abs clamp.

Parameters

marking : current marking vector (len >= max place index). actions_spec : per-action pos/neg place definitions. gains : per-action gain multiplier. abs_max : per-action absolute saturation. slew_per_s : per-action max change rate (units/s). dt : control tick period (s). prev : previous action outputs (same length as actions_spec).

Returns

dict mapping action name → clamped value. Also mutates prev in-place.

decode_action_vector

decode_action_vector(marking, pos_places, neg_places, gains, abs_max, slew_per_s, dt, prev, *, work=None)

Decode marking to a vector of slew- and magnitude-limited actions.

The returned array is prev after in-place update. work may be a caller-owned scratch array and is never returned.

Artifacts

Safety-critical controller admission must call load_artifact(..., require_formal_verification=True) or validate_safety_critical_artifact(). That gate rejects missing, blocked, failed, malformed, or unbounded proof evidence and accepts only hash-addressed bounded proof manifests tied to the compiled controller artifact. The proof manifest must include the canonical artifact payload SHA-256, report SHA-256, bounded proof depth, checked specification names, backend/solver metadata, and a safe relative report URI. Controller artifacts also carry meta.firing_margin, the default fractional firing margin used when a transition omits its own margin. The compiler writes this value, artifact loading validates it as finite and non-negative, and the controller consumes it directly instead of falling back to an implicit runtime constant. Legacy artifacts without the field still load with the historical 0.05 default so archived evidence remains readable. Artifact(...) validates direct construction by default, load_artifact() calls the same public validate_artifact() surface after parsing, and NeuroSymbolicController calls validate_artifact() before runtime arrays, runtime certificates, or controller state are admitted. Validator branch tests can pass validate_on_init=False only to build a deliberately malformed object; that object is still rejected by validate_artifact(), save_artifact(), and controller construction. When callers provide formal_report_root, the loader resolves the report URI under that root and verifies the report bytes against the declared SHA-256. Z3 and Lean report files reached through an artifact manifest are loaded through duplicate-key-safe and schema-strict public report loaders. Z3 reports are additionally schema-versioned as scpn-control.z3-formal-report.v1, carry a canonical payload SHA-256 over the proof payload, reject unknown top-level and proof-section fields, schema-check serialized counterexample records, enforce solver-status/holds/counterexample consistency, reject counterexamples on unknown solver sections, and must match the manifest status, solver, proof depth, and checked specification list before a safety-critical artifact is admitted. Each Z3 proof section must also carry unique non-empty checked_specs; duplicate section obligations are rejected before top-level report/spec matching. Blocked Z3 reports are not proof evidence: they must use the unavailable solver label, zero proof depth, and only the z3_solver_available checked specification. Pass/fail Z3 reports must identify z3-solver and must not use the unavailable-solver label; a missing z3-solver dependency is admitted only as blocked SMT evidence. Lean 4 reports are admitted only through the bounded lean4 manifest path: the manifest must bind a solver string that identifies Lean and includes the declared Lean version, Lake file SHA-256, proof-source SHA-256, theorem names, theorem modules, proved contracts, linked production module references, safety-case identifiers, checked specification list, report SHA-256, and compiled artifact SHA-256. Production module references should use importable module names such as scpn_control.scpn.controller so installed wheels and sdists do not depend on a repository src/ checkout; legacy safe relative source paths remain accepted for existing reports. The Lean report schema is scpn-control.lean4-formal-report.v1; when a report root is supplied, every manifest field above must match the report before admission. The current required Lean proof-contract surface covers PID actuator-saturation preservation and SNN/neuro-symbolic marking-bound preservation, and reports cannot list unsupported proved contracts to imply wider formal coverage. This is an evidence admission contract; it does not claim certification unless the referenced machine-checked proof artefacts are present and verified. The admitted PID/SNN surface is also exact-linked: theorem modules, theorem names, linked production module references, and safety-case identifiers must remain inside the expected PID/SNN namespaces and module references instead of padding a valid report with unrelated evidence. get_artifact_json_schema() returns the current .scpnctl.json Draft-07 schema from the same payload sections and dataclass field sets used by save_artifact() and load_artifact(). The schema declares the serialized meta.firing_margin, closed dense-weight and readout objects, the raw data_u64 packed-weight form, the compact u64-le-zlib-base64 packed-weight form, and the closed formal_verification manifest fields admitted by the runtime validator.

Artifact dataclass

Artifact(meta, topology, weights, readout, initial_state, formal_verification=None, validate_on_init=True)

Full SCPN controller artifact (.scpnctl.json).

nP property

nP

Number of places in the topology.

nT property

nT

Number of transitions in the topology.

__post_init__

__post_init__(validate_on_init)

Validate direct artifact construction unless explicitly disabled.

FormalVerificationEvidence dataclass

FormalVerificationEvidence(required, status, backend, solver, max_depth, checked_specs, artifact_sha256, report_sha256, claim_boundary, report_uri=None, generated_utc=None, counterexample_path=None, counterexample_property=None, lean_version=None, lakefile_sha256=None, proof_source_sha256=None, theorem_names=None, theorem_modules=None, proved_contracts=None, module_paths=None, safety_case_ids=None, proof_assumptions=None, assumption_sha256=None)

Hashable bounded-proof admission evidence for controller artifacts.

compute_artifact_payload_sha256

compute_artifact_payload_sha256(artifact)

Compute a canonical SHA-256 digest over artifact payload sections.

validate_artifact

validate_artifact(artifact)

Validate a controller artifact before runtime use.

Parameters

artifact Parsed or directly constructed artifact to validate.

Raises

ArtifactValidationError If metadata, topology, weights, readout, initial state, or formal evidence fields violate the controller artifact contract.

get_artifact_json_schema

get_artifact_json_schema()

Return the JSON schema for current .scpnctl.json artifact payloads.

Returns

dict[str, Any] Draft-07 JSON schema generated from the same dataclass fields, serializer sections, packed-weight codec variants, and formal-proof evidence field set admitted by load_artifact() and save_artifact().

save_artifact

save_artifact(artifact, path, compact_packed=False)

Serialize an Artifact to indented JSON.

load_artifact

load_artifact(path, require_formal_verification=False, formal_report_root=None)

Parse a .scpnctl.json file into an Artifact dataclass.

validate_safety_critical_artifact

validate_safety_critical_artifact(artifact, formal_report_root=None)

Fail closed unless a controller artifact carries passing bounded-proof evidence.

LeanFormalVerificationReport dataclass

LeanFormalVerificationReport(status, solver, lean_version, checked_specs, artifact_sha256, proof_source_sha256, lakefile_sha256, theorem_names, theorem_modules, proved_contracts, module_paths, safety_case_ids, claim_boundary, proof_assumptions)

Machine-checkable Lean 4 report metadata for bounded controller claims.

build_lean_formal_report_payload

build_lean_formal_report_payload(report)

Build a canonical Lean formal-verification report payload.

validate_lean_formal_report_payload

validate_lean_formal_report_payload(payload, *, require_payload_sha256=True)

Validate a Lean formal-verification report payload.

write_lean_formal_report

write_lean_formal_report(report, path)

Write a canonical Lean formal-verification report and return the payload.

load_lean_formal_report

load_lean_formal_report(path)

Load and validate a Lean formal-verification report payload.

load_lean_formal_report() and the validation executable validation/validate_scpn_lean_formal.py validate Lean report JSON with duplicate-key rejection and can additionally call load_artifact(..., require_formal_verification=True) against a supplied artifact and report root. This is the release-gate path for admitting Lean proof evidence without running long proof jobs inside the Python test suite. The Lean report and artifact manifest validators also enforce namespace coverage for required controller contracts: PID actuator-saturation evidence must include a ScpnControl.PID theorem module and theorem name, while SNN/neuro-symbolic marking-bound evidence must include a ScpnControl.SNN theorem module and theorem name. Lean evidence must also declare explicit bounded proof_assumptions and a canonical assumption_sha256; report and artifact admission reject unbounded assumptions, certification overclaims, malformed assumption digests, or manifest/report assumption mismatches. Admission also rejects non-Lean solver declarations, Lean solver strings that do not include the reported lean_version, and any proved_contracts outside the currently admitted PID/SNN proof surface. Reports also fail closed when theorem_names, theorem_modules, module_paths, or safety_case_ids include unrelated entries outside the admitted PID/SNN proof boundary. Despite the historical field name, module_paths admits importable module references for installed packages and legacy safe relative source paths for existing reports. The same checks run on the .scpnctl artifact manifest itself, even without a report root, so safety-critical artifact loading cannot admit a stale or padded Lean manifest before report-byte comparison is available. Both Lean report payloads and artifact formal_verification manifests are closed schemas: unknown proof fields are rejected rather than ignored. External Lean reports must also carry the canonical payload_sha256 self-digest; reports that omit it are rejected before safety-critical artifact admission.


Phase — Paper 27 Dynamics

Kuramoto-Sakaguchi Step

kuramoto_sakaguchi_step

kuramoto_sakaguchi_step(theta, omega, *, dt, K, alpha=0.0, zeta=0.0, psi_driver=None, psi_mode='external', wrap=True)

Single Euler step of mean-field Kuramoto-Sakaguchi + global driver.

dθ_i/dt = ω_i + K·R·sin(ψ_r − θ_i − α) + ζ·sin(Ψ − θ_i)

Uses the optional Rust backend when available for wrapped phase updates.

kuramoto_sakaguchi_step() may dispatch to the optional Rust backend for wrapped phase updates. Treat latency as benchmark-context evidence; source comments and docstrings do not make fixed timing claims.

order_parameter

order_parameter(theta, weights=None)

Kuramoto order parameter R·exp(i·ψ_r) = / W.

Returns (R, ψ_r).

lyapunov_v

lyapunov_v(theta, psi)

Lyapunov candidate V(t) = (1/N) Σ (1 − cos(θ_i − Ψ)).

V=0 at perfect sync (all θ_i = Ψ), V=2 at maximal desync. Range: [0, 2]. Mirror of control-math/kuramoto.rs::lyapunov_v.

lyapunov_exponent

lyapunov_exponent(v_hist, dt)

Estimate the finite-history Lyapunov change rate.

The input is a sequence of sampled, non-negative Lyapunov candidate values. Endpoint values are floored at LYAPUNOV_VALUE_FLOOR before taking the log ratio, which keeps the heuristic finite near perfect synchrony. The elapsed time is (n_samples - 1) * dt because the history contains sampled states rather than elapsed intervals.

lyapunov_exponent() validates a positive finite dt and finite, non-negative V(t) samples. Its heuristic floors only the initial and final sample at LYAPUNOV_VALUE_FLOOR before the log ratio, then divides by (n_samples - 1) * dt because the input is a sampled state history.

wrap_phase

wrap_phase(x)

Map phases to (-π, π].

GlobalPsiDriver dataclass

GlobalPsiDriver(mode='external')

Resolve the global field phase Ψ.

mode="external" : Ψ supplied by caller (intention/carrier, no dotΨ). mode="mean_field" : Ψ = arg() from the oscillator population.

resolve

resolve(theta, psi_external)

Resolve the reference phase for the configured coupling mode.

Parameters

theta The current oscillator phases. psi_external The externally supplied reference phase, required (and must be finite) when mode == "external".

Returns

float The resolved reference phase.

Raises

ValueError If mode == "external" and psi_external is missing or non-finite.

KuramotoRuntimeEvidence dataclass

KuramotoRuntimeEvidence(schema_version, generated_utc, target_id, oscillator_count, deployment_target_oscillators, dt_s, K, alpha, zeta, psi_mode, wrap, input_sha256, python_reference_sha256, rust_available, rust_parity_checked, rust_theta_max_abs_error_rad, rust_order_parameter_abs_error, parity_tolerance, parity_passed, timestep_refinement_checked, timestep_refinement_error_rad, timestep_refinement_tolerance, timestep_refinement_passed, deployment_claim_allowed, claim_status, payload_sha256)

Tamper-evident phase-runtime parity and timestep-refinement evidence.

kuramoto_runtime_evidence

kuramoto_runtime_evidence(theta, omega, *, dt, K, alpha=0.0, zeta=0.0, psi_driver=None, psi_mode='external', wrap=True, target_id='local-python-runtime', deployment_target_oscillators=None, parity_tolerance=1e-10, timestep_refinement_tolerance=0.005, generated_utc=None, deployment_claim_allowed=False)

Build bounded phase-runtime parity and timestep-refinement evidence.

assert_kuramoto_runtime_claim_admissible

assert_kuramoto_runtime_claim_admissible(evidence)

Fail closed unless phase-runtime evidence supports deployment claims.

save_kuramoto_runtime_evidence

save_kuramoto_runtime_evidence(evidence, output_path)

Persist Kuramoto runtime evidence as sorted JSON.

load_kuramoto_runtime_evidence

load_kuramoto_runtime_evidence(path, *, require_deployment_claim=False)

Load Kuramoto runtime evidence with duplicate-key and digest admission.

Knm Coupling Matrix

KnmSpec dataclass

KnmSpec(K, alpha=None, zeta=None, layer_names=None)

Paper 27 coupling specification.

K : (L, L) coupling matrix. K[n, m] = source n -> target m. alpha : (L, L) Sakaguchi phase-lag (optional). zeta : (L,) per-layer global-driver gain ζ_m (optional).

L property

L

Number of layers L (first dimension of the coupling matrix K).

build_knm_paper27

build_knm_paper27(L=16, K_base=0.45, K_alpha=0.3, zeta_uniform=0.0)

Build the canonical Paper 27 Knm with exponential distance decay.

K[i,j] = K_base · exp(−K_alpha · |i − j|), diag(K) kept for intra-layer sync (unlike the inter-oscillator Knm which zeros diag).

K_base=0.45 and K_alpha=0.3 from Paper 27 §3.2, Eq. 12. Calibration anchors from Paper 27, Table 2. Cross-hierarchy boosts from Paper 27 §4.3.

UPDE Multi-Layer Solver

UPDESystem dataclass

UPDESystem(spec, dt=0.001, psi_mode='external', wrap=True)

Multi-layer UPDE driven by a KnmSpec.

step

step(theta_layers, omega_layers, *, psi_driver=None, actuation_gain=1.0, pac_gamma=0.0, K_override=None)

Advance all L layers by one Euler step.

Parameters

theta_layers : sequence of 1D arrays Phase vectors per layer. omega_layers : sequence of 1D arrays Natural frequencies per layer. psi_driver : float or None External global field phase Ψ (required if psi_mode="external"). actuation_gain : float Multiplicative gain on all coupling terms. pac_gamma : float PAC-like gating: boost inter-layer coupling by (1 + pac_gamma·(1 − R_source)). K_override : array or None Per-tick replacement for spec.K (adaptive coupling).

Returns

dict[str, Any] Output-state snapshot containing theta1, dtheta, R_layer, Psi_layer, R_global, Psi_global, V_layer, and V_global for the completed Euler tick. The resolved global-field driver influences dtheta; the returned Psi_global is the mean phase of theta1.

run

run(n_steps, theta_layers, omega_layers, *, psi_driver=None, actuation_gain=1.0, pac_gamma=0.0, K_override=None)

Run n_steps and return trajectory of per-layer R and global R.

run_lyapunov

run_lyapunov(n_steps, theta_layers, omega_layers, *, psi_driver=None, actuation_gain=1.0, pac_gamma=0.0, K_override=None)

Run n_steps with Lyapunov tracking.

Returns R histories, V histories, and per-layer + global λ. λ < 0 ⟹ stable convergence toward Ψ.

UPDESystem.step() returns a single output-state snapshot contract across the NumPy fallback and the optional Rust/PyO3 path. theta1, dtheta, R_layer, Psi_layer, R_global, Psi_global, V_layer, and V_global describe the same completed tick. The input psi_driver still drives the derivative term, but the returned Psi_global is the mean phase of theta1. Stale Rust bindings that cannot provide the full contract fall back to the NumPy implementation instead of returning partial snapshots.

Lyapunov Guard

LyapunovGuard

LyapunovGuard(window=50, dt=0.001, lambda_threshold=0.0, max_violations=3)

Sliding-window Lyapunov stability monitor.

Parameters

window : int Number of V(t) samples in the sliding window. dt : float Timestep between samples (for λ computation). lambda_threshold : float λ above this value counts as a violation. Default 0 (any growth). max_violations : int Consecutive violations before guard refuses.

reset

reset()

Clear the Lyapunov-value buffer and consecutive-violation counter.

check

check(theta, psi)

Feed one sample and return stability verdict.

check_trajectory

check_trajectory(v_hist)

Batch check: compute λ from a full V(t) trajectory.

Parameters

v_hist : list of float Sampled Lyapunov candidate values V(t), analytically bounded by [0, 2]. Samples within :data:_V_BOUND_TOLERANCE of those bounds are treated as floating-point boundary noise and clipped into range before the exponent is computed; samples further outside are rejected as physically out of range.

Returns

LyapunovVerdict The batch stability verdict for the trajectory.

Raises

ValueError If v_hist contains non-finite values, or any sample lies more than :data:_V_BOUND_TOLERANCE outside [0, 2].

to_director_ai_dict

to_director_ai_dict(verdict)

Export verdict in DIRECTOR_AI AuditLogger format.

Realtime Monitor

RealtimeMonitor dataclass

RealtimeMonitor(upde, guard, theta_layers, omega_layers, psi_driver=0.0, pac_gamma=0.0, adaptive_engine=None)

Tick-by-tick UPDE monitor with LyapunovGuard.

recorder property

recorder

The trajectory recorder accumulating per-tick monitor snapshots.

from_paper27 classmethod

from_paper27(L=16, N_per=50, *, dt=0.001, zeta_uniform=0.5, psi_driver=0.0, pac_gamma=0.0, guard_window=50, guard_max_violations=3, seed=42)

Build from Paper 27 defaults.

from_plasma classmethod

from_plasma(L=8, N_per=50, *, mode='baseline', dt=0.001, zeta_uniform=0.0, psi_driver=0.0, pac_gamma=0.0, guard_window=50, guard_max_violations=3, adaptive_engine=None, seed=42)

Build from plasma-native Knm defaults with optional adaptive engine.

tick

tick(*, record=True, beta_n=0.0, q95=3.0, disruption_risk=0.0, mirnov_rms=0.0)

Advance one UPDE step and return dashboard snapshot.

Tick failures are fail-closed: the monitor returns a complete snapshot with guard_approved=False instead of raising from the live control loop.

reset

reset(seed=42)

Reset oscillator phases, guard state, and recorder.

save_hdf5

save_hdf5(path)

Export recorded trajectory to HDF5.

Datasets: R_global, R_layer, V_global, V_layer, lambda_exp, guard_approved, latency_us, Psi_global. Attributes: L, N_per, psi_driver, pac_gamma, n_ticks.

Requires h5py (pip install h5py).

save_npz

save_npz(path)

Export recorded trajectory to compressed NPZ (no h5py needed).

TrajectoryRecorder dataclass

TrajectoryRecorder(R_global=list(), R_layer=list(), V_global=list(), V_layer=list(), lambda_exp=list(), guard_approved=list(), latency_us=list(), Psi_global=list())

Accumulates tick snapshots for batch export.

n_ticks property

n_ticks

Number of recorded control ticks.

record

record(snap)

Append one control-tick snapshot to the recorded trajectory.

Parameters

snap Snapshot mapping with the per-tick fields (R_global, R_layer, V_global, V_layer, lambda_exp, guard_approved, latency_us, Psi_global).

clear

clear()

Clear all recorded trajectory channels.

Adaptive Knm Engine

AdaptiveKnmEngine

AdaptiveKnmEngine(baseline_spec, config=None)

Diagnostic-driven online adaptation of the Knm coupling matrix.

Holds a baseline K from a KnmSpec and produces K_adapted each tick.

K_current property

K_current

A copy of the current adaptive inter-layer coupling matrix K.

adaptation_summary property

adaptation_summary

Snapshot of engine state for dashboard export.

update

update(snap)

Apply all adaptation channels and return K_adapted.

reset

reset()

Revert to baseline and clear integral state.

AdaptiveKnmConfig dataclass

AdaptiveKnmConfig(beta_scale=0.3, beta_max_boost=0.5, risk_pairs=((2, 5), (3, 5), (2, 4)), risk_gain=0.4, lambda_risk_gain_s=0.1, q95_reference=3.0, q95_low_risk_gain=0.2, mirnov_rms_reference=1.0, mirnov_risk_gain=0.2, coherence_Kp=0.15, coherence_Ki=0.02, coherence_R_target=0.6, lyapunov_v_gain=0.1, coherence_max_boost=0.3, max_delta_per_tick=0.02, revert_on_guard_refusal=True)

Tuning knobs for each adaptation channel.

All gains are dimensionless except lambda_risk_gain_s, which has units of seconds so that lambda_exp in 1/s maps to a dimensionless risk contribution. q95_reference is a dimensionless low-safety-factor reference, and mirnov_rms_reference is the normalisation scale for the dimensionless Mirnov RMS diagnostic.

DiagnosticSnapshot dataclass

DiagnosticSnapshot(R_layer, V_layer, lambda_exp, beta_n, q95, disruption_risk, mirnov_rms, guard_approved)

Per-tick plasma diagnostic bundle.

Attributes

R_layer Per-layer Kuramoto order parameters, dimensionless in [0, 1]. V_layer Per-layer Lyapunov candidate values, dimensionless in [0, 2] for the Kuramoto candidate used by this repository. lambda_exp Global Lyapunov exponent estimate in inverse seconds. beta_n Normalised beta, dimensionless and non-negative. q95 Edge safety factor, dimensionless and positive. disruption_risk Upstream disruption-risk score, dimensionless in [0, 1]. mirnov_rms Normalised Mirnov RMS amplitude, non-negative and dimensionless in this engine contract. guard_approved Whether the preceding Lyapunov guard verdict allowed adaptation.

The adaptive Knm engine uses every required diagnostic field in DiagnosticSnapshot: R_layer and V_layer drive the diagonal coherence channel, while lambda_exp, q95, disruption_risk, and mirnov_rms contribute to a bounded MHD-pair risk drive. The configuration defaults are dimensionless local-control gains except lambda_risk_gain_s, which converts a Lyapunov exponent in 1/s into a dimensionless stress contribution. These settings are bounded heuristics, not facility-calibrated stability claims.

Plasma Knm

build_knm_plasma

build_knm_plasma(mode='baseline', L=8, K_base=0.3, zeta_uniform=0.0, custom_overrides=None, layer_names=None)

Build a plasma-native Knm coupling matrix.

Parameters

mode : str Instability scenario bias. One of: baseline, elm, ntm, sawtooth, hybrid. L : int Number of plasma layers (default 8). K_base : float Base coupling strength for exponential decay backbone. zeta_uniform : float Global driver gain ζ applied uniformly to all layers. custom_overrides : dict, optional Explicit (i, j) → value overrides applied last. Automatically symmetrised. layer_names : sequence of str, optional Layer labels. Defaults to PLASMA_LAYER_NAMES[:L] or PLASMA_LAYER_NAMES_16[:L].

Returns

KnmSpec Ready for UPDESystem consumption.

WebSocket Stream

PhaseStreamServer binds to loopback by default and requires authenticated clients by default. Operators must supply an API key or explicitly disable client authentication for local development. Non-loopback binds require an API key, command frames are capped by max_payload_bytes, accepted commands are rate-limited with token buckets per connection and per network peer, and production remote exposure should enable TLS with require_tls=True. Authentication, origin, payload, rate-limit, capacity, and command-authority rejections emit structured security audit log events without logging tokens. Query-string token authentication and plaintext non-loopback binds are disabled by default and require explicit operator opt-ins for constrained development or isolated lab environments. Browser clients that send an Origin header are rejected unless the origin is allowlisted, and deployments may restrict command authority with allowed_actions. websocket_runtime_evidence() emits a tamper-evident deployment artifact that binds WebSocket configuration, authenticated sessions, accepted commands, successful broadcasts, audit counters, TLS enforcement, payload caps, and backpressure state without storing API-key material. Qualified facility admission requires client authentication, configured TLS enforcement, observed commands, observed broadcasts, no query-token authentication, no insecure remote binding, and zero backpressure disconnects.

PhaseStreamServer dataclass

PhaseStreamServer(monitor, tick_interval_s=0.001, api_key=None, command_rate_limit=20, command_rate_window_s=1.0, max_payload_bytes=65536, client_send_timeout_s=0.25, max_clients=128, max_client_write_buffer_bytes=262144, require_client_auth=True, allow_query_token_auth=False, require_tls=False, allow_insecure_remote=False, allowed_origins=(), allowed_actions=_DEFAULT_ALLOWED_ACTIONS)

Async WebSocket server wrapping a RealtimeMonitor.

runtime_counters

runtime_counters()

Return a copy of WebSocket runtime security and backpressure counters.

stop

stop()

Signal the broadcast tick loop to stop after its current iteration.

Idempotent. A running :meth:serve also cancels the tick loop on exit; this method lets a caller request a graceful stop without cancelling the server task.

serve async

serve(host='127.0.0.1', port=8765, ssl_context=None)

Start WebSocket server and tick loop.

The broadcast tick loop is always cancelled and awaited when this method returns or is cancelled, so no orphan task survives server shutdown.

serve_sync

serve_sync(host='127.0.0.1', port=8765, ssl_context=None)

Blocking entry point.

WebSocketRuntimeEvidence dataclass

WebSocketRuntimeEvidence(schema_version, generated_utc, deployment_id, bind_host, uses_tls, require_client_auth, api_key_configured, api_key_min_bytes, require_tls, allow_query_token_auth, allow_insecure_remote, command_rate_limit, command_rate_window_s, max_payload_bytes, client_send_timeout_s, max_clients, max_client_write_buffer_bytes, allowed_actions, allowed_origins, config_sha256, auth_successes, command_frames, broadcast_frames, peak_connected_clients, authentication_failures, origin_rejections, payload_rejections, rate_limit_rejections, capacity_rejections, action_rejections, backpressure_disconnects, facility_claim_allowed, claim_status, payload_sha256)

Tamper-evident WebSocket runtime security and backpressure evidence.

websocket_runtime_evidence

websocket_runtime_evidence(server, *, deployment_id, bind_host='127.0.0.1', uses_tls=False, generated_utc=None, counters=None, facility_claim_allowed=False)

Build tamper-evident runtime evidence for a phase WebSocket deployment.

assert_websocket_runtime_claim_admissible

assert_websocket_runtime_claim_admissible(evidence)

Fail closed unless WebSocket evidence supports a facility runtime claim.

save_websocket_runtime_evidence

save_websocket_runtime_evidence(evidence, output_path)

Persist WebSocket runtime evidence as sorted JSON without secret material.

load_websocket_runtime_evidence

load_websocket_runtime_evidence(path, *, require_facility_claim=False)

Load WebSocket runtime evidence with duplicate-key and digest admission.


Control — Controllers

H-infinity (Riccati DARE)

HInfinityController

HInfinityController(A, B1, B2, C1, C2, gamma=None, D12=None, D21=None, enforce_robust_feasibility=False)

Riccati-based H-infinity controller for tokamak vertical stability.

Synthesis: Doyle et al. 1989, IEEE TAC 34, 831 (state-space ARE formulation). Riccati sign convention: Zhou, Doyle & Glover 1996, Eq. 14.18. γ-iteration: Green & Limebeer 1995, Ch. 13 (binary search on ||T_{zw}||_∞). Weighting selection: Ariola & Pironti 2008, Ch. 5 (tokamak vertical loop).

Parameters

A : array_like, shape (n, n) Plant state matrix. B1 : array_like, shape (n, p) Disturbance input matrix. B2 : array_like, shape (n, m) Control input matrix. C1 : array_like, shape (q, n) Performance output matrix. C2 : array_like, shape (l, n) Measurement output matrix. gamma : float, optional H-infinity attenuation level. If None, found by bisection. D12 : array_like, optional Feedthrough from control to performance. Default: identity-like. D21 : array_like, optional Feedthrough from disturbance to measurement. Default: identity-like. enforce_robust_feasibility : bool, optional If True, raise ValueError unless rho(XY) < gamma^2 after synthesis.

is_stable property

is_stable

True iff all eigenvalues of A + B2 F lie in the open left-half plane.

stability_margin_db property

stability_margin_db

Eigenvalue-based stability margin in dB.

Not the classical Bode gain margin. Measures the ratio of closed-loop to open-loop dominant eigenvalue real parts. For frequency-domain gain margin, use a Bode analysis on the loop transfer function.

gain_margin_db property

gain_margin_db

Alias for stability_margin_db (backward compatibility).

step

step(error, dt)

Compute control action for one timestep.

Uses DARE-synthesised gains on the ZOH-discretised plant.

Parameters

error : float Observed measurement (y). dt : float Timestep [s].

Returns

float Control action u.

riccati_residual_norms

riccati_residual_norms()

Frobenius norms of the two H-infinity ARE residuals.

Residual form: X A + A^T X + Q - X B R^{-1} B^T X (Zhou, Doyle & Glover 1996, Eq. 14.18). Small residuals (< 1e-6 relative) confirm solver accuracy.

robust_feasibility_margin

robust_feasibility_margin()

Return gamma^2 - rho(XY); positive values satisfy the strict condition.

Strict condition: rho(XY) < γ^2 — Doyle et al. 1989, §IV, Theorem 1.

reset

reset()

Reset controller state to zero.

get_radial_robust_controller

get_radial_robust_controller(gamma_growth=100.0, *, damping=10.0, enforce_robust_feasibility=False)

H-infinity controller for tokamak vertical stability.

Plant: second-order vertical instability model from Ariola & Pironti 2008, Ch. 5, linearised about an unstable equilibrium:

A = [[0,    1         ],
     [γ_v², -damping  ]]

where γ_v is the vertical growth rate. The double integrator structure captures the leading-order Shafranov instability at low plasma current.

Parameters

gamma_growth : float Vertical instability growth rate [1/s]. ITER-like: ~100/s. SPARC: ~1000/s (Ariola & Pironti 2008, Ch. 5). damping : float Passive damping coefficient [1/s]. Resistive-wall contribution; 10.0 is representative for ITER-like wall proximity (Ariola & Pironti 2008). enforce_robust_feasibility : bool, optional If True, require rho(XY) < gamma^2 and raise on infeasible synthesis.

Returns

HInfinityController Riccati-synthesized robust controller.

Model Predictive Control

NeuralSurrogate

NeuralSurrogate(n_coils, n_state, verbose=True)

Linearized surrogate model around current operating point.

train_on_kernel

train_on_kernel(kernel, perturbation=1.0)

Identify the surrogate response matrix by perturbing each coil.

Parameters

kernel The Grad-Shafranov physics kernel to perturb and re-solve. perturbation Coil-current perturbation amplitude; must be finite and positive.

get_state

get_state(kernel)

Extract the shape state [R_axis, Z_axis, R_xpoint, Z_xpoint] in metres.

Parameters

kernel The solved physics kernel.

Returns

FloatArray The magnetic-axis and X-point coordinates, shape (4,).

predict

predict(current_state, action_delta)

Predict the next shape state under a coil-current change.

Parameters

current_state Current shape state. action_delta Coil-current increment.

Returns

FloatArray The linearly predicted next state state + B @ action.

ModelPredictiveController

ModelPredictiveController(surrogate, target_state, *, prediction_horizon=PREDICTION_HORIZON, learning_rate=0.5, iterations=20, action_limit=2.0, action_regularization=0.1)

Gradient-based MPC planner over surrogate dynamics.

plan_trajectory

plan_trajectory(current_state)

Plan a coil-action trajectory minimising the tracking cost.

Optimises the action sequence over the prediction horizon against the surrogate dynamics by gradient descent.

Parameters

current_state The current shape state.

Returns

FloatArray The planned action sequence, shape (horizon, n_coils).

Optimal Control

OptimalController

OptimalController(config_file, *, kernel_factory=FusionKernel, verbose=True, correction_limit=5.0, coil_current_limits=(-40.0, 40.0), current_target_limits=(5.0, 16.0))

MIMO controller using response-matrix inversion with bounded actuation.

identify_system

identify_system(perturbation=0.5)

Perturb each coil and measure plasma-axis response to build Jacobian.

get_plasma_pos

get_plasma_pos()

Return current plasma-axis position [R, Z].

compute_optimal_correction

compute_optimal_correction(current_pos, target_pos, regularization_limit=0.01)

Solve Error = J * Delta_I using damped pseudoinverse.

run_optimal_shot

run_optimal_shot(shot_steps=SHOT_STEPS, target_r=TARGET_R, target_z=TARGET_Z, gain=0.8, ip_start_ma=10.0, ip_span_ma=5.0, identify_first=False, save_plot=True, output_path='Optimal_Control_Result.png')

Run an SVD-MIMO optimal position-control shot.

Parameters

shot_steps Number of control steps. target_r Target magnetic-axis major radius in metres. target_z Target magnetic-axis vertical position in metres. gain Closed-loop feedback gain. ip_start_ma Plasma current at the start of the ramp in MA. ip_span_ma Plasma-current ramp span in MA. identify_first Whether to run system identification before the shot. save_plot Whether to render the telemetry figure. output_path File path for the rendered telemetry figure.

Returns

Dict[str, Any] The shot summary and recorded telemetry history.

plot_telemetry

plot_telemetry(output_path='Optimal_Control_Result.png')

Render the optimal-control telemetry figure to a file.

Parameters

output_path Destination path for the PNG figure.

Returns

Tuple[bool, str | None] (success, error_message); the message is None on success and describes the failure (e.g. missing matplotlib) otherwise.

Digital Twin

run_digital_twin() now supports persistent sensor calibration bias and drift in addition to dropout and white-noise corruption, and it can now stress the command path with deterministic actuator bias, drift, first-order lag, and rate limiting. The returned summary exposes both commanded and applied actions plus actuator-lag telemetry so replay tests can see what the plant actually received. Density and effective-charge knobs are explicit model-update parameters.

digital_twin_online_update adds fail-closed TRANSP/TSC simulator artifact metadata validation and deterministic Bayesian optimisation over bounded twin parameters. The shipped benchmark is synthetic online-update evidence only; external simulator replay claims require validated artifact metadata and the strict digital-twin reference gate. digital_twin_update_evidence() and assert_digital_twin_update_claim_admissible() bind a bounded Bayesian update to TRANSP and TSC simulator metadata digests, observation and prior digests, result digest, baseline-improvement evidence, and an optional safety-critical controller proof artifact digest. Admission also revalidates source binding, finite non-negative loss history, minimum-loss consistency, best-parameter bounds, strict integer campaign settings, and simulator unit coverage for every observation target.

run_digital_twin

run_digital_twin(time_steps=TIME_STEPS, seed=42, save_plot=True, output_path='Tokamak_Digital_Twin.png', verbose=True, gyro_surrogate=None, chaos_monkey=False, sensor_dropout_prob=0.0, sensor_noise_std=0.0, sensor_bias_std=0.0, sensor_drift_std=0.0, sensor_thermal_lag_tau=0.05, dt=0.001, actuator_bias=0.0, actuator_drift_std=0.0, actuator_tau_steps=0.0, actuator_rate_limit=2.0, n_e=1e+20, Z_eff=1.5, rng=None)

Run deterministic digital-twin control simulation.

Returns a summary dict so callers can use the simulation without relying on console text or plot artifacts.

validate_external_simulator_artifact

validate_external_simulator_artifact(payload)

Validate TRANSP/TSC-style simulator artefact metadata before coupling.

bayesian_update_digital_twin

bayesian_update_digital_twin(observation, priors, *, config=None)

Run deterministic Bayesian optimisation for online twin calibration.

DigitalTwinUpdateEvidence dataclass

DigitalTwinUpdateEvidence(schema_version, simulator_codes, external_artifacts_sha256, observation_sha256, priors_sha256, result_sha256, best_loss, baseline_loss, improved_over_baseline, evaluated_points, controller_formal_artifact_sha256, claim_status)

Tamper-evident admission evidence for bounded online twin updates.

digital_twin_update_evidence

digital_twin_update_evidence(observation, priors, result, external_artifacts, *, controller_formal_artifact_sha256=None)

Build tamper-evident evidence for bounded online digital-twin updates.

assert_digital_twin_update_claim_admissible

assert_digital_twin_update_claim_admissible(evidence, observation, priors, result, external_artifacts, *, require_simulators=SUPPORTED_EXTERNAL_SIMULATORS)

Fail closed unless online twin-update evidence matches replay inputs.

synthetic_online_update_benchmark

synthetic_online_update_benchmark(seed=20240531)

Deterministic local benchmark for the online-update contract.

Controller Safety-Case Evidence

control.safety_case defines the bounded safety-case workflow contract that links a passing safety-critical controller proof manifest, audited JAX differentiable-transport evidence, and TRANSP/TSC-backed bounded digital-twin online-update evidence. The bundle is tamper-evident and fails closed unless all evidence items bind to the same canonical controller artifact SHA-256. This is a repository safety-package admission boundary, not an external certification claim. save_controller_safety_case_evidence() persists the bundle with a manifest integrity digest, and load_controller_safety_case_evidence() rejects schema drift, malformed fields, or edited evidence payloads before replay admission. evaluate_controller_safety_case_readiness() separates linked bounded evidence from promotion readiness: external physics validation, target-hardware timing evidence, qualified HIL replay evidence, qualified CODAC/EPICS runtime evidence, qualified WebSocket runtime evidence, qualified HDL export evidence, and independent safety-review digests are all required before assert_controller_safety_case_readiness_admissible() accepts the package. ReadinessArtifactEvidence and evaluate_controller_safety_case_readiness_from_artifacts() provide the normal promotion path: each required readiness input must be a typed artifact with a known kind, SHA-256 digest, safe relative artifact URI, producer, and generation timestamp before it can satisfy the promotion gate. The evaluator also requires an explicit artifact_root: each URI must resolve below that root and match the declared bytes. target_hardware_timing artifacts must additionally pass the schema-versioned E2E latency evidence validator with qualified target hardware and the configured p95 limit. hil_replay_evidence artifacts must pass the schema-versioned HIL replay admission loader with qualified target hardware, so local workstation replay cannot satisfy deployment promotion readiness. codac_runtime_evidence artifacts must pass the schema-versioned CODAC runtime admission loader with qualified facility-claim status, deadline-clean cycle evidence, exercised interlock blocking, zero backpressure events, and hash-bound EPICS/OPC-UA exports. websocket_runtime_evidence artifacts must pass the schema-versioned WebSocket runtime admission loader with authenticated command evidence, TLS enforcement, token-bucket and payload-cap configuration, successful broadcast counters, and zero backpressure disconnects. hdl_export_evidence artifacts must pass the schema-versioned FPGA export admission loader with controller-artifact binding, generated project file digests, synthesis-report digest binding, and non-negative timing slack. save_controller_safety_case_readiness() and load_controller_safety_case_readiness() persist that readiness decision with the same schema-versioned integrity-digest semantics as the safety-case bundle.

ControllerSafetyCaseEvidence dataclass

ControllerSafetyCaseEvidence(schema_version, controller_artifact_sha256, formal_report_sha256, formal_backend, formal_max_depth, transport_evidence_sha256, digital_twin_evidence_sha256, claim_status)

Tamper-evident bounded controller safety-case evidence bundle.

SafetyCaseReadinessEvidence dataclass

SafetyCaseReadinessEvidence(schema_version, safety_case_sha256, status, external_physics_validation_sha256, target_hardware_timing_sha256, hil_replay_evidence_sha256, hdl_export_evidence_sha256, codac_runtime_evidence_sha256, websocket_runtime_evidence_sha256, independent_safety_review_sha256, blocking_reasons, claim_status)

Promotion-readiness gate for a bounded controller safety-case bundle.

ReadinessArtifactEvidence dataclass

ReadinessArtifactEvidence(kind, artifact_sha256, artifact_uri, producer, generated_utc)

Typed artifact evidence required for safety-case promotion readiness.

controller_safety_case_evidence

controller_safety_case_evidence(controller_artifact, transport_evidence, digital_twin_evidence)

Build a bounded safety-case evidence bundle for one controller artifact.

assert_controller_safety_case_admissible

assert_controller_safety_case_admissible(evidence, controller_artifact, transport_evidence, digital_twin_evidence)

Fail closed unless the safety-case evidence matches all supplied inputs.

save_controller_safety_case_evidence

save_controller_safety_case_evidence(evidence, path)

Persist controller safety-case evidence with an integrity digest.

load_controller_safety_case_evidence

load_controller_safety_case_evidence(path)

Load controller safety-case evidence and verify manifest integrity.

evaluate_controller_safety_case_readiness

evaluate_controller_safety_case_readiness(safety_case, *, external_physics_validation_sha256=None, target_hardware_timing_sha256=None, hil_replay_evidence_sha256=None, hdl_export_evidence_sha256=None, codac_runtime_evidence_sha256=None, websocket_runtime_evidence_sha256=None, independent_safety_review_sha256=None)

Evaluate whether a bounded safety-case bundle is promotion-ready.

The linked internal evidence chain is necessary but not sufficient for promotion readiness. This gate requires external physics validation, target-hardware timing evidence, HIL replay evidence, HDL export evidence, CODAC/EPICS runtime evidence, WebSocket runtime evidence, and an independent safety review digest.

evaluate_controller_safety_case_readiness_from_artifacts

evaluate_controller_safety_case_readiness_from_artifacts(safety_case, artifacts, *, artifact_root, max_target_hardware_e2e_p95_us=1000.0)

Evaluate promotion readiness from typed external evidence artifacts.

assert_controller_safety_case_readiness_admissible

assert_controller_safety_case_readiness_admissible(readiness, safety_case)

Fail closed unless readiness evidence matches the safety case and is complete.

save_controller_safety_case_readiness

save_controller_safety_case_readiness(readiness, path)

Persist controller safety-case readiness with an integrity digest.

load_controller_safety_case_readiness

load_controller_safety_case_readiness(path)

Load controller safety-case readiness and verify manifest integrity.

Flight Simulator

IsoFluxController

IsoFluxController(config_file, kernel_factory=FusionKernel, verbose=True, actuator_tau_s=0.06, heating_actuator_tau_s=None, actuator_current_delta_limit=1000000000.0, heating_beta_max=5.0, control_dt_s=0.05)

Simulates the Plasma Control System (PCS).

Uses PID loops to adjust Coil Currents to maintain plasma shape.

pid_step

pid_step(pid, error)

Advance a PID controller one step and return its output.

Parameters

pid Mutable PID state with Kp, Ki, Kd gains plus the running err_sum and last_err accumulators (updated in place). error The current tracking error.

Returns

float The PID control output.

run_shot

run_shot(shot_duration=SHOT_DURATION, save_plot=True, output_path='Tokamak_Flight_Report.png')

Run the closed-loop current-ramp / divertor-formation shot.

Parameters

shot_duration Number of simulation steps; must be at least 1. save_plot Whether to render the flight-report figure. output_path File path for the rendered report.

Returns

Dict[str, Any] The shot summary and recorded trajectory history.

Raises

ValueError If shot_duration is less than 1.

visualize_flight

visualize_flight(output_path='Tokamak_Flight_Report.png')

Render the flight-report figure to a file.

Parameters

output_path Destination path for the PNG figure.

Returns

Tuple[bool, str | None] (success, error_message); the message is None on success and describes the failure (e.g. missing matplotlib) otherwise.

run_flight_sim

run_flight_sim(config_file=None, shot_duration=SHOT_DURATION, seed=42, save_plot=True, output_path='Tokamak_Flight_Report.png', verbose=True, actuator_tau_s=0.06, heating_actuator_tau_s=None, actuator_current_delta_limit=1000000000.0, heating_beta_max=5.0, control_dt_s=0.05, kernel_factory=FusionKernel)

Run deterministic tokamak flight-sim control loop and return summary.

Free-Boundary Tracking

Experimental closed-loop free-boundary tracking that keeps the full FusionKernel in the loop and re-identifies the local coil-response map from repeated solves. Safe-current fallback targets can be supplied through the free_boundary_tracking.fallback_currents config block when supervisor rejection should ramp the coils toward a predefined safe state. Persistent objective residuals can also be accumulated with the config-driven free_boundary_tracking.observer_gain and observer_max_abs settings. When free-boundary objective tolerances are configured, the controller also uses them directly in its correction and accept/reject logic so tighter X-point or divertor targets take precedence over looser shape goals, and it refuses trial steps that would push an already-satisfied objective back outside tolerance. If the identified coil-response map loses authority entirely, the controller marks that degraded state explicitly and drops into the safe-state recovery path instead of silently accepting a zero-action step. Residuals already inside the configured tolerances are also treated as deadband, so the controller stops chattering the coils once the protected objectives are met. Coil allocation is also headroom-aware, so the regularized solve prefers actuators that still have current authority instead of leaning equally on a nearly saturated coil. Deterministic objective-space sensor bias and per-step drift can be injected through free_boundary_tracking.measurement_bias and measurement_drift_per_step, and known calibration corrections can be applied with measurement_correction_bias and measurement_correction_drift_per_step. The run summary reports both measured and hidden true objective errors so calibration faults cannot masquerade as control success in acceptance tests.

from scpn_control.control.free_boundary_tracking import run_free_boundary_tracking

summary = run_free_boundary_tracking(
    "iter_config.json",
    shot_steps=5,
    gain=0.8,
    verbose=False,
    coil_slew_limits=2.5e5,
    supervisor_limits={"x_point_position": 0.15, "max_abs_actuator_lag": 1.0e5},
    hold_steps_after_reject=2,
)

print(summary["shape_rms"], summary["objective_converged"], summary["supervisor_intervention_count"])

FreeBoundaryTrackingController

FreeBoundaryTrackingController(config_file, *, kernel_factory=FusionKernel, verbose=True, identification_perturbation=0.25, correction_limit=2.0, response_regularization=0.001, response_refresh_steps=1, solve_max_outer_iter=10, solve_tol=0.0001, objective_tolerances=None, control_dt_s=None, coil_actuator_tau_s=None, coil_slew_limits=None, supervisor_limits=None, hold_steps_after_reject=None, state_estimator=None)

Direct free-boundary controller using local coil-response identification.

This path keeps the full Grad-Shafranov kernel in the loop instead of replacing it with a reduced-order plant.

Examples

from scpn_control.control.free_boundary_tracking import run_free_boundary_tracking summary = run_free_boundary_tracking( ... "iter_config.json", ... shot_steps=3, ... gain=0.8, ... verbose=False, ... ) summary["boundary_variant"] 'free_boundary'

identify_response_matrix

identify_response_matrix(perturbation=None)

Identify the shape-response matrix by perturbing each coil current.

Parameters

perturbation Coil-current perturbation amplitude; defaults to the configured identification perturbation. Must be finite and positive.

Returns

FloatArray The ∂observation / ∂I_coil response matrix.

compute_correction

compute_correction(observation, *, metrics=None)

Compute the coil-current correction toward the shape targets.

Parameters

observation Current shape-observation vector (must match the target shape). metrics Optional precomputed objective metrics; evaluated from observation when omitted.

Returns

FloatArray The coil-current correction vector.

evaluate_objectives

evaluate_objectives(observation)

Evaluate shape-tracking objective metrics for an observation.

Parameters

observation The shape-observation vector.

Returns

dict[str, Any] Tracking-error norms and shape diagnostics (RMS, max abs, X-point).

evaluate_supervisor

evaluate_supervisor(metrics, *, max_abs_coil_current, max_abs_actuator_lag)

Apply the supervisory limit checks to the tracking metrics.

Parameters

metrics Objective metrics from :meth:evaluate_objectives. max_abs_coil_current Coil-current limit for the supervisor. max_abs_actuator_lag Actuator-lag limit for the supervisor.

Returns

dict[str, Any] The supervisory verdict and per-limit flags.

run_tracking_shot

run_tracking_shot(*, shot_steps=10, gain=1.0, disturbance_callback=None, stop_on_convergence=False)

Run a closed-loop free-boundary shape-tracking shot.

Parameters

shot_steps Number of control steps; must be at least 1. gain Correction gain; must be finite and positive. disturbance_callback Optional callback (state, coils, step) -> None injecting disturbances each step. stop_on_convergence Whether to stop early once the tracking error converges.

Returns

dict[str, Any] The shot history and final tracking metrics.

run_free_boundary_tracking

run_free_boundary_tracking(config_file=None, *, shot_steps=10, gain=1.0, verbose=True, kernel_factory=FusionKernel, objective_tolerances=None, control_dt_s=None, coil_actuator_tau_s=None, coil_slew_limits=None, supervisor_limits=None, hold_steps_after_reject=None, disturbance_callback=None, stop_on_convergence=False)

Run deterministic free-boundary tracking over the configured objectives.

Examples

summary = run_free_boundary_tracking("iter_config.json", shot_steps=2, gain=0.7, verbose=False) bool(summary["objective_convergence_active"]) True

Free-Boundary Tracking Claim Evidence

free_boundary_tracking_claim_evidence

free_boundary_tracking_claim_evidence(summary, *, source, source_id, model_id='bounded_free_boundary_tracking', reference_artifact=None, shape_rms_abs_tolerance=0.02, x_point_position_abs_tolerance_m=0.05, x_point_flux_abs_tolerance=0.02, divertor_rms_abs_tolerance=0.02, coil_current_relative_tolerance=0.05)

Build fail-closed free-boundary evidence from a run summary.

assert_free_boundary_tracking_facility_claim_admissible

assert_free_boundary_tracking_facility_claim_admissible(evidence)

Raise when free-boundary evidence is insufficient for facility-control claims.

save_free_boundary_tracking_claim_evidence

save_free_boundary_tracking_claim_evidence(evidence, path)

Persist free-boundary claim evidence as deterministic JSON.

Disruption Predictor

predict_disruption_risk_safe() still returns a bounded scalar risk, but its metadata now includes deterministic sigma-point uncertainty summaries (risk_p05, risk_p50, risk_p95, risk_std, risk_interval) for both fallback and checkpoint inference paths. DisruptionTransformer.predict() returns the same bounded scalar-risk shape expected by evaluate_predictor(), so trained transformer instances can be evaluated directly without wrapper adapters.

DisruptionTransformer

DisruptionTransformer(seq_len=DEFAULT_SEQ_LEN, dropout=0.1)

Bases: _DisruptionTransformerImpl

Runtime transformer surface that raises until torch is installed.

predict_disruption_risk

predict_disruption_risk(signal, toroidal_observables=None)

Heuristic disruption-risk baseline returning a value in [0, 1].

A transparent, deterministic fixed-weight logistic score over toroidal-asymmetry observables (n=1,2,3 mode amplitudes) from 3D diagnostics. This is not a model trained on a real disruption database: the feature weights and logit bias below are hand-chosen by inspection (sanity-checked against synthetic DIII-D/JET shots in validation/reports/disruption_replay_pipeline_benchmark.md), not fitted by an optimiser. It can be compared with the optional synthetic Transformer pathway as an interpretable baseline. Logit bias: sigmoid(−4.0) ≈ 0.018, giving low base risk on zero features.

predict_disruption_risk_safe

predict_disruption_risk_safe(signal, toroidal_observables=None, *, model_path=None, seq_len=DEFAULT_SEQ_LEN, train_if_missing=False, mc_samples=10, allow_legacy_fallback=False, allow_inference_fallback=False, allow_legacy_inference_fallback=False)

Predict disruption risk with MC dropout uncertainty if model is available.

Returns

risk, metadata risk is the MC mean risk in [0, 1]. metadata includes risk_std (epistemic uncertainty).

Disruption Contracts

run_disruption_episode

run_disruption_episode(*, rng, rl_agent, base_tbr, explorer)

Run one synthetic disruption episode through the RL agent and explorer.

Parameters

rng Random generator for the episode draw. rl_agent The fusion RL agent scoring disruption risk and actions. base_tbr Baseline tritium breeding ratio; must be positive. explorer The global design explorer evaluated for the episode.

Returns

dict[str, float | bool] Episode metrics including the risk before and after mitigation and the mitigation outcome.

predict_disruption_risk

predict_disruption_risk(signal, toroidal_observables=None)

Heuristic disruption-risk baseline returning a value in [0, 1].

A transparent, deterministic fixed-weight logistic score over toroidal-asymmetry observables (n=1,2,3 mode amplitudes) from 3D diagnostics. This is not a model trained on a real disruption database: the feature weights and logit bias below are hand-chosen by inspection (sanity-checked against synthetic DIII-D/JET shots in validation/reports/disruption_replay_pipeline_benchmark.md), not fitted by an optimiser. It can be compared with the optional synthetic Transformer pathway as an interpretable baseline. Logit bias: sigmoid(−4.0) ≈ 0.018, giving low base risk on zero features.

Disruption ROC

scpn_control.control.disruption_roc scores a fixed-weight risk series over a shot, sweeps alarm thresholds, and reports bounded internal ROC/AUC plus warning-time recall. The scoring core is a bounded model (the n=3 toroidal amplitude approximates n=2), so its metrics stay internal and admission-blocked.

score_risk_series

score_risk_series(dbdt, n1_amp, n2_amp, *, window_size)

Return the per-sample disruption risk over a shot.

The signal window is the window_size-sample history of dbdt ending at (but excluding) each sample; the toroidal observables are derived from the n=1/n=2 mode amplitudes with the n=3 amplitude approximated as 0.4 * n2. Samples before the first full window carry zero risk. This mirrors the scoring loop of :func:scpn_control.control.disruption_contracts.run_real_shot_replay and is its single source of truth.

Parameters

dbdt, n1_amp, n2_amp : numpy.ndarray One-dimensional, equal-length, finite arrays: the magnetic pickup-coil time derivative and the n=1/n=2 toroidal mode amplitudes. window_size : int Sliding predictor window in samples (>= 1).

Returns

numpy.ndarray Risk in [0, 1] per sample, zero for the leading window_size samples.

ShotEvaluation dataclass

ShotEvaluation(risk_series, label, disruption_time_idx, time_s, window_size)

A scored shot ready for ROC and warning-time analysis.

risk_series is the per-sample output of :func:score_risk_series; label is 1 for a disruptive shot and 0 for a safe shot; disruption_time_idx is the labelled disruption sample (>= 0 and required when label == 1, ignored when label == 0); time_s is the strictly increasing shot timebase used for warning-time leads; and window_size is the leading gap of zero-risk samples to skip when scanning for an alarm.

first_alarm_index

first_alarm_index(risk_series, threshold, *, start)

Return the first sample index at/after start whose risk exceeds threshold.

Returns -1 when the risk never rises above threshold.

confusion_at_threshold

confusion_at_threshold(evaluations, threshold)

Confusion counts and rates at a single alarm threshold.

A disruptive shot is a true positive only when an alarm fires strictly before its labelled disruption sample; a later or absent alarm is a false negative. A safe shot with any alarm is a false positive.

roc_curve

roc_curve(evaluations, thresholds)

Sweep thresholds and return parallel (fpr, tpr) lists.

roc_auc_from_curve

roc_auc_from_curve(fpr, tpr)

Return the trapezoidal AUC of a ROC curve.

The (0, 0) and (1, 1) endpoints are added when absent, the points are sorted by false-positive rate, and the area is integrated with the trapezoidal rule — the same assembly used by the synthetic ROC analysis.

warning_time_recall

warning_time_recall(evaluations, threshold, warning_ms)

Fraction of disruptive shots alarmed at least warning_ms before disruption.

Returns 0.0 when there are no disruptive shots. The lead time is measured on each shot's timebase between the labelled disruption sample and the first alarm sample.

disruption_metrics

disruption_metrics(evaluations, *, thresholds, alarm_threshold, warning_ms)

Assemble the full ROC + warning-time metric bundle for scored shots.

Combines the threshold-swept ROC curve and AUC with the confusion matrix and warning-time recall evaluated at a single operating alarm_threshold.

SPI Mitigation

ShatteredPelletInjection

ShatteredPelletInjection(Plasma_Energy_MJ=300.0, Plasma_Current_MA=15.0)

Reduced SPI mitigation model for thermal/current quench campaigns.

SPI concept: Commaux et al. 2010, Nucl. Fusion 50, 112001.

estimate_z_eff staticmethod

estimate_z_eff(neon_quantity_mol)

Estimate post-injection Z_eff for a neon-only shattered-pellet dose.

Parameters

neon_quantity_mol Injected neon in moles.

Returns

float The effective charge after assimilation.

estimate_z_eff_cocktail staticmethod

estimate_z_eff_cocktail(*, neon_quantity_mol=0.0, argon_quantity_mol=0.0, xenon_quantity_mol=0.0)

Estimate Z_eff for a noble-gas SPI cocktail.

Radiative efficiency weighting reflects that higher-Z species dominate coronal radiation. Lehnen et al. 2015, J. Nucl. Mater. 463, 39.

estimate_mitigation_cocktail staticmethod

estimate_mitigation_cocktail(*, risk_score, disturbance, action_bias=0.0)

Recommend an impurity-injection cocktail for a disruption risk.

Parameters

risk_score Disruption risk in [0, 1]; must be finite. disturbance Disturbance amplitude; must be finite. action_bias Optional bias shifting the recommended dose; must be finite.

Returns

dict[str, float] Recommended neon, argon, and xenon quantities in moles.

estimate_tau_cq staticmethod

estimate_tau_cq(te_keV, z_eff)

Estimate the current-quench time scale.

Scaling: τ_CQ ∝ T_e^0.25 / Z_eff, consistent with resistive decay. τ_TQ ~ 1–3 ms, τ_CQ ~ 20–150 ms for ITER. Hender et al. 2007, Nucl. Fusion 47, S128, Sec. 3.

trigger_mitigation

trigger_mitigation(neon_quantity_mol=0.1, argon_quantity_mol=0.0, xenon_quantity_mol=0.0, return_diagnostics=False, *, duration_s=0.05, dt_s=1e-05, verbose=True)

Inject a shattered-pellet impurity cocktail and simulate the response.

Parameters

neon_quantity_mol Injected neon in moles; must be non-negative. argon_quantity_mol Injected argon in moles; must be non-negative. xenon_quantity_mol Injected xenon in moles; must be non-negative. return_diagnostics Whether to return the detailed time-resolved diagnostics. duration_s Simulated mitigation duration in seconds. dt_s Simulation time step in seconds. verbose Whether to log progress.

Returns

Any The mitigation summary, or detailed diagnostics when return_diagnostics is set.

run_spi_mitigation

run_spi_mitigation(*, plasma_energy_mj=300.0, plasma_current_ma=15.0, neon_quantity_mol=0.1, argon_quantity_mol=0.0, xenon_quantity_mol=0.0, duration_s=0.05, dt_s=1e-05, save_plot=True, output_path='SPI_Mitigation_Result.png', verbose=True)

Run SPI mitigation simulation and return deterministic summary metrics.

Fusion Control Room

run_control_room

run_control_room(sim_duration=SIM_DURATION, *, seed=42, save_animation=True, save_report=True, output_gif='SCPN_Fusion_Control_Room.gif', output_report='SCPN_Fusion_Status_Report.png', verbose=True, kernel_factory=None, config_file=None, allow_analytic_fallback=False, allow_legacy_analytic_fallback=False, allow_runtime_solve_fallback=False, allow_legacy_runtime_solve_fallback=False, allow_kernel_psi_fallback=False, allow_legacy_kernel_psi_fallback=False, allow_coil_update_fallback=False, allow_legacy_coil_update_fallback=False, allow_output_render_fallback=False, allow_legacy_output_render_fallback=False)

Run the control-room loop and return deterministic summary metrics.

TokamakPhysicsEngine

TokamakPhysicsEngine(size=RESOLUTION, *, seed=42, kernel=None)

Reduced Grad-Shafranov geometry model with optional kernel-Psi ingestion.

solve_flux_surfaces

solve_flux_surfaces()

Return (density, psi) from kernel state when available, otherwise from analytic Miller-parameterized geometry.

step_dynamics

step_dynamics(coil_action_top, coil_action_bottom)

Reduced vertical-displacement dynamics.

Gymnasium Environment

TokamakEnv

TokamakEnv(dt=0.001, max_steps=500, T_target=20.0, noise_std=0.01, seed=42, n_e_20=1.0, V_plasma=830.0)

Minimal Gymnasium-compatible tokamak control environment.

Implements a bounded reduced-order plasma response model for energy and current evolution based on 0D lumped-parameter equations. Ref: Wesson, J. (2011). Tokamaks. 4th Edition, Chapter 1.

Follows the gymnasium.Env interface (reset/step/render) without requiring gymnasium as a hard dependency. If gymnasium is installed, this class can be registered via gymnasium.register().

Parameters

dt : float Timestep per step call [s]. max_steps : int Episode length. T_target : float Target axis temperature [keV]. noise_std : float Observation noise standard deviation. n_e_20 : float Line-averaged electron density [10^20 m^-3]. Default 1.0. V_plasma : float Plasma volume [m^3]. Default 830.0 (ITER).

observation_space_shape property

observation_space_shape

Shape of the observation vector (6 plasma-state features).

action_space_shape property

action_space_shape

Shape of the action vector (2 actuator commands).

reset

reset(seed=None)

Reset to initial plasma state.

step

step(action)

Advance one timestep using physics-based energy balance.

Returns (obs, reward, terminated, truncated, info).

render

render()

Print current state.

Analytic Solver

AnalyticEquilibriumSolver

AnalyticEquilibriumSolver(config_path, *, kernel_factory=FusionKernel, verbose=True)

Analytic vertical-field target and least-norm coil-current solve.

calculate_required_Bv

calculate_required_Bv(R_geo, a_min, Ip_MA, *, beta_p=0.5, li=0.8)

Shafranov radial-force balance vertical field estimate.

compute_coil_efficiencies

compute_coil_efficiencies(target_R, *, target_Z=0.0)

Compute dBz/dI per coil at target location using kernel vacuum-field map.

solve_coil_currents

solve_coil_currents(target_Bv, target_R, *, target_Z=0.0, ridge_lambda=0.0)

Solve least-norm coil currents for desired vertical field target.

apply_currents

apply_currents(currents)

Set the kernel coil currents from a current vector.

Parameters

currents Coil currents, one per kernel coil; must be finite and match the coil count.

Raises

ValueError If the length mismatches the coils or any value is non-finite.

apply_and_save

apply_and_save(currents, output_path=None)

Apply coil currents and save the resulting kernel config to JSON.

Parameters

currents Coil currents to apply (see :meth:apply_currents). output_path Destination JSON path; defaults to validation/iter_analytic_config.json under the repo root. Preserved metadata keys in an existing file are retained.

Returns

str The path to the written configuration file.

Bio-Holonomic Controller

BioHolonomicController

BioHolonomicController(dt_s=0.01, seed=42)

Biological feedback controller mapping L4/L5 states to actuator commands.

Rather than mitigating plasma disruptions, this controller mitigates biological decoherence by tracking autonomic tone and triggering resonant acoustic interventions.

step

step(telemetry)

Advances the bio-controller one tick using incoming telemetry.

Digital Twin Ingest

RealtimeTwinHook

RealtimeTwinHook(machine, *, max_buffer=512, seed=42)

In-memory realtime ingest + SNN planning hook.

ingest

ingest(packet)

Append a telemetry packet to the rolling buffer.

Parameters

packet The telemetry packet to ingest; the buffer is capped at max_buffer.

scenario_plan

scenario_plan(*, horizon=24)

Produce a forward scenario plan from the buffered telemetry.

Parameters

horizon Planning horizon in samples; must be at least 4.

Returns

dict[str, float | bool] The projected risk metrics and mitigation recommendation.

Raises

RuntimeError If no telemetry has been ingested.

Director Interface

DirectorInterface

DirectorInterface(config_path, *, allow_fallback=False, allow_legacy_fallback=False, director=None, controller_factory=NeuroCyberneticController, entropy_threshold=0.3, history_window=10)

Interfaces the 'Director' (Layer 16: Coherence Oversight) with the Fusion Reactor.

Role: The Director does NOT control the coils (Layer 2 does that). The Director controls the Controller. It sets the strategy and monitors for "Backfire".

Mechanism: 1. Sample System State (Physics + Neural Activity). 2. Format as a "Prompt" for the Director. 3. Director calculates Entropy/Risk. 4. If Safe: Director updates Target Parameters. 5. If Unsafe: Director triggers corrective action.

format_state_for_director

format_state_for_director(t, ip, err_r, err_z, brain_activity)

Translate physical telemetry into a semantic prompt for the Director.

run_directed_mission

run_directed_mission(duration=100, *, use_quantum=True, glitch_start_step=50, glitch_std=500.0, rng_seed=42, save_plot=True, output_path='Director_Interface_Result.png', verbose=True, allow_plot_fallback=False, allow_legacy_plot_fallback=False, allow_runtime_contract_fallback=False, allow_legacy_runtime_contract_fallback=False)

Run a Director-mediated closed-loop fusion-control mission.

Parameters

duration Number of mission steps; must be at least 1. use_quantum Whether to engage the quantum-assisted control path. glitch_start_step Step at which an injected sensor glitch begins. glitch_std Standard deviation of the injected glitch. rng_seed Random seed for reproducibility. save_plot Whether to render the mission figure. output_path File path for the rendered figure. verbose Whether to log progress. allow_plot_fallback, allow_legacy_plot_fallback Explicit opt-ins for degraded plotting paths. allow_runtime_contract_fallback, allow_legacy_runtime_contract_fallback Explicit opt-ins for degraded runtime-contract paths.

Returns

dict[str, Any] The mission summary and recorded telemetry log.

Raises

ValueError If duration is less than 1.

visualize

visualize(output_path='Director_Interface_Result.png')

Render the Director-mediated control telemetry figure.

Parameters

output_path Destination path for the PNG figure.

Returns

str The path to the written figure.

Fueling Mode Controller

IcePelletFuelingController

IcePelletFuelingController(target_density=1.0)

Hybrid Petri-to-SNN + PI fueling controller.

step

step(density, k, dt_s)

Advance the hybrid PI/SNN fuelling controller one step.

Parameters

density The measured plasma density (normalised units). k The current step index. dt_s Time step in seconds.

Returns

tuple[float, float] The clipped fuelling command and the density-tracking error.

Halo RE Physics

HaloCurrentModel

HaloCurrentModel(plasma_current_ma=15.0, minor_radius_m=2.0, major_radius_m=6.2, wall_resistivity_ohm_m=7e-07, wall_thickness_m=0.06, tpf=2.0, contact_fraction=0.3)

Fitzpatrick-style L/R circuit halo current model.

Parameters

plasma_current_ma : float Pre-disruption plasma current (MA). minor_radius_m : float Plasma minor radius (m). major_radius_m : float Plasma major radius (m). wall_resistivity_ohm_m : float Wall resistivity (Ohm·m). Default: stainless steel ~7e-7. wall_thickness_m : float Wall thickness (m). tpf : float Toroidal peaking factor (1.0 = uniform, up to ~2.5 in severe VDEs). contact_fraction : float Fraction of plasma cross-section in wall contact (0–1).

simulate

simulate(tau_cq_s=0.01, duration_s=0.05, dt_s=1e-05)

Run the L/R circuit halo current model.

Parameters

tau_cq_s : float Current quench time constant (s). duration_s : float Simulation duration (s). dt_s : float Time step (s).

DisruptionMitigationClaimEvidence dataclass

DisruptionMitigationClaimEvidence(schema_version, model_id, source, source_id, ensemble_runs, ensemble_seed, prevention_rate, mean_halo_peak_ma, p95_halo_peak_ma, mean_re_peak_ma, p95_re_peak_ma, mean_tpf_product, passes_iter_limits, reference_source, reference_dataset_id, reference_artifact_sha256, reference_case_count, risk_after_abs_error, detection_lead_time_abs_error_ms, halo_current_relative_error, runaway_beam_relative_error, tbr_abs_error, risk_after_abs_tolerance, detection_lead_time_abs_tolerance_ms, halo_current_relative_tolerance, runaway_beam_relative_tolerance, tbr_abs_tolerance, mitigation_claim_allowed, claim_status)

Serialisable admission evidence for halo/runaway mitigation claims.

disruption_mitigation_claim_evidence

disruption_mitigation_claim_evidence(report, *, source, source_id, ensemble_seed, model_id='halo_runaway_disruption_mitigation', reference_artifact_path=None)

Build fail-closed evidence for halo/runaway mitigation claims.

assert_disruption_mitigation_claim_admissible

assert_disruption_mitigation_claim_admissible(evidence)

Return evidence only when strict matched-reference admission passed.

save_disruption_mitigation_claim_evidence

save_disruption_mitigation_claim_evidence(evidence, path)

Persist disruption mitigation claim evidence as deterministic JSON.

HIL Test Harness

HILControlLoop

HILControlLoop(target_rate_hz=1000.0, sensor=None)

Real-time 1 kHz control loop with measured timing.

Executes a user-supplied control callback at a target rate (default 1 kHz) and measures actual loop timing using time.perf_counter_ns.

Parameters

target_rate_hz : float Target control loop rate (Hz). Default: 1000 (1 kHz). sensor : SensorInterface or None Sensor/actuator interface. Created with defaults if None.

set_controller

set_controller(fn)

Register the control callback: fn(error, sensor) -> command.

run

run(iterations=1000, plant_fn=None, initial_state=0.0, setpoint=0.0)

Execute the control loop and measure timing.

Parameters

iterations : int Number of control loop iterations. plant_fn : callable or None Plant model: state_new = plant_fn(state, command). Default: simple integrator (state += command * dt). initial_state : float Initial plant state. setpoint : float Control target.

HILBenchmarkResult dataclass

HILBenchmarkResult(control_metrics, sensor_latency_us, controller_latency_us, actuator_latency_us, total_loop_latency_us, passes_sub_ms, passes_1khz, fpga_register_map)

Benchmark result for HIL sub-ms timing validation.

hil_replay_evidence

hil_replay_evidence(metrics, *, controller_id, target_hardware_id='local-unqualified-host', target_hardware_class='local-userspace-replay', rt_kernel='generic-userspace', clock_source='time.perf_counter_ns', interlock_events=0, backpressure_events=0, deployment_claim_allowed=False, generated_at=None)

Build tamper-evident HIL replay evidence with claim admission metadata.

Local workstation timing is useful regression evidence, but it is not production deployment evidence. Setting deployment_claim_allowed=True therefore fail-closes unless qualified target hardware metadata, zero safety events, zero overruns, and target-rate latency bounds are present.

assert_hil_replay_evidence_admissible

assert_hil_replay_evidence_admissible(evidence, *, require_target_hardware=False)

Validate a HIL replay evidence payload and return an admitted copy.

save_hil_replay_evidence

save_hil_replay_evidence(evidence, path, *, require_target_hardware=False)

Persist an admitted HIL replay evidence payload as canonical JSON.

load_hil_replay_evidence

load_hil_replay_evidence(path, *, require_target_hardware=False)

Load and admit a HIL replay evidence payload from JSON.

JAX Traceable Runtime

Requires pip install "scpn-control[jax]".

TraceableRuntimeSpec dataclass

TraceableRuntimeSpec(dt_s=0.001, tau_s=0.005, gain=1.0, command_limit=1.0)

Configuration for reduced traceable first-order actuator dynamics.

LIF+NEF SNN Controller

NengoSNNController

NengoSNNController(config=None)

SNN controller for tokamak position control.

Pure-NumPy LIF + NEF implementation. Per channel: error ensemble decodes gain·x into control ensemble, which decodes identity to output.

Parameters

config : NengoSNNConfig or None Controller configuration. Uses defaults if None.

build_network

build_network()

Build LIF ensembles and compute NEF decoders.

step

step(state)

Run one control step.

Parameters

state : array of shape (n_channels,) Error vector.

Returns

array of shape (n_channels,) Control command.

reset

reset()

Reset all neuron state and filters.

get_spike_data

get_spike_data()

Return probe data from simulation.

export_weights

export_weights()

Extract decoder, encoder, and gain/bias arrays.

Returns

dict mapping label -> weight array

export_fpga_weights

export_fpga_weights(filename)

Export weight matrices for FPGA synthesis as .npz.

export_loihi

export_loihi(filename)

Loihi export requires the original nengo + nengo_loihi packages.

benchmark

benchmark(n_steps=1000)

Measure per-step latency.

Returns

dict with mean_us, p50_us, p95_us, p99_us, max_us

Neuro-Cybernetic Controller

NeuroCyberneticController

NeuroCyberneticController(config_file, seed=42, *, shot_duration=SHOT_DURATION, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False, kernel_factory=None, safety_confidence_threshold=0.35, safe_shutdown_ramp_steps=3)

Replaces PID loops with push-pull spiking populations.

safety_trigger_count property

safety_trigger_count

Number of safety-FSM entries since the last reset.

overflow_trap_count property

overflow_trap_count

Number of non-finite command traps since the last reset.

initialize_brains

initialize_brains(use_quantum=False)

Reset and initialise the SNN populations and the safety FSM.

Parameters

use_quantum Whether to initialise the quantum-hybrid entropy source instead of the classical generator.

run_shot

run_shot(*, save_plot=True, verbose=True, output_path=None)

Run a classical-SNN control shot end to end.

Parameters

save_plot Whether to save a history plot. verbose Whether to log progress. output_path Optional output path for the plot.

Returns

Dict[str, Any] The simulation history and summary metrics.

run_quantum_shot

run_quantum_shot(*, save_plot=True, verbose=True, output_path=None)

Run a quantum-neuro-hybrid control shot end to end.

Parameters

save_plot Whether to save a history plot. verbose Whether to log progress. output_path Optional output path for the plot.

Returns

Dict[str, Any] The simulation history and summary metrics.

visualize

visualize(title, *, output_path=None, verbose=True)

Plot the control history and return the saved figure path.

Parameters

title Plot title prefix. output_path Optional path for the saved figure. verbose Whether to log the save location.

Returns

str The path to the saved figure.

Raises

RuntimeError If matplotlib is unavailable.

TORAX Hybrid Loop

run_nstxu_torax_hybrid_campaign

run_nstxu_torax_hybrid_campaign(*, seed=42, episodes=16, steps_per_episode=220)

Run deterministic NSTX-U-like realtime hybrid control campaign.

Advanced SOC Learning

run_advanced_learning_sim

run_advanced_learning_sim(size=L, time_steps=TIME_STEPS, seed=42, *, epsilon=EPSILON, noise_probability=0.01, shear_step=0.05, shear_bounds=(0.0, 1.0), save_plot=True, output_path='Advanced_SOC_Learning.png', verbose=True)

Run deterministic SOC+Q-learning control simulation and return summary metrics.

NMPC Controller (v0.16.0)

NonlinearMPC validates the NMPC quadratic program contract before optimization: Q, R, and optional terminal P must be finite symmetric positive-definite matrices with tokamak state/input dimensions; state, input, and slew bounds must be finite and ordered; and plant-model evaluations must return finite state vectors. Invalid math contracts fail closed instead of propagating undefined SQP or PGD iterates. The public compute_cost() evaluator includes the finite-horizon terminal penalty, using configured P when supplied and the controller's conservative fallback terminal weight otherwise. Production plant models may provide an analytic linearization_model(x, u) contract returning finite (6, 6) state and (6, 3) input Jacobians. The controller validates those matrices before use and records last_linearization_source == "analytic". If no analytic provider is supplied, the controller can use linearization_backend="jax" for JAX-traceable plant models and records last_linearization_source == "jax"; otherwise it falls back to bounded central finite differences and records last_linearization_source == "finite_difference". Quadratic weights use a strict symmetry gate before positive-definite projection so near-zero off-diagonal asymmetry cannot pass as a valid cost matrix. DARE-derived terminal matrices are accepted only when finite, symmetric, and positive definite; invalid solver output falls back to the conservative terminal weight. Explicit terminal state sets are configured with paired terminal_x_min and terminal_x_max vectors. These bounds must lie inside the configured physics state envelope and currently require qp_backend="scipy", qp_backend="osqp", qp_backend="casadi", or qp_backend="acados" so the coupled terminal-state inequality is enforced inside the constrained QP solve rather than checked after the fact. casadi is a repository-local optional dependency path. The acados backend is a full optional OCP interface: deployments may inject a pre-built acados OCP/solver factory, or provide symbolic_dynamics_model(ca, x, u) so the controller builds a discrete augmented-state acados model. The augmented state stores the previous actuator vector, making |Δu| <= du_max a native acados path constraint instead of a post-solve clamp. The default builder configures SQP, partial-condensing HPIPM, exact Hessian mode, linear least-squares stage/terminal costs, state/input bounds, terminal state bounds, warm starts, fail-closed solver-status handling, and a runtime plant-consistency gate. The returned acados state trajectory must start from the commanded state, remain inside configured state bounds, satisfy any terminal admissible set, and match plant_model transitions within acados_dynamics_residual_tol before the first actuator command is admitted. The previous input supplied to step() must already satisfy actuator bounds so the slew-rate projection cannot propagate an unsafe actuator state. The accepted horizon=1 case is handled as a valid one-step receding-horizon controller and warm-starts from the bounded previous input. Each QP solve records last_qp_iterations and last_qp_converged, making projection-tolerance convergence distinguishable from iteration-budget exhaustion. The projected-gradient QP iteration budget is configured by qp_max_iter instead of being an unobservable hard-coded loop bound. Linearization perturbations are clipped to the configured state/input domain: interior points use central differences, while boundary points use one-sided finite differences.

NonlinearMPC

NonlinearMPC(plant_model, config, linearization_model=None, symbolic_dynamics_model=None, acados_ocp_factory=None, acados_solver_factory=None)

SQP-based NMPC with validated plant linearization contracts.

Each SQP outer iteration linearizes f around the nominal trajectory with an optional analytic Jacobian provider. When no provider is configured, the controller falls back to bounded finite differences. The condensed QP is solved by either SciPy SLSQP or curvature-scaled projected gradient.

close

close()

Release cached external solver resources held by this controller.

__enter__

__enter__()

Return this controller for deterministic external-solver lifetime scopes.

__exit__

__exit__(exc_type, exc, traceback)

Release external solver resources without suppressing control-loop faults.

linearize

linearize(x0, u0)

Linearise the plant about an operating point.

Parameters

x0 The state about which to linearise. u0 The input about which to linearise.

Returns

tuple[FloatArray, FloatArray] The state and input Jacobians (A, B).

compute_cost

compute_cost(x_traj, u_traj, x_ref)

Evaluate the NMPC cost J over a trajectory.

J = Σ_{k=0}^{N-1} ‖x_k − x_ref‖²_Q + ‖u_k‖²_R Rawlings, Mayne & Diehl 2017, Ch. 1, Eq. (1.2).

step

step(x, x_ref, u_prev)

Compute optimal first control action via SQP.

Warm-started from the previous solution shifted by one step.

step_rti

step_rti(x, x_ref, u_prev)

Advance one Real-Time Iteration tick: one linearisation, one QP solve.

The previous horizon solution is shifted forward as the warm start, then a single SQP iteration is taken — never an inner convergence loop. The tick is timed and its projected KKT stationarity residual is checked, so a controller can fail closed (admitted is False) when the linearised step drifts beyond rti_residual_tol or violates the state envelope.

reset_warm_start

reset_warm_start()

Clear the Real-Time Iteration warm-start memory and control horizon.

benchmark_rti_latency

benchmark_rti_latency(x, x_ref, u_prev, *, warmup_ticks=2, timed_ticks=20)

Measure Real-Time Iteration tick latency percentiles on this host.

The report is local timing evidence on the recorded host, not a hard real-time guarantee; production sub-millisecond claims require isolated-core measurement on the declared target hardware.

cost_hessian_jax

cost_hessian_jax(x0, U, x_ref)

Exact Hessian d²J/dU² of the rolled-out NMPC cost through JAX autodiff.

The Hessian is taken with respect to the flattened control sequence and includes the second-order dynamics curvature, unlike the Gauss-Newton curvature used inside the condensed QP. It fails closed when JAX is unavailable or the plant is not JAX-traceable; existing analytic terminal and linearisation providers remain authoritative for the control law.

audit_cost_hessian_jax

audit_cost_hessian_jax(x0, U, x_ref, *, epsilon=0.0001, tolerance=0.001, sample_indices=None)

Audit the JAX cost Hessian against sampled finite differences.

A deterministic subset of Hessian entries is compared with independent central second differences of the NumPy cost rollout. The audit also reports the symmetry error and the smallest eigenvalue so callers can see whether the curvature is positive semidefinite at the evaluation point.

assert_cost_hessian_consistent

assert_cost_hessian_consistent(x0, U, x_ref, *, epsilon=0.0001, tolerance=0.001, sample_indices=None)

Return the cost-Hessian audit or fail closed on a finite-difference gap.

NMPC Transport-Model Tuning (v0.16.0)

The transport-model tuning entry points live in their own scpn_control.control.nmpc_transport_tuning module: fitting the transport model the controller tracks against is a distinct responsibility from receding-horizon tracking, so it is separated from nmpc_controller. The entry-point signatures and fail-closed semantics are unchanged. tune_transport_coefficients_for_tracking() connects NMPC controller tuning to the differentiable transport facade. It updates four-channel transport coefficients from the JAX gradient of the transport tracking loss, applies non-negative coefficient bounds and fractional update caps, and fails closed when JAX gradients are unavailable. By default, coefficient tuning also runs the differentiable-transport finite-difference gradient audit before admission and stores the audit result beside the validated transport campaign metadata for backend, dtype, radial grid, boundary conditions, closure provenance, and gradient tolerance. tune_neural_transport_closure_for_tracking() initialises the same tuning path from a bounded neural transport closure, preserving the differentiable facade's four-channel coefficient contract, the explicit JAX-gradient requirement, and the default gradient-audit admission gate. tune_transport_sources_for_tracking() applies the audited JAX gradient path to additive heating, fuelling, and impurity-source schedules. Source lower and upper bounds are explicit because replay studies may include physically valid sink terms, and every accepted update carries campaign metadata plus the gradient-audit result. tune_transport_source_rollout_for_tracking() extends that admission boundary from a single transport step to a complete (n_steps, 4, n_rho) source schedule. It uses JAX for the multi-step rollout gradient, requires a sampled NumPy finite-difference audit by default, clips per-entry source updates when configured, and records bounded campaign metadata before the schedule can enter NMPC tuning. The default audit-failure mode is fail-closed. The explicit gradient_audit_failure_mode="warn" mode exists only for advisory, non-control analysis and preserves the failed audit evidence in the returned result.

TransportCoefficientTuningResult dataclass

TransportCoefficientTuningResult(loss, gradient, updated_chi, step_norm, metadata, gradient_audit)

Result of a gradient-based transport-coefficient tuning step.

TransportSourceScheduleTuningResult dataclass

TransportSourceScheduleTuningResult(loss, gradient, updated_sources, step_norm, metadata, gradient_audit)

Result of a gradient-based transport source-schedule tuning step.

TransportSourceRolloutGradientAudit dataclass

TransportSourceRolloutGradientAudit(loss, epsilon, tolerance, checked_indices, source_max_abs_error, passed)

Finite-difference audit for multi-step source-schedule gradients.

TransportSourceRolloutTuningResult dataclass

TransportSourceRolloutTuningResult(loss, gradient, updated_sources, final_profiles, step_norm, metadata, gradient_audit)

Result of a gradient-based multi-step transport source rollout update.

tune_transport_coefficients_for_tracking

tune_transport_coefficients_for_tracking(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None, learning_rate, chi_min=0.0, max_fractional_update=0.1, gradient_tolerance=None, require_gradient_audit=True, gradient_audit_epsilon=1e-05, gradient_audit_tolerance=0.0005, gradient_audit_sample_indices=None, equilibrium_psi=None, _closure_for_metadata=None)

Tune transport coefficients for NMPC tracking through JAX autodiff.

The gradient is taken with respect to the four-channel transport coefficient profile used by scpn_control.core.differentiable_transport.transport_loss_gradient. This function intentionally has no finite-difference fallback: coefficient tuning is exposed to NMPC only when the differentiable JAX path is present.

tune_transport_sources_for_tracking

tune_transport_sources_for_tracking(profiles, chi, sources, target_profiles, rho, dt, edge_values, *, weights=None, learning_rate, source_min=None, source_max=None, max_absolute_update=None, gradient_tolerance=None, require_gradient_audit=True, gradient_audit_epsilon=1e-05, gradient_audit_tolerance=0.0005, gradient_audit_sample_indices=None, equilibrium_psi=None, _closure_for_metadata=None)

Tune additive transport source schedules through JAX autodiff.

This is the NMPC admission path for heating, fuelling, and impurity-source schedules. It uses the same differentiable transport loss as coefficient tuning, but applies the update to the source array and keeps source bounds explicit because sinks or negative feedback terms may be physically valid in reduced replay studies.

tune_transport_source_rollout_for_tracking

tune_transport_source_rollout_for_tracking(initial_profiles, chi, source_sequence, target_history, rho, dt, edge_values, *, weights=None, learning_rate, source_min=None, source_max=None, max_absolute_update=None, gradient_tolerance=None, require_gradient_audit=True, gradient_audit_failure_mode='raise', gradient_audit_epsilon=1e-05, gradient_audit_tolerance=0.0005, gradient_audit_sample_indices=None, equilibrium_psi=None, _closure_for_metadata=None)

Tune a full NMPC transport source rollout through JAX autodiff.

The update acts on the complete (n_steps, 4, n_rho) heating, fuelling, and impurity-source schedule. A sampled NumPy finite-difference audit is required by default so the controller does not admit unaudited JAX gradients for multi-step source optimisation.

tune_neural_transport_closure_for_tracking

tune_neural_transport_closure_for_tracking(profiles, closure, sources, target_profiles, rho, dt, edge_values, *, weights=None, learning_rate, impurity_diffusivity_fraction=1.0, chi_min=0.0, max_fractional_update=0.1, gradient_tolerance=None, require_gradient_audit=True, gradient_audit_epsilon=1e-05, gradient_audit_tolerance=0.0005, gradient_audit_sample_indices=None, equilibrium_psi=None)

Tune NMPC transport coefficients initialised from a neural closure.

Mu-Synthesis (v0.16.0)

MuSynthesisController

MuSynthesisController(plant_ss, uncertainty)

Structured robust controller with bounded static mu-analysis evidence.

Theory: Doyle 1982 (μ definition); Balas et al. 1993 (DK algorithm); Skogestad & Postlethwaite 2005, §8.5 (convergence and practical use).

Physical uncertainty model for tokamak control follows Ariola & Pironti 2008, Ch. 7: - plasma_position real_scalar ±2 cm - plasma_current real_scalar ±3 % - plasma_shape full ±5 %

synthesize

synthesize(n_dk_iter=5)

Run the bounded mu-analysis synthesis pass and store the controller.

step

step(x, dt)

Apply synthesised controller: u = -K x.

robustness_margin

robustness_margin()

Return 1/μ_peak — the structured stability margin.

μ_peak < 1 means the system is robustly stable for all Δ with ||Δ|| ≤ 1/μ_peak (Doyle 1982; Skogestad & Postlethwaite 2005, §8.2).

compute_mu_upper_bound

compute_mu_upper_bound(M, delta_structure)

D-scaling upper bound on μ(M).

Computes min_D σ̄(D M D^{-1}) where D is block-diagonal with positive real scalars matching delta_structure. This is always ≥ μ(M) and equals μ(M) for complex full blocks (Doyle 1982, IEE Proc. D 129, 242).

A finite-difference descent on log(D) is used to fit the static D-scaling.

Parameters

M : AnyFloatArray | AnyComplexArray, shape (n, n) Closed-loop transfer matrix evaluated at a single frequency (real or complex). delta_structure : list of (size, block_type) Block sizes and types from StructuredUncertainty.build_Delta_structure().

Returns

float Upper bound μ̄ ≥ μ(M).

MuSynthesisClaimEvidence dataclass

MuSynthesisClaimEvidence(schema_version, source, source_id, model_id, state_dimension, control_dimension, output_dimension, uncertainty_block_count, uncertainty_total_size, max_uncertainty_bound, block_structure, mu_peak_upper_bound, robustness_margin, controller_gain_frobenius_norm, d_scalings, closed_loop_spectral_abscissa, static_dc_analysis_only, reference_source, reference_dataset_id, reference_artifact_sha256, reference_case_count, mu_upper_bound_relative_error, robustness_margin_abs_error, controller_gain_relative_error, d_scaling_relative_error, closed_loop_spectral_abscissa_abs_error, mu_upper_bound_relative_tolerance, robustness_margin_abs_tolerance, controller_gain_relative_tolerance, d_scaling_relative_tolerance, closed_loop_spectral_abscissa_abs_tolerance, validated_claim_allowed, claim_status, payload_sha256='')

Serialisable evidence for bounded or externally validated μ-analysis claims.

mu_synthesis_claim_evidence

mu_synthesis_claim_evidence(controller, *, source, source_id, model_id='bounded_static_mu_synthesis', reference_artifact=None, mu_upper_bound_relative_tolerance=0.05, robustness_margin_abs_tolerance=0.05, controller_gain_relative_tolerance=0.1, d_scaling_relative_tolerance=0.1, closed_loop_spectral_abscissa_abs_tolerance=0.05)

Build fail-closed evidence for bounded static μ-analysis claims.

assert_mu_synthesis_validated_claim_admissible

assert_mu_synthesis_validated_claim_admissible(evidence)

Raise when μ-analysis evidence is insufficient for validated claims.

save_mu_synthesis_claim_evidence

save_mu_synthesis_claim_evidence(evidence, path)

Persist μ-analysis claim evidence as deterministic JSON.

load_mu_synthesis_claim_evidence

load_mu_synthesis_claim_evidence(path, *, require_validated_claim=False)

Load μ-analysis claim evidence with duplicate-key and digest admission.

Real-Time EFIT (v0.16.0)

RealtimeEFIT

RealtimeEFIT(diagnostics, R_grid, Z_grid, n_p_modes=3, n_ff_modes=3, vacuum_rb_phi=33.0)

Simplified real-time equilibrium reconstruction (EFIT).

reconstruct

reconstruct(measurements, *, coils=None, mode='psi_n', max_iter=25, tol=1e-05, regularization=1e-09, rel_sigma=0.02)

Reconstruct the equilibrium by weighted least-squares fitting of p'/FF'.

Implements the EFIT response-function inverse (Lao et al. 1985): for a fixed flux-surface geometry psi is linear in the profile coefficients, so the magnetic fit is a weighted linear least-squares problem; the geometry nonlinearity (psi_N depends on psi) is resolved by an outer Picard loop.

Parameters

measurements Magnetic diagnostics dict (flux_loops, b_probes, Ip). coils Optional external :class:CoilSet. When supplied, the reconstruction is free-boundary: the plasma basis uses the von Hagenow free-space boundary condition, the coil currents become additional unknowns (toroidal Green's-function columns), and the total flux is psi_plasma + psi_coil. When None the fixed-boundary inverse is used. mode "psi_n" fits p'/FF' as polynomials in the normalised flux (Picard iterated); "geometric" uses the fixed geometric-radius basis (single exact linear solve). max_iter, tol Picard iteration cap and relative-flux convergence tolerance. regularization Tikhonov coefficient (raise it for ill-conditioned diagnostic sets). rel_sigma Relative per-group measurement sigma used to build the fit weights.

find_lcfs

find_lcfs(psi)

Trace the last closed flux surface from a flux map.

Parameters

psi Poloidal-flux map on the EFIT R/Z grid.

Returns

AnyFloatArray The LCFS contour as an array of (R, Z) boundary points.

find_xpoint

find_xpoint(psi)

Locate magnetic nulls (dpsi/dR = 0, dpsi/dZ = 0).

compute_shape_params

compute_shape_params(psi, p_coeffs=None, ff_coeffs=None)

Compute plasma-shape parameters from a reconstructed flux map.

Delegates the macroscopic descriptors to the reusable :func:scpn_control.core.equilibrium_shape.compute_equilibrium_shape: R0/minor radius/elongation/triangularity from the boundary contour, li(3) from the poloidal-field volume integral, poloidal beta from the fitted pressure profile, and q95 from the toroidal flux function. The plasma current is taken from the flux map; a degenerate (no-plasma) map returns geometric defaults.

Parameters

psi Poloidal-flux map on the EFIT R/Z grid. p_coeffs, ff_coeffs Fitted p'(psi_N) / FF'(psi_N) coefficients (needed for beta_pol and q95); default to zeros when called without a reconstruction.

EFITLiteClaimEvidence dataclass

EFITLiteClaimEvidence(schema_version, source, source_id, diagnostic_source, model_id, grid_shape, n_flux_loops, n_b_probes, rogowski_radius_m, chi_squared, n_iterations, wall_time_ms, ip_reconstructed_A, q95, beta_pol, li, psi_relative_error, ip_relative_error, q95_abs_error, beta_pol_abs_error, li_abs_error, psi_relative_tolerance, ip_relative_tolerance, q95_abs_tolerance, beta_pol_abs_tolerance, li_abs_tolerance, facility_claim_allowed, claim_status)

Serialisable admission evidence for EFIT-lite reconstruction claims.

efit_lite_claim_evidence

efit_lite_claim_evidence(result, diagnostics, *, source, source_id, diagnostic_source, model_id='bounded_efit_lite', reference_psi=None, reference_shape=None, psi_relative_tolerance=0.05, ip_relative_tolerance=0.02, q95_abs_tolerance=0.1, beta_pol_abs_tolerance=0.1, li_abs_tolerance=0.1)

Build fail-closed evidence for EFIT-lite claim admission.

assert_efit_lite_facility_claim_admissible

assert_efit_lite_facility_claim_admissible(evidence)

Return evidence or fail closed before an EFIT-lite facility claim.

save_efit_lite_claim_evidence

save_efit_lite_claim_evidence(evidence, path)

Persist EFIT-lite claim evidence as deterministic JSON.

Gain-Scheduled Controller (v0.16.0)

GainScheduledController

GainScheduledController(controllers)

Multi-regime PID controller with bumpless gain interpolation.

Scheduling approach: Rugh & Shamma 2000, Automatica 36, 1401, §3. Bumpless transfer via linear interpolation over _TAU_SWITCH seconds avoids step transients at regime boundaries (Walker et al. 2006, §3.2).

LPV interpretation: gains are piecewise-affine in the scheduling vector (I_p, β_N, l_i) per Packard 1994, Systems & Control Letters 22, 79.

step

step(x, t, dt, detected_regime)

Compute PID output with bumpless gain interpolation.

On regime switch: α = (t - t_switch) / τ_switch ∈ [0,1]. Gains interpolated linearly: K(α) = (1-α) K_old + α K_new. Walker et al. 2006, §3.2, Eq. (4).

Shape Controller (v0.16.0)

PlasmaShapeController

PlasmaShapeController(target, coil_set, kernel)

Shape controller using a Tikhonov-regularised ISOFLUX pseudoinverse.

Gain matrix

K = (JᵀWJ + λI)⁻¹ JᵀW

Ferron et al. 1998, Nucl. Fusion 38, 1055: ISOFLUX control law maps ψ differences at boundary points to coil current corrections. The control law and its regularisation/weighting are faithful to the reference; the Jacobian (:class:ShapeJacobian) and the shape-error signal (:meth:_compute_shape_error) are synthetic placeholders, so this is a control-law reference and test surface rather than a validated real-plasma controller (see the module-level fidelity boundary).

step

step(psi, coil_currents)

Compute coil current corrections to reduce shape errors.

evaluate_performance

evaluate_performance(psi)

Evaluate shape-control errors for a flux map.

Parameters

psi Poloidal-flux map on the equilibrium grid.

Returns

ShapeControlResult Isoflux, gap, X-point, and strike-point errors and the minimum gap.

Safe RL Controller (v0.16.0)

LagrangianPPO

LagrangianPPO(env, lambda_lr=0.01, gamma=0.99, policy_lr=0.05, exploration_std=0.1, seed=0)

Clipped policy-gradient controller with Lagrangian safety multipliers.

Objective (Tessler et al. 2018, Algorithm 1): r_aug = r - Σ_i λ_i c_i

Multiplier update (dual gradient ascent): λ_i ← max(0, λ_i + lr (C_i - d_i))

Safety target: under the feasibility and convergence assumptions of the constrained-policy literature, the Lagrangian optimum satisfies J_C_i(π*) ≤ d_i for each constraint. This lightweight NumPy controller exposes that optimisation structure; it does not replace a formal runtime safety certificate.

The implementation is intentionally dependency-light: it uses a linear diagonal-Gaussian policy, clips the scalar advantage in the PPO spirit, and updates the policy from its own sampled rollouts instead of drawing actions directly from the environment's action-space sampler.

update_lambdas

update_lambdas(episode_costs)

Dual gradient ascent step.

λ_i ← max(0, λ_i + lr (C_i - d_i)) Achiam et al. 2017, §5, Lagrangian update.

train

train(total_timesteps)

Train the linear policy on augmented rewards and update λ multipliers.

The rollout action comes from the controller's current stochastic policy, not from action_space.sample(). Episode returns update the linear policy, while accumulated constraint costs update the Lagrangian multipliers after each episode.

predict

predict(obs)

Return an action for an observation.

Parameters

obs The current observation.

Returns

FloatArray The deterministic mean action from the learned linear policy, clipped to the environment action bounds.

Sliding-Mode Vertical (v0.16.0)

VerticalStabilizer

VerticalStabilizer(n_index, Ip_MA, R0, m_eff, tau_wall, smc)

Vertical-position (VS) controller for an elongated tokamak.

Vertical instability growth rate for elongated plasmas

γ ≈ (κ - 1) / τ_wall

where κ is elongation, τ_wall is the resistive wall time. Lazarus et al. 1990, Nucl. Fusion 30, 111, §2.

Real-time VS implementation at DIII-D: Humphreys et al. 2009, Nucl. Fusion 49, 115003.

Restoring force on the plasma column (rigid-body model): F = -n μ₀ I_p² / (4π R₀) · Z ≡ -K_vs · Z where n is the field-index (n < 0 for unstable equilibria), μ₀ = 4π×10⁻⁷ H/m (SI), I_p in A, R₀ in m. (Wesson 2004, "Tokamaks", Oxford, §3.7)

K_vs property

K_vs

Vertical restoring force coefficient [N/m].

K_vs = -n μ₀ I_p² / (4π R₀) Wesson 2004, §3.7, Eq. (3.7.4). Negative n_index (n < 0) makes K_vs > 0 (unstable).

step

step(Z_meas, Z_ref, dZ_dt_meas, dt)

Compute coil correction voltage.

Plant: m_eff Z̈ = -K_vs Z + F_coil (linearised, Humphreys 2009). e = Z_meas - Z_ref; SMC drives e → 0.

Scenario Scheduler (v0.16.0)

ScenarioOptimizer

ScenarioOptimizer(plant_model, target_state, T_total, dt=0.5)

Offline trajectory design.

optimize

optimize(n_iter=100)

Gradient-free optimization of breakpoint values.

Closed-Loop Integrated Scenario Demo (v0.22.1)

scpn_control.control.closed_loop_scenario wires the reusable ScenarioSchedule / FeedforwardController surface into IntegratedScenarioSimulator for the scpn-control demo --scenario combined path. The exported result carries controller commands, bounded auxiliary-power application, and a replay coupling audit. It is a deterministic repository wiring contract; measured-discharge validation remains gated by the physics traceability registry.

ClosedLoopScenarioStep dataclass

ClosedLoopScenarioStep(step_index, time_s, measured_w_thermal_j, target_w_thermal_j, thermal_error_fraction, commanded_p_aux_mw, applied_p_aux_mw, ip_ref_ma, beta_n, q_min, w_thermal_j)

One controller-to-plant exchange in the bounded scenario loop.

Attributes

step_index Zero-based controller step index. time_s Plant time after the integrated-scenario step. measured_w_thermal_j Thermal energy observed before the controller command. target_w_thermal_j Thermal-energy target used by the feedback trim. thermal_error_fraction Normalised (target - measured) / target error. commanded_p_aux_mw Auxiliary-heating command emitted by feedforward plus feedback. applied_p_aux_mw Auxiliary-heating command after actuator bounds. ip_ref_ma Plasma-current reference from the feedforward schedule. beta_n Normalised beta after the plant step. q_min Minimum q value after the plant step. w_thermal_j Thermal energy after the plant step.

ClosedLoopScenarioResult dataclass

ClosedLoopScenarioResult(schema_version, claim_status, steps, coupling_audit, initial_w_thermal_j, target_w_thermal_j, final_w_thermal_j, final_abs_error_fraction, p_aux_min_mw, p_aux_max_mw)

Completed bounded integrated-scenario closed-loop result.

Attributes

schema_version Stable result schema identifier for downstream JSON consumers. claim_status Human-readable claim boundary for the local wiring evidence. steps Ordered controller-to-plant exchanges captured during the run. coupling_audit Integrated-scenario replay audit for the produced plant states. initial_w_thermal_j Thermal energy at plant initialisation. target_w_thermal_j Thermal-energy target used by the feedback trim. final_w_thermal_j Thermal energy after the final plant step. final_abs_error_fraction Absolute final thermal-energy error normalised by the target. p_aux_min_mw Lower auxiliary-heating actuator bound in megawatts. p_aux_max_mw Upper auxiliary-heating actuator bound in megawatts.

run_integrated_scenario_closed_loop

run_integrated_scenario_closed_loop(config=None, *, target_w_thermal_j=None, feedback_gain_mw=25.0, p_aux_bounds_mw=(0.0, 20.0), max_steps=None)

Run a bounded controller-to-integrated-scenario closed loop.

The loop uses :class:~scpn_control.control.scenario_scheduler.FeedforwardController to combine a constant scenario schedule with a thermal-energy feedback trim. The command is applied to ScenarioConfig.P_aux_MW before each :class:~scpn_control.core.integrated_scenario.IntegratedScenarioSimulator plant step, then the completed replay is audited with :func:~scpn_control.core.integrated_scenario.audit_scenario_coupling.

This is a deterministic repository wiring contract, not measured-discharge validation or facility-control evidence.

Fault-Tolerant Control (v0.16.0)

ReconfigurableController

ReconfigurableController(base_controller, jacobian, n_coils, n_sensors)

Control-allocation reconfiguration after actuator or sensor faults.

Weighted pseudo-inverse gain: u = (J^T W J + λI)^{-1} J^T W v. Reference: Bodson 2002, J. Guidance 25, 307, Eq. 12.

Faulted coil columns are zeroed in J before the gain is recomputed, matching the reconfiguration procedure in Ambrosino et al. 2008, Fusion Eng. Des. 83, 1485 for the ITER VS system.

handle_actuator_fault

handle_actuator_fault(coil_index, fault_type, stuck_val=0.0)

Reconfigure the controller around a faulted actuator coil.

Zeroes the coil's Jacobian column and recomputes the control gain; records the held value for a stuck actuator.

Parameters

coil_index Index of the faulted coil. fault_type The actuator fault category. stuck_val Held output value for a stuck actuator.

handle_sensor_fault

handle_sensor_fault(sensor_index, fault_type)

Reconfigure the controller around a faulted sensor.

Zeroes the sensor's weighting and recomputes the control gain.

Parameters

sensor_index Index of the faulted sensor. fault_type The sensor fault category (dropout, drift, or noise increase).

Raises

IndexError If sensor_index is outside the configured sensor range. ValueError If fault_type is not a sensor fault.

step

step(error, dt)

Apply reconfigured gain; compensate for stuck-coil offsets.

controllability_check

controllability_check()

Return True if the remaining actuators span the minimum required target space.

MIN_REQUIRED_RANK = 2 covers control of Ip and vertical position, the two safety-critical outputs identified by Ambrosino et al. 2008.

graceful_shutdown

graceful_shutdown()

Return zero ramp-down command for all coils.

RZIp Model (v0.16.0)

RZIPModel

RZIPModel(R0, a, kappa, Ip_MA, B0, n_index, vessel, active_coils=None, vertical_inertia_kg=1.0)

Rigid-plasma (RZIP) vertical-displacement response and circuit model.

Assembles the linear state space x = [Z, dZ/dt, I_1, …, I_n] coupling the vertical plasma motion to the passive vessel and active-coil currents, where the destabilising force constant is K = n_index μ₀ I_p² / (4π R0).

Parameters

R0 Major radius in metres; must be finite and positive. a Minor radius in metres; must be finite, positive, and below R0. kappa Plasma elongation (dimensionless); must be positive. Ip_MA Plasma current in MA; must be positive. B0 Toroidal field on axis in tesla; must be positive. n_index Field decay index (dimensionless); n < 0 is vertically unstable. vessel Conducting-vessel model supplying element inductances and resistances. active_coils Optional active control coils appended to the circuit. vertical_inertia_kg Effective vertical inertia of the bounded RZIP plant in kg; must be positive.

build_state_space

build_state_space()

Assemble the linear state-space matrices of the RZIP plant.

States are [Z, dZ/dt, I_1, …, I_n_circuits] and inputs are the active-coil voltages.

Returns

tuple of NDArray[np.float64] The (A, B, C, D) matrices: A is (n_states, n_states), B is (n_states, n_coils), C selects Z as the output, and D is zero.

Raises

ValueError If the circuit inductance matrix is singular.

vertical_growth_rate

vertical_growth_rate()

Largest real eigenvalue of the open-loop plant (growth rate).

Returns

float The vertical instability growth rate in s⁻¹ (negative if stable).

vertical_growth_time

vertical_growth_time()

Vertical-instability e-folding time.

Returns

float The growth time in milliseconds (inf if the plant is stable).

stability_margin

stability_margin()

Passive vertical-stability margin.

Returns

float The field decay index n_index; positive values are passively stable, the marginal point being n_index = 0.

RZIPController

RZIPController(rzip, Kp, Kd)

LQR vertical-position controller for the RZIP plant.

Solves the continuous-time algebraic Riccati equation for the optimal gain weighting position (Kp) and velocity (Kd). If SciPy's Riccati input validation fails because of a local numerical-stack mismatch, the controller falls back to a finite-horizon discrete Riccati iteration for the same plant. Zero gain is the final fail-closed fallback when no feedback design can be computed.

Parameters

rzip The RZIP plant model providing the state space. Kp Position state-cost weight (dimensionless); floored at 1.0, must be non-negative. Kd Velocity state-cost weight (dimensionless); floored at 1.0, must be non-negative.

step

step(dZ_measured, dt)

Compute the active-coil voltage command for one control cycle.

Estimates the vertical velocity from successive position measurements and applies the LQR law u = -K x with coil and wall currents assumed zero (no observer).

Parameters

dZ_measured Measured vertical displacement in metres; must be finite. dt Control time step in seconds; must be positive.

Returns

NDArray[np.float64] The commanded active-coil voltages in volts, shape (n_coils,).

closed_loop_eigenvalues

closed_loop_eigenvalues()

Eigenvalues of the LQR closed-loop system matrix A - B K.

Returns

NDArray[np.float64] The complex closed-loop eigenvalues; negative real parts indicate a stabilised vertical mode.

RZIPCalibrationEvidence dataclass

RZIPCalibrationEvidence(schema_version, source, source_id, model_id, vertical_inertia_kg, wall_time_constant_s, growth_rate_s_inv, growth_time_ms, reference_growth_rate_s_inv, growth_rate_relative_error, growth_rate_relative_tolerance, facility_claim_allowed, claim_status, evidence_payload_sha256)

Serialisable calibration/admission evidence for a bounded RZIP plant.

rzip_calibration_evidence

rzip_calibration_evidence(rzip, *, source, source_id, wall_time_constant_s, model_id='bounded_rzip', reference_growth_rate_s_inv=None, growth_rate_relative_tolerance=0.2)

Build fail-closed calibration evidence for RZIP claim admission.

local_regression_reference evidence is useful for deterministic regression reports but never permits facility claims. Facility claims require documented public, external-code, or measured-discharge reference sources and must satisfy the declared growth-rate tolerance.

assert_rzip_facility_claim_admissible

assert_rzip_facility_claim_admissible(evidence)

Return evidence or fail closed before a RZIP facility-control claim.

save_rzip_calibration_evidence

save_rzip_calibration_evidence(evidence, path)

Persist RZIP calibration evidence as deterministic JSON.

RWM Feedback (v0.16.0)

RWMFeedbackController

RWMFeedbackController(n_sensors, n_coils, G_p, G_d, tau_controller=0.0001, M_coil=1.0)

Sensor-coil PD feedback for RWM stabilization.

The closed-loop effective growth rate combines wall physics (including rotation) with proportional feedback suppression:

γ_eff = γ_total − G_p M_coil γ_total / (1 + γ_total τ_ctrl)

Garofalo et al. 2002, Phys. Plasmas 9, 1997, Sec. III.

step

step(B_r_sensors, dt)

Compute feedback coil currents from sensor measurements.

I = G_p B_r + G_d dB_r/dt

effective_growth_rate

effective_growth_rate(rwm)

Closed-loop growth rate.

γ_eff = γ_total − G_p M_coil γ_total / (1 + γ_total τ_ctrl)

Garofalo et al. 2002, Phys. Plasmas 9, 1997, Eq. (5). Uses γ_total from RWMPhysics (includes rotation if Ω_φ ≠ 0).

is_stabilized

is_stabilized(rwm)

Return True when the closed-loop growth rate is negative.

RWMClaimEvidence dataclass

RWMClaimEvidence(schema_version, source, source_id, model_id, beta_n, beta_n_nowall, beta_n_wall, tau_wall_s, tau_eff_s, omega_phi_rad_s, wall_radius_m, plasma_radius_m, n_sensors, n_coils, proportional_gain, derivative_gain, controller_latency_s, coil_coupling, open_loop_growth_rate_s_inv, closed_loop_growth_rate_s_inv, required_proportional_gain, reference_closed_loop_growth_rate_s_inv, closed_loop_growth_rate_abs_error, closed_loop_growth_rate_abs_tolerance, facility_claim_allowed, claim_status)

Serialisable admission evidence for RWM feedback claims.

rwm_claim_evidence

rwm_claim_evidence(rwm, controller, *, source, source_id, model_id='bounded_rwm_feedback', reference_closed_loop_growth_rate_s_inv=None, closed_loop_growth_rate_abs_tolerance=5.0)

Build fail-closed evidence for RWM feedback claim admission.

Local regression evidence is admissible only for bounded repository claims. Facility claims require documented public, external MHD, or measured-shot references plus a finite closed-loop growth-rate comparison within the declared tolerance.

assert_rwm_facility_claim_admissible

assert_rwm_facility_claim_admissible(evidence)

Return evidence or fail closed before a RWM facility-control claim.

save_rwm_claim_evidence

save_rwm_claim_evidence(evidence, path)

Persist RWM claim evidence as deterministic JSON.


Complete Module Index

This index keeps the published API reference aligned with every tracked Python module under src/scpn_control/. Domain pages above highlight primary entry points; this section exposes the remaining module surfaces through mkdocstrings so public signatures and docstrings stay visible as the codebase grows.

Top-Level CLI

Cli

cli

CLI for scpn-control.

Usage::

scpn-control demo --scenario combined --steps 1000
scpn-control benchmark --n-bench 5000
scpn-control validate
scpn-control validate-manifest real_manifest.json --json-out
scpn-control validate-data-manifests --json-out
scpn-control validate-release-evidence artifacts/release_evidence_report.json --json-out
scpn-control validate-physics-traceability --json-out
scpn-control validate-gk-geometry-reference --json-out
scpn-control validate-gk-species-reference --json-out
scpn-control validate-jax-gk-parity --require-parity-artifacts --json-out
scpn-control validate-gk-ood-calibration --require-campaign-artifacts --json-out
scpn-control validate-gk-interface-artifacts --require-interface-artifacts --json-out
scpn-control validate-blob-transport-reference --require-reference-artifacts --json-out
scpn-control validate-elm-reference --require-reference-artifacts --json-out
scpn-control validate-eped-reference --require-reference-artifacts --json-out
scpn-control validate-marfe-reference --require-reference-artifacts --json-out
scpn-control validate-ntm-reference --require-reference-artifacts --json-out
scpn-control validate-neural-equilibrium-reference --require-reference-artifacts --json-out
scpn-control validate-neural-transport-reference --require-reference-artifacts --json-out
scpn-control validate-neural-turbulence-reference --require-reference-artifacts --json-out
scpn-control validate-orbit-reference --require-reference-artifacts --json-out
scpn-control validate-uncertainty-reference --require-reference-artifacts --json-out
scpn-control validate-vmec-reference --require-reference-artifacts --json-out
scpn-control validate-rzip-reference --require-reference-artifacts --json-out
scpn-control validate-density-reference --require-reference-artifacts --json-out
scpn-control validate-burn-reference --require-reference-artifacts --json-out
scpn-control validate-volt-second-reference --require-reference-artifacts --json-out
scpn-control validate-current-drive-reference --require-reference-artifacts --json-out
scpn-control validate-mu-synthesis-reference --require-reference-artifacts --json-out
scpn-control validate-free-boundary-reference --require-reference-artifacts --json-out
scpn-control validate-disruption-reference --require-reference-artifacts --json-out
scpn-control validate-digital-twin-reference --require-reference-artifacts --json-out
scpn-control validate-soc-reference --require-reference-artifacts --json-out
scpn-control acquire-mdsplus-shot --spec-json validation/reference_data/diiid/acquisition_specs/shot_163303_mdsplus.json
scpn-control live --port 8765 --zeta 0.5
scpn-control hil-test --shots-dir validation/reference_data/diiid/disruption_shots
scpn-control run-hardware-campaign --neurons 64 --core-snn 1 --core-z3 2 --core-net 3 --core-hb 4

main

main()

SCPN Control — Neuro-symbolic Stochastic Petri Net controller.

demo

demo(scenario, steps, json_out)

Run a closed-loop control demo.

benchmark

benchmark(n_bench, n_warmup, json_out)

Benchmark PID versus SNN control-step latency.

A warm-up phase is executed but excluded from timing to isolate steady-state micro-benchmark behavior from cold-start effects (JIT, allocator, cache, init).

run_hardware_campaign

run_hardware_campaign(neurons, seed, state_init_r, state_init_z, plant_gain, target_r, target_z, beta_n_limit, itpa_cgb, itpa_path, kuramoto_weights, transport_endpoint, transport_port, transport_ttl, transport_max_queue, transport_backend, execution_backend, heartbeat_port, heartbeat_timeout_ms, core_snn, core_z3, core_net, core_hb, steps, runtime_s, tick_interval_s, pacing_mode, runtime_admission_policy, max_publish_failures, formal_mode, formal_stride, formal_depth, formal_max_marking, formal_channel_capacity, require_native, json_out)

Launch the Rust-native execution plane from Python control layer.

acquire_mdsplus_shot_command

acquire_mdsplus_shot_command(spec_json, tree, shot, signals_json, output_npz, manifest_json, source_uri, access_policy, licence, retrieved_at, json_out)

Acquire selected MDSplus shot signals and write a validated manifest.

live

live(port, host, layers, n_per, zeta, psi, tick_interval, api_key, command_rate_limit, command_rate_window_s, max_payload_bytes, max_client_write_buffer_bytes, allow_unauthenticated_clients, allow_query_token_auth, require_tls, allow_insecure_remote, allowed_origin, allowed_action, tls_cert, tls_key)

Start real-time WebSocket phase sync server.

Streams Kuramoto R/V/lambda tick snapshots over ws://:. Connect with: examples/streamlit_ws_client.py or any WS client.

hil_test

hil_test(shots_dir, json_out)

Hardware-in-the-loop test campaign against reference shots.

info

info(json_out)

Print environment and backend information.

CLI Reference Validators

cli_reference_validators

Click commands that validate persisted reference-data artifacts.

Each command wraps a validation.validate_* reference check, emits a text or JSON report, and exits non-zero on failure. They are registered onto the root scpn-control group via :data:REFERENCE_VALIDATOR_COMMANDS in :mod:scpn_control.cli, keeping the CLI entry point a thin dispatcher.

validate_gk_crosscode_command

validate_gk_crosscode_command(evidence_root, require_external_runs, output_json, json_out)

Validate real external-code evidence for linear GK agreement.

validate_gk_geometry_reference_command

validate_gk_geometry_reference_command(reference_path, output_json, json_out)

Validate Miller geometry output against immutable reference cases.

validate_gk_species_reference_command

validate_gk_species_reference_command(reference_path, output_json, json_out)

Validate GK species normalisation and collision reference cases.

validate_jax_gk_parity_command

validate_jax_gk_parity_command(artifact_root, require_parity_artifacts, require_cases, require_backends, output_json, json_out)

Validate persisted JAX/native GK parity artifacts.

validate_gk_ood_calibration_command

validate_gk_ood_calibration_command(artifact_root, require_campaign_artifacts, output_json, json_out)

Validate persisted GK OOD calibration campaign artifacts.

validate_gk_interface_artifacts_command

validate_gk_interface_artifacts_command(artifact_root, require_interface_artifacts, output_json, json_out)

Validate persisted external GK interface parser artifacts.

validate_blob_transport_reference_command

validate_blob_transport_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted blob-transport reference artifacts.

validate_elm_reference_command

validate_elm_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted ELM reference artifacts.

validate_eped_reference_command

validate_eped_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted EPED reference artifacts.

validate_marfe_reference_command

validate_marfe_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted MARFE reference artifacts.

validate_ntm_reference_command

validate_ntm_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted NTM reference artifacts.

validate_neural_equilibrium_reference_command

validate_neural_equilibrium_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted neural-equilibrium reference artifacts.

validate_neural_transport_reference_command

validate_neural_transport_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted neural-transport reference artifacts.

validate_neural_turbulence_reference_command

validate_neural_turbulence_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted neural-turbulence reference artifacts.

validate_orbit_reference_command

validate_orbit_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted orbit-following reference artifacts.

validate_uncertainty_reference_command

validate_uncertainty_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted uncertainty-quantification reference artifacts.

validate_vmec_reference_command

validate_vmec_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted VMEC-lite reference artifacts.

validate_rzip_reference_command

validate_rzip_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted RZIP vertical-stability reference artifacts.

validate_current_drive_reference_command

validate_current_drive_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted current-drive reference artifacts.

validate_mu_synthesis_reference_command

validate_mu_synthesis_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted mu-synthesis reference artifacts.

validate_volt_second_reference_command

validate_volt_second_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted volt-second reference artifacts.

validate_burn_reference_command

validate_burn_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted DT burn-control reference artifacts.

validate_density_reference_command

validate_density_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted density-control reference artifacts.

validate_free_boundary_reference_command

validate_free_boundary_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted free-boundary tracking reference artifacts.

validate_disruption_reference_command

validate_disruption_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted disruption-mitigation reference artifacts.

validate_digital_twin_reference_command

validate_digital_twin_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted tokamak digital-twin reference artifacts.

validate_soc_reference_command

validate_soc_reference_command(artifact_root, require_reference_artifacts, output_json, json_out)

Validate persisted SOC turbulence-learning reference artifacts.

CLI Evidence Validators

cli_evidence_validators

Click commands that validate persisted evidence and data-manifest artifacts.

Each command wraps a validation.validate_* evidence check (or a manifest / RMSE dashboard entry point), emits a text or JSON report, and exits non-zero on failure. They are registered onto the root scpn-control group via :data:EVIDENCE_VALIDATOR_COMMANDS in :mod:scpn_control.cli, keeping the CLI entry point a thin dispatcher over the core operational commands.

validate

validate(json_out, data_manifest_root, no_data_manifests, no_verify_artifacts, jax_gk_parity_root, no_jax_gk_parity, physics_traceability_registry, no_physics_traceability, multi_shot_campaign_python_report, multi_shot_campaign_rust_report, multi_shot_min_digest_count, no_multi_shot_campaign_evidence, runtime_admission_report, no_runtime_admission_evidence, native_formal_certificate_report, native_formal_max_aot_p99_cycle_us, no_native_formal_certificate)

Run import hygiene, provenance, parity, traceability, and formal evidence validation.

validate_release_evidence_command

validate_release_evidence_command(report, json_out)

Validate top-level release evidence report admission.

validate_manifest

validate_manifest(manifest, json_out, verify_artifact)

Validate real-shot or synthetic-shot data manifest provenance.

validate_data_manifests_command

validate_data_manifests_command(root, output_json, no_verify_artifacts, require_real_acquisition, json_out)

Validate repository data manifests, local artefacts, and acquisition specs.

validate_physics_traceability_command

validate_physics_traceability_command(registry, output_json, json_out)

Validate physics fidelity traceability and bounded-claim contracts.

validate_rmse

validate_rmse(json_out, output_json, output_md)

Run full RMSE validation dashboard against reference data.

Shared Typing Aliases

_typing

Canonical numpy array-type aliases for the staged strict = true migration.

The package is migrating module by module to fully strict mypy (disallow_any_generics), which forbids bare numpy.ndarray annotations. Several phase modules already define FloatArray = NDArray[np.float64] locally; this module promotes that idiom to a single shared definition and adds the broader input alias the transport-solver modules need.

Conventions

FloatArray Use for arrays the code constructs, returns, or stores (attributes, return types, locals). Every public entry point normalises with numpy.asarray(..., dtype=float), so float64 is the precise dtype these positions carry.

AnyFloatArray Use for public input parameters that may legitimately receive an array of any floating precision — most importantly the output of numpy.gradient and arrays produced by neighbouring modules — so callers need not pre-cast. A FloatArray is assignable to AnyFloatArray but not the reverse, which is why the distinction matters at module boundaries.

AnyComplexArray Use for public input parameters of frequency-domain routines (structured singular value / μ-analysis) that legitimately receive a complex response matrix. Pair it with AnyFloatArray in a union when a routine accepts a real or complex matrix and normalises internally with numpy.asarray(..., dtype=complex).

Shape typing

numpy.typing.NDArray is ndarray[tuple[int, ...], dtype[T]] (any number of dimensions), whereas numpy.zeros(n) with a scalar argument infers the narrower ndarray[tuple[int], dtype[float64]] (exactly one dimension). Assigning an any-dimensional value into an attribute whose type mypy inferred as one dimensional fails under numpy 2.x shape typing. Always annotate array attributes and dataclass fields explicitly with FloatArray (rather than relying on inference from numpy.zeros/numpy.empty) so stored arrays keep the any-dimensional shape type and accept reconstructed values.

Shared NPZ Writer

_npz

Typed helpers for NumPy .npz archives used by control artefacts.

save_npz_arrays

save_npz_arrays(path, arrays)

Write named numeric arrays to an uncompressed NumPy .npz archive.

Parameters

path : str or PathLike[str] Destination archive path. arrays : Mapping[str, ArrayLike] Named arrays to store. Keys become member names in the archive and must be simple file-name components.

Raises

ValueError If an array name is empty, path-like, or an array requires pickling.

Control Modules

Burn Controller

burn_controller

D-T burn-control, alpha-heating, Lawson, and auxiliary-heating utilities.

BurnControlClaimEvidence dataclass

BurnControlClaimEvidence(schema_version, source, source_id, model_id, R0_m, a_m, kappa, profile_points, ne_volume_average_1e20_m3, Te_volume_average_keV, Ti_volume_average_keV, P_alpha_MW, P_aux_MW, Q, lawson_triple_product, lawson_margin, burn_fraction, reactivity_exponent, thermally_stable, controller_Q_target, controller_T_target_keV, controller_P_aux_max_MW, controller_command_MW, reference_source, reference_dataset_id, reference_artifact_sha256, reference_case_count, P_alpha_relative_error, Q_abs_error, lawson_margin_abs_error, burn_fraction_relative_error, reactivity_exponent_abs_error, P_alpha_relative_tolerance, Q_abs_tolerance, lawson_margin_abs_tolerance, burn_fraction_relative_tolerance, reactivity_exponent_abs_tolerance, reactor_claim_allowed, claim_status)

Serialisable evidence for bounded or validated burn-control claims.

AlphaHeating

AlphaHeating(R0, a, kappa=1.0)

Alpha heating power from D-T fusion.

P_α = n_D n_T <σv> E_α × V. ITER Physics Basis 1999, Nucl. Fusion 39, 2137. Reactivity <σv>_DT: Bosch & Hale 1992, Nucl. Fusion 32, 611.

power_density

power_density(ne_20, Te_keV, Ti_keV)

Alpha power density [MW/m^3] for 50:50 D-T mixture.

p_α = n_D n_T <σv>(T_i) E_α. ITER Physics Basis 1999, Nucl. Fusion 39, 2137, Eq. (2.2.1).

power

power(ne_20, Te_keV, Ti_keV, rho)

P_alpha [MW] integrated over plasma volume.

dV = 4π² R₀ a² κ ρ dρ (torus shell element in normalised radius).

Q

Q(P_alpha_MW, P_aux_MW)

Fusion energy gain Q = P_fus / P_aux = 5 P_α / P_aux.

P_fus = 5 P_α because α carries 1/5 of total DT energy (E_α/E_fus = 3.52/17.6). Delayed feedback for alpha-heating runaway: Mitarai & Muraoka 1999, Nucl. Fusion 39, 725.

BurnStabilityAnalysis

BurnStabilityAnalysis(alpha_heating)

Thermal burn stability based on reactivity exponent.

Delayed alpha-heating feedback control: Mitarai & Muraoka 1999, Nucl. Fusion 39, 725.

reactivity_exponent

reactivity_exponent(Ti_keV)

d(ln <σv>) / d(ln T), evaluated via finite difference.

Stability requires this exponent < 2. Mitarai & Muraoka 1999, Nucl. Fusion 39, 725, Eq. (5).

is_thermally_stable

is_thermally_stable(Ti_keV)

Return True if d(ln <σv>)/d(ln T) < 2 (Mitarai & Muraoka 1999).

stability_boundary_keV

stability_boundary_keV()

T where d(ln <σv>)/d(ln T) = 2 (bisection, 5–30 keV).

BurnController

BurnController(Q_target=10.0, T_target_keV=20.0, P_aux_max_MW=73.0)

PI burn controller targeting Q and T_i.

Delayed feedback prevents alpha-heating runaway. Mitarai & Muraoka 1999, Nucl. Fusion 39, 725. P_aux_max = 73 MW matches ITER heating system capacity. ITER Physics Basis 1999, Nucl. Fusion 39, 2137, Table I.

step

step(Q_meas, T_meas_keV, P_alpha_MW, dt)

Advance the burn-temperature PI controller one step.

Suppresses auxiliary heating above 30 keV to prevent alpha runaway (Mitarai & Muraoka 1999).

Parameters

Q_meas Measured fusion gain; must be non-negative. T_meas_keV Measured ion temperature in keV; must be non-negative. P_alpha_MW Alpha-heating power in MW; must be non-negative. dt Time step in seconds; must be positive.

Returns

float The commanded auxiliary heating power in MW, clipped to [0, P_aux_max].

BurnPoint dataclass

BurnPoint(Te_keV, P_alpha_MW, P_loss_MW, Q, stable)

A candidate burn operating point from a temperature scan.

Attributes

Te_keV Electron temperature in keV. P_alpha_MW Alpha-heating power in MW. P_loss_MW Energy-loss power in MW. Q Fusion gain at this point. stable Whether the point is thermally stable.

SubignitedBurnPoint

SubignitedBurnPoint(alpha_heating)

Sub-ignited burn operating-point finder via 0-D power balance.

Parameters

alpha_heating The alpha-heating model providing the geometry and P_α(T).

find_operating_point

find_operating_point(ne_20, P_aux_MW, tau_E_s)

Scan T to find P_α(T) + P_aux = P_loss(T) intersections.

P_loss = 3 n T V / τ_E (energy balance, 0-D model). Lawson criterion: n τ_E T > 3×10^21 m^-3 s keV for ignition. Lawson 1957, Proc. Phys. Soc. B 70, 6.

burn_control_claim_evidence

burn_control_claim_evidence(alpha_heating, controller, *, rho, ne_20, Te_keV, Ti_keV, tau_E_s, P_aux_MW, source, source_id, model_id='bounded_dt_burn_control', reference_artifact=None, P_alpha_relative_tolerance=0.05, Q_abs_tolerance=0.5, lawson_margin_abs_tolerance=0.2, burn_fraction_relative_tolerance=0.1, reactivity_exponent_abs_tolerance=0.25)

Build fail-closed DT burn-control evidence from explicit profiles.

assert_burn_control_reactor_claim_admissible

assert_burn_control_reactor_claim_admissible(evidence)

Raise when burn-control evidence is insufficient for reactor-control claims.

save_burn_control_claim_evidence

save_burn_control_claim_evidence(evidence, path)

Persist burn-control claim evidence as deterministic JSON.

lawson_triple_product

lawson_triple_product(ne_m3, tau_E_s, T_keV)

Return n τ_E T [m^-3 s keV].

Ignition requires n τ_E T > 3×10^21 m^-3 s keV. Lawson 1957, Proc. Phys. Soc. B 70, 6.

burn_fraction

burn_fraction(n_dt_m3, sigv, v_th_ms, a_m)

Approximate DT burn fraction.

f_b ≈ a² n_DT <σv> / (4 v_th). Wesson 2011, "Tokamaks" 4th ed., Eq. 1.7.3.

Codac Interface

codac_interface

ITER CODAC/EPICS integration prototype.

Generates EPICS .db and OPC-UA XML nodeset files for binding a NeuroSymbolicController to the ITER Plant Instrumentation & Control (I&C) infrastructure. Does NOT require pyepics or any EPICS runtime.

References

ITER CODAC Handbook v7.0, §3.2 (PV naming conventions)
IEC 62541 (OPC Unified Architecture)

CODACConfig dataclass

CODACConfig(pv_prefix='ITER-SCPN', cycle_hz=1000.0, timeout_ms=1.5, heartbeat_pv='ITER-SCPN:HEARTBEAT', interlock_pvs=('ITER-CIS:INTERLOCK:VDE', 'ITER-CIS:INTERLOCK:HALO', 'ITER-CIS:INTERLOCK:DISRUPTION'))

CODAC plant-system configuration.

EPICSChannel dataclass

EPICSChannel(pv_name, direction, dtype, units, description, low_limit=0.0, high_limit=0.0)

Single EPICS process variable definition.

CODACRuntimeEvidence dataclass

CODACRuntimeEvidence(schema_version, generated_utc, controller_id, plant_system, pv_prefix, cycle_hz, cycle_budget_us, timeout_budget_us, deadline_us, observed_cycle_p50_us, observed_cycle_p95_us, observed_cycle_p99_us, observed_cycle_max_us, input_channel_count, output_channel_count, interlock_pv_count, interlock_checks, interlock_blocks, backpressure_events, epics_db_sha256, opcua_nodeset_sha256, facility_claim_allowed, claim_status, payload_sha256)

Tamper-evident CODAC/EPICS runtime admission evidence.

CODACInterface

CODACInterface(config, controller)

Binds a NeuroSymbolicController to ITER CODAC I&C channels.

define_input_channels

define_input_channels()

Return the EPICS input (process-variable) channels read each cycle.

define_output_channels

define_output_channels()

Return the EPICS output (actuator) channels written each cycle.

pack_observation

pack_observation(pv_values)

Convert raw EPICS PV dict to ControlObservation for the controller.

unpack_action

unpack_action(action)

Convert controller actions to EPICS process-variable values.

Parameters

action : Mapping[str, float] Controller action values keyed by the CODAC action names in _OUTPUT_KEY_MAP. Missing actions default to zero output so an incomplete controller command maps to a fail-stationary actuator packet.

Returns

dict[str, float] EPICS process-variable values keyed by fully qualified PV names.

run_cycle

run_cycle(pv_values)

Single control cycle: pack -> step -> unpack.

safety_interlock

safety_interlock(pv_values)

Return True if any hard limit is violated (actuation must be blocked).

render_epics_db

render_epics_db()

Return EPICS .db text with record() entries for all channels.

generate_epics_db

generate_epics_db(output_path)

Write EPICS .db file with record() entries for all channels.

render_opcua_nodeset

render_opcua_nodeset()

Return OPC-UA XML nodeset text for ITER SDN integration.

generate_opcua_nodeset

generate_opcua_nodeset(output_path)

Write OPC-UA XML nodeset for ITER SDN integration.

CycleTimer

CycleTimer(cycle_hz)

Enforces real-time cycle budget with deadline monitoring.

Uses time.perf_counter_ns() for sub-microsecond resolution.

start_cycle

start_cycle()

Mark the start of a control cycle for jitter measurement.

end_cycle

end_cycle()

Return jitter in milliseconds (elapsed - budget).

check_overrun

check_overrun()

Return True if the last cycle exceeded its budget.

codac_runtime_evidence

codac_runtime_evidence(interface, *, controller_id, observed_cycle_us, interlock_checks, interlock_blocks, backpressure_events=0, plant_system='ITER-CODAC-EPICS', generated_utc=None, facility_claim_allowed=False)

Build tamper-evident runtime evidence for a CODAC/EPICS boundary.

assert_codac_runtime_claim_admissible

assert_codac_runtime_claim_admissible(evidence)

Fail closed unless CODAC runtime evidence can support a facility claim.

save_codac_runtime_evidence

save_codac_runtime_evidence(evidence, output_path)

Persist CODAC runtime evidence as canonical sorted JSON.

load_codac_runtime_evidence

load_codac_runtime_evidence(path, *, require_facility_claim=False)

Load CODAC runtime evidence with duplicate-key and digest admission.

CODACRuntimeEvidence dataclass

CODACRuntimeEvidence(schema_version, generated_utc, controller_id, plant_system, pv_prefix, cycle_hz, cycle_budget_us, timeout_budget_us, deadline_us, observed_cycle_p50_us, observed_cycle_p95_us, observed_cycle_p99_us, observed_cycle_max_us, input_channel_count, output_channel_count, interlock_pv_count, interlock_checks, interlock_blocks, backpressure_events, epics_db_sha256, opcua_nodeset_sha256, facility_claim_allowed, claim_status, payload_sha256)

Tamper-evident CODAC/EPICS runtime admission evidence.

codac_runtime_evidence

codac_runtime_evidence(interface, *, controller_id, observed_cycle_us, interlock_checks, interlock_blocks, backpressure_events=0, plant_system='ITER-CODAC-EPICS', generated_utc=None, facility_claim_allowed=False)

Build tamper-evident runtime evidence for a CODAC/EPICS boundary.

assert_codac_runtime_claim_admissible

assert_codac_runtime_claim_admissible(evidence)

Fail closed unless CODAC runtime evidence can support a facility claim.

save_codac_runtime_evidence

save_codac_runtime_evidence(evidence, output_path)

Persist CODAC runtime evidence as canonical sorted JSON.

load_codac_runtime_evidence

load_codac_runtime_evidence(path, *, require_facility_claim=False)

Load CODAC runtime evidence with duplicate-key and digest admission.

Controller Tuning

controller_tuning

Automated controller gain tuning using Bayesian optimisation.

Optimises PID and H-infinity parameters against Gymnasium environments to minimise tracking error. The PID objective evaluates the full parallel-form control law u = Kp·e + Ki·∫e dt + Kd·de/dt with anti-windup, so the tuned integral and derivative gains are the ones actually applied during the rollout (not merely suggested and discarded).

Optuna is an optional dependency; install the tuning extra (pip install scpn-control[tuning]) to enable optimisation. When it is absent the tuners log a warning and return conservative default gains.

tune_pid

tune_pid(env, n_trials=50, dt=None)

Tune parallel-form PID gains (Kp, Ki, Kd) with Optuna.

Every trial evaluates a full proportional-integral-derivative rollout — the suggested integral and derivative gains are applied, not just the proportional term — minimising the mean integral-of-absolute-error across :data:_TUNE_EPISODES episodes.

Parameters

env : Any Gymnasium tokamak control environment; observation element 0 is the tracking error. n_trials : int, optional Number of Optuna trials. dt : float or None, optional Control period in seconds. When None the environment's dt is used if present, otherwise :data:_DEFAULT_DT.

Returns

dict[str, float] Optimal {"Kp", "Ki", "Kd"} gains. When Optuna is unavailable a warning is logged and conservative default gains are returned.

tune_hinf

tune_hinf(plant, n_trials=50)

Tune H-infinity parameters (gamma, bandwidth) using Optuna.

Density Controller

density_controller

Density-control plant, estimator, fuelling actuators, and optimisation utilities.

ParticleTransportModel

ParticleTransportModel(n_rho=50, R0=6.2, a=2.0)

Radial particle-transport plant for the density-control loop.

Integrates the flux-surface-averaged continuity equation ∂_t n = -V'^{-1} ∂_ρ (V' Γ) + S with anomalous flux Γ = -D ∂_r n + V_pinch n on a uniform rho = r/a grid, using an explicit forward-Euler step clamped to the diffusion CFL limit. Source and sink terms (gas puff, pellet, NBI, recycling, cryopump) are supplied by the dedicated methods. A circular cross-section sets the volume elements.

Parameters

n_rho Number of radial grid points; must be at least 2. R0 Major radius in metres; must be finite and positive. a Minor radius in metres; must be finite and positive.

set_transport

set_transport(D, V_pinch)

Override the diffusivity and pinch-velocity radial profiles.

Parameters

D Particle diffusivity in m²/s, shape (n_rho,); finite and non-negative. V_pinch Pinch velocity in m/s, shape (n_rho,) (negative is inward); finite.

Raises

ValueError If a profile has the wrong shape, is non-finite, or D is negative.

gas_puff_source

gas_puff_source(rate, penetration_depth=0.03)

Particles/s. Edge-localised source from gas injection.

Pacher et al. 2007, Nucl. Fusion 47, 469: ITER gas-injection modelling places the effective source within ~3% of the minor radius from the wall.

pellet_source

pellet_source(speed_ms, radius_mm, launch_angle_deg=0.0, *, ne_profile=None, Te_eV_profile=None, B0_T=5.3, injection_side='HFS')

NGS trajectory deposition profile from a single pellet.

The deposition path is delegated to :class:PelletTrajectory, which integrates Parks-Turnbull neutral-gas-shielding ablation along the radial pellet trajectory and applies the repository drift correction.

nbi_source

nbi_source(beam_energy_keV, power_MW)

Core-peaked particle source from neutral beam injection.

cryopump_sink

cryopump_sink(pump_speed, ne_edge)

Edge particle removal from cryopump.

recycling_source

recycling_source(outflux, recycling_coeff=0.97)

Recycling_coeff = 0.97 is the standard ITER assumption for a metal wall.

ITER Physics Basis 1999, Nucl. Fusion 39, 2175, §4.2.

step

step(ne, sources, dt)

Advance the density profile by one CFL-limited diffusion step.

Parameters

ne Current electron-density profile in m⁻³, shape (n_rho,). sources Net particle source profile in m⁻³ s⁻¹, shape (n_rho,). dt Requested time step in seconds; clamped to the explicit-diffusion CFL limit (drho a)² / (2 D_max) when larger.

Returns

FloatArray Updated density profile in m⁻³, floored at 1e16 m⁻³, shape (n_rho,).

Raises

ValueError If profiles have the wrong shape or are negative, or dt is not finite and positive.

ActuatorCommand dataclass

ActuatorCommand(gas_puff_rate, pellet_freq, pellet_speed, cryo_pump_speed)

Fuelling and pumping set-points emitted by the density controller.

Attributes

gas_puff_rate Gas-puff particle rate in particles/s. pellet_freq Pellet injection frequency in Hz. pellet_speed Pellet velocity in m/s. cryo_pump_speed Cryopump pumping speed in facility units.

DensityControlClaimEvidence dataclass

DensityControlClaimEvidence(schema_version, source, source_id, geometry_source, transport_source, actuator_source, diagnostic_source, model_id, n_rho, R0_m, a_m, dt_requested_s, dt_cfl_s, cfl_limited, greenwald_fraction, below_iter_greenwald_margin, gas_puff_rate, pellet_frequency, pellet_speed, cryopump_speed, total_source_particles_per_s, particle_inventory_delta, greenwald_fraction_abs_error, inventory_delta_relative_error, greenwald_fraction_abs_tolerance, inventory_delta_relative_tolerance, facility_density_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for density-control claims.

DensityController

DensityController(model, dt_control=0.001)

PI density controller with Greenwald limit enforcement.

Greenwald limit: n_GW = I_p / (π a²) [10^20 m^-3] Greenwald 2002, PPCF 44, R27, Eq. 1.

ITER operational margin: n/n_GW < 0.85. ITER Physics Basis 1999, Nucl. Fusion 39, 2175, §2.3.

set_target

set_target(ne_target)

Set the target electron-density profile.

Parameters

ne_target Desired density profile in m⁻³, shape (n_rho,); finite and non-negative.

set_constraints

set_constraints(n_GW, gas_max, pellet_freq_max, pump_max)

Set the Greenwald density limit and actuator saturation bounds.

Parameters

n_GW Greenwald density limit in m⁻³; must be positive. gas_max Maximum gas-puff rate in particles/s; must be non-negative. pellet_freq_max Maximum pellet frequency in Hz; must be non-negative. pump_max Maximum cryopump speed; must be non-negative.

compute_greenwald_limit staticmethod

compute_greenwald_limit(I_p_MA, a_m)

n_GW = I_p / (π a²) [10^20 m^-3], converted to [m^-3].

Greenwald 2002, PPCF 44, R27, Eq. 1.

greenwald_fraction

greenwald_fraction(ne, I_p_MA, a)

Volume-averaged n / n_GW.

Greenwald 2002, PPCF 44, R27, Eq. 1. ITER safe operating limit: fraction < 0.85. ITER Physics Basis 1999, Nucl. Fusion 39, 2175, §2.3.

below_greenwald_safety_margin

below_greenwald_safety_margin(ne)

Return True if the volume-averaged density is within the ITER safety margin.

ITER Physics Basis 1999, Nucl. Fusion 39, 2175, §2.3: n/n_GW < 0.85.

step

step(ne_measured)

Compute one PI control action with Greenwald-limit enforcement.

The volume-integrated particle error drives a PI command; gas puffing and pellets fuel a deficit while the cryopump removes excess. Above the hard Greenwald fraction (0.95) the pump saturates regardless of the PI term.

Parameters

ne_measured Measured electron-density profile in m⁻³, shape (n_rho,).

Returns

ActuatorCommand Fuelling and pumping set-points for this control cycle.

KalmanDensityEstimator

KalmanDensityEstimator(n_rho, n_chords=8)

Linear Kalman filter reconstructing the density profile from interferometry.

The state is the radial density profile; the measurement model is the Abel-transform line integral along n_chords interferometer chords. The initial state and the process/measurement covariances are scaled to m⁻³ magnitudes.

Parameters

n_rho Number of radial state grid points. n_chords Number of interferometer chords (measurements).

measurement_matrix

measurement_matrix(chord_angles)

Abel-transform projection matrix for interferometry chords.

predict

predict(ne, dt)

Propagate the state and inflate its covariance by process noise.

Parameters

ne Predicted density profile in m⁻³, shape (n_rho,), used as the propagated state mean. dt Time step in seconds scaling the process-noise increment.

Returns

AnyFloatArray The predicted state (density profile) in m⁻³, shape (n_rho,).

update

update(ne_pred, measurements, chord_angles)

Correct the predicted state with chord measurements (Kalman update).

Parameters

ne_pred Predicted density profile in m⁻³, shape (n_rho,). measurements Line-integrated chord measurements, shape (n_chords,). chord_angles Chord impact geometry passed to :meth:measurement_matrix.

Returns

AnyFloatArray Posterior density estimate in m⁻³, shape (n_rho,).

PelletSchedule dataclass

PelletSchedule(times, speeds, sizes)

Timed pellet-injection sequence.

Attributes

times Injection times in seconds. speeds Pellet velocities in m/s, one per injection. sizes Pellet radii in mm, one per injection.

FuelingOptimizer

Plan evenly-spaced pellet-injection sequences toward a target density.

optimize_pellet_sequence

optimize_pellet_sequence(ne_current, ne_target, n_pellets, time_horizon)

Evenly-spaced pellet schedule over the given horizon.

density_control_claim_evidence

density_control_claim_evidence(model, controller, *, source, source_id, geometry_source, transport_source, actuator_source, diagnostic_source, ne_before, ne_after, sources, command, dt_requested_s, model_id='bounded_density_control', reference_greenwald_fraction=None, reference_inventory_delta=None, greenwald_fraction_abs_tolerance=0.02, inventory_delta_relative_tolerance=0.05)

Build fail-closed density-control evidence for bounded or facility claims.

assert_density_control_facility_claim_admissible

assert_density_control_facility_claim_admissible(evidence)

Raise when density-control evidence is insufficient for a facility claim.

save_density_control_claim_evidence

save_density_control_claim_evidence(evidence, path)

Persist density-control claim evidence as deterministic JSON.

DensityControlClaimEvidence dataclass

DensityControlClaimEvidence(schema_version, source, source_id, geometry_source, transport_source, actuator_source, diagnostic_source, model_id, n_rho, R0_m, a_m, dt_requested_s, dt_cfl_s, cfl_limited, greenwald_fraction, below_iter_greenwald_margin, gas_puff_rate, pellet_frequency, pellet_speed, cryopump_speed, total_source_particles_per_s, particle_inventory_delta, greenwald_fraction_abs_error, inventory_delta_relative_error, greenwald_fraction_abs_tolerance, inventory_delta_relative_tolerance, facility_density_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for density-control claims.

density_control_claim_evidence

density_control_claim_evidence(model, controller, *, source, source_id, geometry_source, transport_source, actuator_source, diagnostic_source, ne_before, ne_after, sources, command, dt_requested_s, model_id='bounded_density_control', reference_greenwald_fraction=None, reference_inventory_delta=None, greenwald_fraction_abs_tolerance=0.02, inventory_delta_relative_tolerance=0.05)

Build fail-closed density-control evidence for bounded or facility claims.

assert_density_control_facility_claim_admissible

assert_density_control_facility_claim_admissible(evidence)

Raise when density-control evidence is insufficient for a facility claim.

save_density_control_claim_evidence

save_density_control_claim_evidence(evidence, path)

Persist density-control claim evidence as deterministic JSON.

Detachment Controller

detachment_controller

Divertor detachment state estimation and impurity-seeding control utilities.

DetachmentState

Bases: Enum

Divertor detachment regime classification.

RadiationFrontModel

RadiationFrontModel(impurity, R0, a, q95)

Impurity radiation-front position and detachment-degree model.

Parameters

impurity Seeding impurity species symbol. R0 Major radius in metres; must be positive. a Minor radius in metres; must be positive and below R0. q95 Edge safety factor q95; must be positive.

radiation_temperature

radiation_temperature(impurity)

Peak radiation temperature [eV] for each impurity species.

N₂: ~10 eV, Ne: ~30 eV, Ar: ~100 eV. Kallenbach et al. 2015, Nucl. Fusion 55, 053026, §3.

front_position

front_position(P_SOL_MW, n_u_19, seeding_rate)

Radiation front location: 0 = target, 1 = X-point.

Higher seeding rate moves the front toward the X-point. Higher P_SOL pushes it back toward the target. Lipschultz et al. 1999, PPCF 41, A585: empirical front-position scaling.

degree_of_detachment

degree_of_detachment(T_target_eV, n_target, n_u)

DOD = Γ_t,attached / Γ_t,actual via T_target rollover.

DOD = 1 when attached (T_t > 5 eV). Below the onset (Stangeby 2000, Ch. 16) DOD rises as T_t falls, reflecting reduced ion flux from volumetric recombination.

DetachmentController

DetachmentController(impurity='N2', target_DOD=3.0, target_T_t_eV=3.0)

PI controller driving divertor detachment via impurity seeding.

Target: T_div ≈ 3 eV (below the 5 eV onset, above the MARFE threshold). Stangeby 2000, Ch. 16: T_div < 5 eV criterion for detachment onset. Lipschultz et al. 1999, PPCF 41, A585: thermal bifurcation stability.

Nitrogen seeding rates up to 10^22 molecules/s for ITER. Kallenbach et al. 2015, Nucl. Fusion 55, 053026.

step

step(T_t_measured, n_t_measured, P_rad_measured, rho_front, dt)

Compute the impurity seeding-rate command for one control cycle.

PI control on the target-temperature error, with a hard seeding cutback when the radiation front reaches the X-point (MARFE risk).

Parameters

T_t_measured Measured divertor target temperature in eV; must be positive. n_t_measured Measured target density in 10¹⁹ m⁻³; must be positive. P_rad_measured Measured radiated power in MW; must be non-negative. rho_front Radiation-front position in [0, 1] (0 = target, 1 = X-point). dt Control time step in seconds; must be positive.

Returns

float The commanded impurity seeding rate (non-negative).

DetachmentPoint dataclass

DetachmentPoint(seeding_rate, T_target, n_target, DOD, P_rad_frac, state)

One steady-state point on the detachment bifurcation scan.

Attributes

seeding_rate Impurity seeding rate at this point. T_target Divertor target temperature in eV. n_target Divertor target density in 10¹⁹ m⁻³. DOD Degree of detachment. P_rad_frac Radiated power fraction. state The detachment regime at this point.

DetachmentBifurcation

DetachmentBifurcation(sol, impurity)

Steady-state scan across seeding rates to locate the thermal bifurcation.

Thermal bifurcation (S-curve) in T_target vs seeding_rate described by: Lipschultz et al. 1999, PPCF 41, A585 (Alcator C-Mod data + model). Two-point model (Stangeby 2000, Eq. 5.69) provides the upstream–target link.

scan_seeding

scan_seeding(seeding_range, P_SOL_MW, n_u_19)

Scan steady-state detachment across a range of seeding rates.

Parameters

seeding_range Strictly increasing non-negative seeding rates. P_SOL_MW Scrape-off-layer power in MW; must be positive. n_u_19 Upstream density in 10¹⁹ m⁻³; must be positive.

Returns

list[DetachmentPoint] One steady-state detachment point per seeding rate.

find_rollover_point

find_rollover_point(P_SOL_MW, n_u_19)

Seeding rate where ion flux Γ ~ n_t √T_t peaks (flux rollover).

Rollover marks the detachment onset on the bifurcation S-curve. Stangeby 2000, Ch. 16; Lipschultz et al. 1999, PPCF 41, A585.

MultiImpuritySeeding

MultiImpuritySeeding(impurities, controllers)

Coordinator running one detachment controller per impurity species.

Parameters

impurities Impurity species symbols to seed. controllers Per-impurity detachment controllers keyed by species symbol.

step

step(diagnostics, dt)

Compute per-impurity seeding rates from a diagnostics frame.

Parameters

diagnostics Diagnostic values (T_target_eV, n_target_19, P_rad_MW, rho_front); defaults are used for absent keys. dt Control time step in seconds; must be positive.

Returns

dict[str, float] Seeding rate per impurity species (0.0 for species without a controller).

two_point_q_parallel

two_point_q_parallel(T_upstream_eV, L_parallel_m)

Parallel heat flux from the Spitzer conduction model.

q_∥ = κ₀ T_u^(7/2) / (7 L_∥) [W m^-2]

Stangeby 2000, "The Plasma Boundary of Magnetic Fusion Devices", Eq. 5.69. κ₀ = 2390 W m^-1 eV^(-7/2) (electron Spitzer conductivity, Stangeby Eq. 5.67).

Federated Disruption

federated_disruption

Federated learning framework for cross-machine disruption prediction.

Trains a shared MLP disruption classifier across heterogeneous tokamak datasets (DIII-D, JET, KSTAR, EAST, SPARC) without centralising raw data. Supports FedAvg (McMahan et al., AISTATS 2017) and FedProx (Li et al., MLSys 2020) aggregation strategies.

Disruption features: Ip, beta_N, q95, n/n_GW, li, dBp/dt, locked_mode_amplitude, n1_rms — 8-dimensional input space whose distributions differ across machines (JET: higher Ip; DIII-D: more shaping; KSTAR: longer pulses).

DifferentialPrivacyConfig dataclass

DifferentialPrivacyConfig(max_update_norm=1.0, noise_multiplier=1.0, delta=1e-05, seed=20240531)

Facility-level differential privacy for federated client updates.

PrivacyLedgerEntry dataclass

PrivacyLedgerEntry(round_index, participating_clients, epsilon_spent, cumulative_epsilon, delta, max_update_norm, noise_multiplier, clipped_clients)

Per-round facility-level privacy accounting record.

FacilityBenchmarkSummary dataclass

FacilityBenchmarkSummary(aggregation, machines, n_rounds, mean_accuracy, mean_loss, per_machine_accuracy, privacy_epsilon, privacy_delta, evidence_kind)

Deterministic benchmark summary for a federated disruption campaign.

FederatedConfig dataclass

FederatedConfig(n_rounds=10, local_epochs=5, learning_rate=0.01, aggregation='fedavg', mu_proximal=0.01, min_clients=2, machines=(lambda: ['DIII-D', 'JET', 'KSTAR'])(), dp_config=None)

Configuration for federated disruption prediction training.

MachineClient

MachineClient(machine, X_train, y_train, X_test, y_test, learning_rate=0.01)

Local training client for a single tokamak.

get_data_size

get_data_size()

Return the number of local training samples held by this federated client.

local_train

local_train(global_weights, n_epochs, mu_proximal=0.0)

SGD on local data, starting from global_weights.

When mu_proximal > 0, adds the FedProx penalty (mu/2)||w - w_global||^2 to the loss gradient.

local_evaluate

local_evaluate(weights)

Binary classification metrics on local test set.

FederatedServer

FederatedServer(config, seed=42)

Orchestrates federated training across machine clients.

aggregate

aggregate(client_updates)

FedAvg: weighted average of model weights by dataset size.

McMahan et al., "Communication-Efficient Learning of Deep Networks from Decentralized Data", AISTATS 2017.

fedprox_aggregate

fedprox_aggregate(client_updates, global_weights, mu)

FedProx aggregation with proximal regularisation.

The proximal term is applied during local training (not aggregation), so aggregation itself is weighted averaging — the difference from FedAvg is in the client-side gradient update. This method exists for API symmetry; the mu parameter documents the proximal weight used.

run_round

run_round(clients)

Single federated round: distribute → local train → aggregate.

train

train(clients, n_rounds=None)

Full federated training loop.

Returns per-round metrics including per-client accuracy, loss, n_samples.

privacy_summary

privacy_summary()

Return cumulative facility-level DP spend for the current server.

get_state

get_state()

Return a serialisable snapshot of server state.

from_state classmethod

from_state(state)

Reconstruct server from serialised state.

gaussian_mechanism_epsilon

gaussian_mechanism_epsilon(noise_multiplier, delta)

Return the conservative Gaussian-mechanism epsilon for one round.

compose_privacy_epsilon

compose_privacy_epsilon(noise_multiplier, delta, n_rounds)

Linearly compose the conservative per-round Gaussian epsilon budget.

differential_privacy_clip

differential_privacy_clip(gradients, max_norm, noise_sigma, rng=None)

Clip per-parameter gradient norms and add Gaussian noise (DP-SGD).

Reference: Abadi et al., "Deep Learning with Differential Privacy", CCS 2016.

create_machine_clients

create_machine_clients(machine_configs, seed=42)

Create MachineClient instances with synthetic disruption data.

Parameters

machine_configs : list of dicts Each dict must have "machine" (str) and optionally "n_train" (int, default 200), "n_test" (int, default 50), "disruption_fraction" (float, default 0.4), "learning_rate" (float, default 0.01). seed : int Base RNG seed; each machine gets seed + index.

create_facility_clients_from_arrays

create_facility_clients_from_arrays(datasets, *, learning_rate=0.01)

Create clients from per-facility arrays without centralising raw shots.

Each facility payload must contain X_train, y_train, X_test, and y_test. The constructor enforces the shared 8-feature disruption contract and binary label boundary before the data can enter a federation.

run_synthetic_multifacility_benchmark

run_synthetic_multifacility_benchmark(*, machines=('DIII-D', 'JET', 'KSTAR', 'EAST'), n_rounds=4, local_epochs=3, aggregation='fedprox', dp_config=None, seed=20240531)

Run a deterministic synthetic multi-facility disruption benchmark.

This benchmark exercises the production federation, heterogeneity, and privacy-accounting contracts. It is not measured cross-facility validation.

State Estimator

state_estimator

Extended Kalman Filter (EKF) for plasma state estimation.

EKF predict-update cycle

x_{k+1|k} = f(x_k, u_k) P_{k+1|k} = F P_k Fᵀ + Q y_k = z_k − H x_{k|k−1} S_k = H P_{k|k−1} Hᵀ + R K_k = P_{k|k−1} Hᵀ S_k⁻¹ x_{k|k} = x_{k|k−1} + K_k y_k P_{k|k} = (I − K_k H) P_{k|k−1}

Simon 2006, "Optimal State Estimation", Ch. 13.

Magnetic diagnostic-based tokamak state estimation: Lister et al. 1997, Nucl. Fusion 37, 1633 — real-time equilibrium reconstruction from magnetic measurements on TCV.

Observer-based current profile estimation: Moreau et al. 2008, Nucl. Fusion 48, 106001 — model-based estimation of the safety-factor profile q(ρ) from partial magnetic and kinetic data.

ExtendedKalmanFilter

ExtendedKalmanFilter(x0, P0, Q, R_cov)

EKF for 6D plasma state estimation.

State vector: [R, Z, vR, vZ, Ip, Te_core] Measurement vector: [R, Z, Ip, Te_core]

Process model: constant-velocity for (R, Z); Ip and Te_core modelled as random walks (zero-input prediction). The linearization is exact because the process model is affine, so this degenerates to a standard Kalman filter for the current implementation.

Lister et al. 1997, Nucl. Fusion 37, 1633: magnetic flux and field measurements provide [R, Z, Ip] observability in real time. Moreau et al. 2008, Nucl. Fusion 48, 106001: Te_core estimated via ECE + model observer.

Parameters

x0 : AnyFloatArray Initial state estimate (6D). P0 : AnyFloatArray Initial error covariance (6×6). Q : AnyFloatArray Process noise covariance (6×6). R_cov : AnyFloatArray Measurement noise covariance (4×4).

predict

predict(dt, u=None)

Advance state estimate and covariance.

x_{k+1|k} = F x_k (constant-velocity kinematics + random walk) P_{k+1|k} = F P_k Fᵀ + Q dt

Simon 2006, Ch. 13, Eqs. (13.7)–(13.8).

Parameters

dt : float Time step [s]. u : AnyFloatArray, optional Control input (unused in constant-velocity model).

Returns

AnyFloatArray — Predicted state.

update

update(z)

Correct state estimate with a new measurement.

y = z − H x_{k|k−1} S = H P_{k|k−1} Hᵀ + R K = P_{k|k−1} Hᵀ S⁻¹ x = x_{k|k−1} + K y P = (I − K H) P_{k|k−1}

Simon 2006, Ch. 13, Eqs. (13.9)–(13.13).

Parameters

z : AnyFloatArray Measured values [R, Z, Ip, Te_core] (4D).

Returns

AnyFloatArray — Updated state.

estimate

estimate()

Return the current state estimate.

Volt Second Manager

volt_second_manager

Volt-second flux budgeting, consumption monitoring, and scenario feasibility utilities.

FluxStatus dataclass

FluxStatus(flux_consumed_Vs, flux_remaining_Vs, estimated_remaining_time_s, fraction_consumed)

Instantaneous volt-second consumption status.

Attributes

flux_consumed_Vs Volt-seconds consumed so far. flux_remaining_Vs Volt-seconds remaining in the budget. estimated_remaining_time_s Estimated time to budget exhaustion at the current loop voltage, in seconds. fraction_consumed Fraction of the total budget consumed.

FluxReport dataclass

FluxReport(ramp_flux, flat_top_flux, ramp_down_flux, total_flux, within_budget, margin_Vs)

Per-phase decomposition of scenario volt-second consumption.

Attributes

ramp_flux Volt-seconds consumed during the current ramp-up. flat_top_flux Volt-seconds consumed during flat-top. ramp_down_flux Volt-seconds consumed (net) during ramp-down. total_flux Total volt-seconds consumed across the scenario. within_budget True if the total stays within the available budget. margin_Vs Volt-second margin (budget minus total); negative if over budget.

VoltSecondClaimEvidence dataclass

VoltSecondClaimEvidence(schema_version, source, source_id, model_id, Phi_CS_Vs, L_plasma_H, R_plasma_Ohm, Ip_MA, I_bs_MA, ramp_duration_s, flat_duration_s, ramp_down_duration_s, ramp_flux_Vs, flat_top_flux_Vs, ramp_down_flux_Vs, total_flux_Vs, margin_Vs, within_budget, ejima_startup_flux_Vs, max_flattop_duration_s, bootstrap_source, reference_source, reference_dataset_id, reference_artifact_sha256, reference_case_count, total_flux_relative_error, flat_top_duration_relative_error, ejima_flux_relative_error, bootstrap_current_abs_error_MA, margin_abs_error_Vs, total_flux_relative_tolerance, flat_top_duration_relative_tolerance, ejima_flux_relative_tolerance, bootstrap_current_abs_tolerance_MA, margin_abs_tolerance_Vs, facility_claim_allowed, claim_status)

Serialisable evidence for bounded or facility volt-second claims.

FluxBudget

FluxBudget(Phi_CS_Vs, L_plasma_uH, R_plasma_uOhm)

Volt-second budget tracker.

Implements the balance

∫ V_loop dt = L_p dI_p + R_p I_p dt

Reference: Wesson 2011, Tokamaks 4th ed., Eq. 3.7.4.

inductive_flux

inductive_flux(Ip_MA)

L_p · I_p — inductive volt-second consumption.

Wesson 2011, Tokamaks 4th ed., Eq. 3.7.4 (first term).

resistive_flux_ramp

resistive_flux_ramp(Ip_trace, dt)

∫ R_p I_p dt — resistive volt-second consumption during ramp.

Wesson 2011, Tokamaks 4th ed., Eq. 3.7.4 (second term).

ejima_startup_flux

ejima_startup_flux(R0_m, Ip_MA)

Startup flux via Ejima coefficient: ΔΨ = C_Ejima · μ₀ · R₀ · I_p.

Ejima et al. 1982, Nucl. Fusion 22, 1313, Eq. 2. C_EJIMA = 0.4 is the ITER design value.

remaining_flux

remaining_flux(Ip_MA, ramp_flux)

Volt-seconds left after inductive and ramp consumption.

Parameters

Ip_MA Plasma current in MA. ramp_flux Volt-seconds already consumed during ramp-up; must be non-negative.

Returns

float Remaining volt-seconds, floored at zero.

max_flattop_duration

max_flattop_duration(Ip_MA, I_bs_MA, ramp_flux)

τ_flat = (Ψ_avail − Ψ_startup) / (R_p I_p).

ITER Physics Basis 1999, Nucl. Fusion 39, 2137, §3.

VoltSecondOptimizer

VoltSecondOptimizer(flux_budget, transport_model=None)

Current-ramp planner that respects the volt-second budget.

Parameters

flux_budget The available flux budget. transport_model Optional transport model used to refine the ramp; the linear default ramp does not use it.

optimize_ramp

optimize_ramp(Ip_target_MA, t_ramp_max, n_segments=10)

Plan a current ramp to the target over the allowed ramp time.

Parameters

Ip_target_MA Target plasma current in MA; must be non-negative. t_ramp_max Maximum ramp duration in seconds; must be positive. n_segments Number of ramp samples; must be at least 2.

Returns

NDArray[np.float64] The plasma-current trace in MA, shape (n_segments,).

BootstrapCurrentEstimate

Bootstrap-current proxy from pressure-gradient profiles.

from_profiles staticmethod

from_profiles(ne, Te, Ti, q, rho, R0, a)

Simplified bootstrap current proxy: I_bs ~ ε^{1/2} · ∫ dp/dr dr.

Full neoclassical expression in Wesson 2011, Ch. 4.9. ε = a/R₀ is the inverse aspect ratio.

FluxConsumptionMonitor

FluxConsumptionMonitor(flux_budget)

Online volt-second consumption tracker integrating the loop voltage.

Parameters

flux_budget The available flux budget against which consumption is tracked.

step

step(Ip, V_loop, dt)

Integrate V_loop dt to track consumed volt-seconds.

Wesson 2011, Tokamaks 4th ed., Eq. 3.7.4 — V_loop drives both inductive and resistive flux consumption.

ScenarioFluxAnalysis

ScenarioFluxAnalysis(flux_budget)

Per-phase volt-second budget analysis of a full scenario.

Parameters

flux_budget The available flux budget.

analyze

analyze(ramp_dur, flat_dur, down_dur, Ip_MA, I_bs_MA)

Decompose total flux consumption into ramp / flat-top / ramp-down.

Ramp: L_p I_p (inductive) + R_p · 0.5 I_p · t_ramp (resistive at mean current). Flat-top: R_p · (I_p − I_bs) · t_flat. Ramp-down: resistive loss minus partial inductive recovery. Reference: ITER Physics Basis 1999, Nucl. Fusion 39, 2137, §3.

volt_second_claim_evidence

volt_second_claim_evidence(budget, report, *, Ip_MA, I_bs_MA, ramp_duration_s, flat_duration_s, ramp_down_duration_s, R0_m, ramp_flux_for_flattop_Vs, source, source_id, bootstrap_source='repository bootstrap-current proxy', model_id='bounded_volt_second_manager', reference_artifact=None, total_flux_relative_tolerance=0.03, flat_top_duration_relative_tolerance=0.05, ejima_flux_relative_tolerance=0.05, bootstrap_current_abs_tolerance_MA=0.25, margin_abs_tolerance_Vs=0.5)

Build fail-closed evidence for scenario volt-second claims.

assert_volt_second_facility_claim_admissible

assert_volt_second_facility_claim_admissible(evidence)

Raise when volt-second evidence is insufficient for facility scenario claims.

save_volt_second_claim_evidence

save_volt_second_claim_evidence(evidence, path)

Persist volt-second claim evidence as deterministic JSON.

Core Support and Physics Modules

Rust Compatibility

_rust_compat

Backward compatibility layer: imports from Rust (scpn_control_rs) if available, falls back to pure-Python implementations.

Usage

from scpn_control.core._rust_compat import FusionKernel, RUST_BACKEND

RustAcceleratedKernel

RustAcceleratedKernel(config_path)

Drop-in wrapper around Rust PyFusionKernel mirroring the Python FusionKernel attribute interface.

Mirrors .Psi, .R, .Z, .RR, .ZZ, .cfg, etc. Delegates equilibrium solve to Rust for ~20x speedup while keeping all attribute accesses compatible with downstream code.

solve_equilibrium

solve_equilibrium()

Solve Grad-Shafranov equilibrium via Rust backend.

sample_psi_at

sample_psi_at(r, z)

Interpolate psi at a single (R, Z) point via Rust bilinear interpolation.

sample_psi_at_probes

sample_psi_at_probes(probes)

Interpolate psi at multiple (R, Z) probe positions.

Parameters

probes : list of (float, float) Probe positions as (R, Z) pairs.

Returns

ndarray, shape (len(probes),)

compute_b_field

compute_b_field()

Compute (B_R, B_Z) from current psi, delegating to Rust when available.

find_x_point

find_x_point(Psi)

Locate the null point (B=0) using local minimization.

Matches Python FusionKernel.find_x_point() interface.

calculate_vacuum_field

calculate_vacuum_field()

Compute vacuum field via Python FusionKernel (not yet in PyO3).

set_solver_method

set_solver_method(method)

Set inner linear solver: 'sor' or 'multigrid'.

solver_method

solver_method()

Get current solver method name.

calculate_thermodynamics

calculate_thermodynamics(p_aux_mw=50.0)

D-T fusion thermodynamics from current equilibrium (Rust backend).

save_results

save_results(filename='equilibrium_nonlinear.npz')

Save current state to .npz file.

RustSnnPool

RustSnnPool(n_neurons=50, gain=10.0, window_size=20)

Python wrapper for Rust SpikingControllerPool (LIF neuron population).

Falls back with ImportError if the Rust extension is not compiled.

Parameters

n_neurons : int Number of LIF neurons per sub-population (positive/negative). gain : float Output scaling factor. window_size : int Sliding window length for rate-code averaging.

step

step(error)

Process error through SNN pool and return scalar control output.

RustSnnController

RustSnnController(target_r=6.2, target_z=0.0)

Python wrapper for Rust NeuroCyberneticController (dual R+Z SNN pools).

Falls back with ImportError if the Rust extension is not compiled.

Parameters

target_r : float Target major-radius position [m]. target_z : float Target vertical position [m].

step

step(measured_r, measured_z)

Process measured (R, Z) position and return (ctrl_R, ctrl_Z).

RustPIDController

RustPIDController(kp, ki, kd)

Rust PID controller (kp, ki, kd gains with finite-input validation).

Parameters

kp, ki, kd : float Proportional / integral / derivative gains.

RustIsoFluxController

RustIsoFluxController(target_r, target_z)

Rust iso-flux controller (decoupled R + Z PID).

Parameters

target_r, target_z : float Target R, Z position [m].

RustHInfController

RustHInfController(gamma_growth=100.0, damping=10.0, gamma=1.0, u_max=10.0, dt=0.001)

Rust H-infinity observer-based controller for vertical stability.

Uses LQR-approximated gains for the 2-state VDE plant (full DARE solver pending ndarray-linalg).

Parameters

gamma_growth : float Unstable growth rate [1/s]. damping : float Passive damping coefficient. gamma : float H-infinity performance level. u_max : float Actuator saturation limit [A]. dt : float Nominal timestep [s].

step

step(y, dt)

Measurement y → control u (observer-based, saturation-limited).

RustSPIMitigation

RustSPIMitigation(w_th_mj=300.0, ip_ma=15.0, te_kev=20.0)

Simulate SPI disruption mitigation.

Uses the Rust backend when available, falling back to a pure-Python implementation matching the Rust constants.

Parameters

w_th_mj : float Initial stored thermal energy [MJ]. ip_ma : float Initial plasma current [MA]. te_kev : float Initial electron temperature [keV].

run

run()

Run full SPI simulation and return snapshot history.

rust_simulate_tearing_mode

rust_simulate_tearing_mode(steps, seed=None)

Python tearing-mode simulation fallback.

rust_bosch_hale_dt

rust_bosch_hale_dt(t_kev)

Bosch-Hale D-T reaction rate [m³/s] at temperature t_kev [keV].

rust_multigrid_vcycle

rust_multigrid_vcycle(source, psi_bc, r_min, r_max, z_min, z_max, nr, nz, tol=1e-06, max_cycles=500)

Solve the multigrid V-cycle GS problem.

Uses the Rust backend when available, falling back to FusionKernel's Python multigrid.

Returns

tuple of (psi, residual, n_cycles, converged)

rust_svd_optimal_correction

rust_svd_optimal_correction(response_matrix, error, gain=0.8)

Compute the SVD-based coil-current correction.

Uses the Rust backend when available, falling back to the NumPy SVD pseudoinverse.

Parameters

response_matrix : ndarray, shape (m, n) Plant response Jacobian (typically 2 x n_coils). error : ndarray, shape (m,) Position error vector [R_err, Z_err]. gain : float Correction gain factor.

Returns

ndarray, shape (n,) Coil current deltas.

Statistics Helpers

_statistics

Statistical helpers for runtime paths that avoid fragile optional reductions.

linear_percentile

linear_percentile(values, percentile)

Return the linearly interpolated percentile of a finite sample.

Parameters

values Non-empty finite scalar sample. percentile Percentile in the inclusive range [0, 100].

Returns

float The linearly interpolated percentile, matching NumPy's default percentile interpolation for one-dimensional samples.

Raises

ValueError If the sample is empty, contains a non-finite value, or the percentile lies outside [0, 100].

Validators

_validators

Shared input validators for public API boundaries.

require_bounded_float

require_bounded_float(name, value, *, low=-inf, high=inf, low_exclusive=False, high_exclusive=False)

Validate scalar is finite and within [low, high] (or exclusive bounds).

Subsumes require_positive_float, require_non_negative_float, require_fraction for arbitrary bound combinations.

require_finite_array

require_finite_array(name, value, *, ndim=None, shape=None)

Validate array is finite with optional shape/ndim constraint.

Alfven Eigenmodes

alfven_eigenmodes

Alfven-eigenmode gap, drive, damping, and stability-screening utilities.

AlfvenGap dataclass

AlfvenGap(rho_location, omega_lower, omega_upper, m_coupling)

A toroidicity-induced gap in the shear Alfvén continuum.

Attributes

rho_location Normalised radius of the gap. omega_lower Lower gap frequency in rad/s. omega_upper Upper gap frequency in rad/s. m_coupling Lower poloidal mode number of the coupled pair.

AlfvenContinuum

AlfvenContinuum(rho, q, ne, B0, R0, m_i_amu=2.5, a=2.0)

Shear Alfvén continuum and TAE gap structure.

Cheng & Chance 1986, Phys. Fluids 29, 3695 — gap condition q = (2m+1)/(2n).

alfven_speed

alfven_speed(rho_eval)

Alfvén speed interpolated at a normalised radius.

Parameters

rho_eval Normalised radius within the continuum grid.

Returns

float The local Alfvén speed in m/s.

continuum

continuum(m, n)

ω_A(ρ) = |n q − m| / (q R₀) · v_A.

Cheng & Chance 1986, Eq. 2.

find_gaps

find_gaps(n)

TAE gap midpoint at q = (2m+1)/(2n); width ≃ ε·ω₀.

Cheng & Chance 1986, Eq. 3.6.

TAEMode

TAEMode(n, q_rational, v_A, R0, T_e_keV=None, m_coupling=1)

Toroidal Alfvén eigenmode frequency and damping.

Cheng & Chance 1986, Phys. Fluids 29, 3695.

frequency

frequency()

ω_TAE = v_A / (2 q R₀) — Cheng & Chance 1986, Eq. 3.5.

frequency_kHz

frequency_kHz()

TAE frequency in kilohertz (frequency() / 2π in kHz).

electron_landau_damping

electron_landau_damping()

Electron Landau damping rate for a TAE.

γ_e = −(π/4)·(ω / |k_∥ v_the|) · ω · exp(−(ω / k_∥ v_the)²)

where v_the = sqrt(2 T_e / m_e), T_e in SI k_∥ = (n − m/q) / (q R₀) (parallel wave vector)

Returns |γ_e| (positive damping magnitude).

Rosenbluth & Rutherford 1975, Phys. Rev. Lett. 34, 1428, Eq. 9.

FastParticleDrive

FastParticleDrive(E_fast_keV, n_fast_frac, m_fast_amu=4.0)

Fast-particle (alpha/beam-ion) drive for Alfvén eigenmodes.

Fu & Van Dam 1989, Phys. Fluids B 1, 1949. Heidbrink 2008, Phys. Plasmas 15, 055501 — review.

beta_fast

beta_fast(ne_20, B0)

Fast-particle beta fraction.

Parameters

ne_20 Electron density in 10²⁰ m⁻³; must be positive. B0 Toroidal field on axis in tesla; must be positive.

Returns

float The fast-particle beta.

resonance_function

resonance_function(v_f, v_A)

Analytic fast-ion resonance function.

Primary resonance (v_f ≃ v_A): F_main = (v_f/v_A)³ · exp(−(v_f/v_A − 1)² / (2·0.2²))

Sideband resonance (v_f ≃ v_A/3): F_sb = 0.15·(v_f/v_A)² · exp(−(v_f/v_A − 1/3)² / (2·0.1²))

Fu & Van Dam 1989, Phys. Fluids B 1, 1949, Eq. 28. σ_main = 0.2, σ_side = 0.1, A_side = 0.15 — ibid., Table 1.

growth_rate

growth_rate(tae, beta_fast)

γ_fast / ω ≃ β_f · q² · F(v_f/v_A).

Fu & Van Dam 1989, Phys. Fluids B 1, 1949, Eq. 15. Heidbrink 2008, Phys. Plasmas 15, 055501, Eq. 10.

TAEStabilityResult dataclass

TAEStabilityResult(n, m, frequency_kHz, gamma_drive, gamma_damp, gamma_net, unstable)

Stability verdict for one toroidal Alfvén eigenmode.

Attributes

n Toroidal mode number. m Dominant poloidal mode number. frequency_kHz Mode frequency in kilohertz. gamma_drive Fast-particle drive growth rate in s⁻¹. gamma_damp Electron Landau damping rate in s⁻¹. gamma_net Net growth rate gamma_drive - gamma_damp in s⁻¹. unstable True when the net growth rate is positive.

AlfvenStabilityAnalysis

AlfvenStabilityAnalysis(continuum, fast_params, T_e_keV=10.0)

Combined TAE stability: electron Landau damping vs. fast-particle drive.

Rosenbluth & Rutherford 1975, Phys. Rev. Lett. 34, 1428. Fu & Van Dam 1989, Phys. Fluids B 1, 1949. Heidbrink 2008, Phys. Plasmas 15, 055501.

tae_stability

tae_stability(n_range=range(1, 6))

Assess TAE stability across a range of toroidal mode numbers.

For each continuum gap, balances fast-particle drive against electron Landau damping.

Parameters

n_range Toroidal mode numbers to evaluate.

Returns

list[TAEStabilityResult] One stability result per mode found in the gaps.

critical_beta_fast

critical_beta_fast(n)

β_f,crit where γ_net = 0 for the most resonant mode.

γ_drive = C·β_f → β_crit = γ_damp / C = γ_damp / (γ_drive / β_current).

alpha_particle_loss_estimate

alpha_particle_loss_estimate(gamma_net, tau_sd=0.5)

Fraction of alpha power lost; loss ∝ γ·τ_sd (Heidbrink 2008, §V).

rsae_frequency

rsae_frequency(q_min, n, m, v_A, R0, T_e_keV=10.0, T_i_keV=10.0, m_i_amu=2.5)

Reversed-Shear Alfvén Eigenmode (RSAE) frequency.

ω²_RSAE = ω²_BAE + (v_A / (2 q_min R₀))² · (n − m/q_min)²

where the beta-induced acoustic gap (BAE) frequency is ω_BAE = v_thi · sqrt(7/4 + T_e/T_i) / R₀, v_thi = sqrt(T_i / m_i).

Sharapov et al. 2001, Phys. Lett. A 289, 127, Eq. 5. Geodesic acoustic coupling: Zonca & Chen 1996, Plasma Phys. 38, 2011.

Blob Transport

blob_transport

SOL filament / blob transport.

References

D'Ippolito, D. A., Myra, J. R. & Zweben, S. J., Phys. Plasmas 18 (2011) 060501. Comprehensive review of convective SOL transport by blobs/filaments. Krasheninnikov, S. I., Phys. Lett. A 283 (2001) 368. Blob velocity scaling: v_b = 2T_e/(e B R δ_b) × (c_s/Ω_i) [Eq. 5]. Myra, J. R. et al., Phys. Plasmas 13 (2006) 112502. Blob size scaling: δ_b ≈ (ρ_s L_∥)^(2/5) R^(1/5) — critical size at sheath-inertial transition. Garcia, O. E. et al., Phys. Plasmas 19 (2012) 092306. PDF of SOL density fluctuations follows a gamma distribution; log-normal approximation valid for large intermittency parameter.

BlobDynamics

BlobDynamics(R0, B0, Te_eV, Ti_eV, mi_amu=2.0)

Blob velocity and size in sheath-connected and inertial regimes.

D'Ippolito et al. 2011, Phys. Plasmas 18, 060501 — two-regime picture: • sheath regime (small blobs): v_b ∝ δ_b^{-1/2} • inertial regime (large blobs): v_b ∝ δ_b^{+1/2}

critical_size

critical_size(L_parallel)

Blob critical radius δ_b* at sheath–inertial transition [m].

Myra et al. 2006, Phys. Plasmas 13, 112502, Eq. 12: δ_b* ≈ 2 ρ_s (L_∥ / (R ρ_s))^(1/5)

max_velocity

max_velocity(L_parallel)

Peak blob velocity at δ_b = δ_b* [m/s].

sheath_velocity

sheath_velocity(delta_b)

Radial blob velocity in the sheath-connected regime [m/s].

Krasheninnikov 2001, Phys. Lett. A 283, 368, Eq. 5: v_b = 2 T_e / (e B R δ_b) × (c_s / Ω_i) ≡ 2 c_s ρ_s / (R √(δ_b / R)) [in the limit δ_b ≪ R]

inertial_velocity

inertial_velocity(delta_b)

Radial blob velocity in the inertia-limited regime [m/s].

D'Ippolito et al. 2011, Phys. Plasmas 18, 060501, Eq. 4: v_b ∝ (δ_b / R)^{1/2} c_s

blob_velocity

blob_velocity(delta_b, n_e, L_parallel)

Select sheath or inertial velocity based on δ_b vs δ_b*.

D'Ippolito et al. 2011, Phys. Plasmas 18, 060501, Fig. 2.

BlobPopulation dataclass

BlobPopulation(sizes, amplitudes, velocities, birth_times)

A statistically generated ensemble of SOL filaments (blobs).

Attributes

sizes Cross-field blob sizes in metres, one per blob. amplitudes Peak density amplitudes, one per blob. velocities Radial blob velocities in m/s, one per blob. birth_times Cumulative blob arrival times in seconds (Poisson process).

BlobEnsemble

BlobEnsemble(dynamics, n_blobs=1000)

Statistical ensemble of blobs with gamma-distributed amplitudes.

Garcia et al. 2012, Phys. Plasmas 19, 092306: SOL density PDF is a gamma distribution; log-normal is the large-intermittency limit. Exponential waiting times produce a Poisson blob arrival process.

generate

generate(delta_b_mean, delta_b_sigma, amplitude_mean, waiting_time_mean, rng)

Sample a blob ensemble with log-normal amplitudes and Poisson arrivals.

Parameters

delta_b_mean Mean blob size in metres; must be positive. delta_b_sigma Blob-size standard deviation in metres; must be non-negative. amplitude_mean Mean blob amplitude; must be positive. waiting_time_mean Mean inter-arrival time in seconds (exponential); must be positive. rng NumPy random generator for reproducible sampling.

Returns

BlobPopulation The sampled blob sizes, amplitudes, radial velocities, and birth times.

radial_flux

radial_flux(population)

Time-averaged blob particle flux Γ_blob [m^-2 s^-1].

D'Ippolito et al. 2011, Phys. Plasmas 18, 060501, Eq. 12.

heat_flux

heat_flux(population, Te_eV)

Blob-driven heat flux q ~ (3/2) n T v [W m^-2].

SOLBlobProfile

Scrape-off-layer density profile under blob-enhanced transport.

radial_density staticmethod

radial_density(r, Gamma_blob, D_perp, lambda_n)

SOL density profile with blob-enhanced transport.

Without blobs: n = n₀ exp(−r / λ_n). Blob transport broadens the profile via an effective λ. D'Ippolito et al. 2011, Phys. Plasmas 18, 060501, Sec. IV.

wall_flux staticmethod

wall_flux(r_wall, Gamma_blob, lambda_n)

Particle flux reaching the wall at radius r_wall.

Parameters

r_wall Wall distance from the separatrix in metres; must be non-negative. Gamma_blob Blob-driven particle flux at the separatrix in m⁻² s⁻¹; must be non-negative. lambda_n Unperturbed SOL density e-folding length in metres; must be positive.

Returns

float The wall particle flux in m⁻² s⁻¹.

BlobEvent dataclass

BlobEvent(start_idx, end_idx, peak_amplitude, duration, size_estimate)

A single detected blob event in a probe signal.

Attributes

start_idx Sample index where the event crosses the threshold. end_idx Sample index where the event returns to the mean. peak_amplitude Peak signal amplitude of the event, in signal units. duration Event duration in seconds. size_estimate Estimated blob size in metres.

BlobDetector

Threshold-crossing blob detector with conditional averaging.

Garcia et al. 2012, Phys. Plasmas 19, 092306 — standard method for identifying intermittent transport events in probe signals.

detect_blobs

detect_blobs(signal, dt=1e-06, threshold=2.5)

Detect blob events by threshold crossings on a normalised signal.

An event starts when the signal exceeds threshold standard deviations above the mean and ends when it returns to the mean.

Parameters

signal Probe time series (e.g. ion-saturation current). dt Sample spacing in seconds; must be positive. threshold Detection threshold in standard deviations; must be positive.

Returns

list[BlobEvent] The detected blob events (empty when the signal has zero variance).

conditional_average

conditional_average(signal, events, window=50)

Conditionally average the signal around detected blob events.

Parameters

signal The probe time series the events were detected in. events The detected blob events. window Half-width of the averaging window in samples; must be non-negative.

Returns

FloatArray The conditionally averaged waveform of length 2*window + 1 (zeros when no event fits a full window).

Checkpoint

checkpoint

Utilities for saving and restoring simulation and campaign states.

Enables resuming long-running stress campaigns and persisting solver hot-starts across sessions.

save_checkpoint

save_checkpoint(path, solver_state, episode, metrics)

Save simulation state to a JSON checkpoint.

Parameters

path : Path or str Destination file path. solver_state : dict Current state of the physics solvers (e.g. Psi, profiles). episode : int Current episode counter. metrics : dict Accumulated performance metrics.

load_checkpoint

load_checkpoint(path)

Restore simulation state from a JSON checkpoint.

Parameters

path : Path or str Source file path.

Returns

tuple (solver_state, episode, metrics)

Disruption Sequence

disruption_sequence

Thermal-quench, current-quench, runaway-electron, and halo-current disruption sequence model.

DisruptionConfig dataclass

DisruptionConfig(R0, a, B0, kappa, Ip_MA, W_th_MJ, Te_pre_keV, ne_pre_20, dBr_over_B_trigger)

Pre-disruption machine and plasma state for a disruption simulation.

Attributes

R0 Major radius in metres. a Minor radius in metres (must be smaller than R0). B0 Toroidal field on axis in tesla. kappa Plasma elongation (dimensionless). Ip_MA Pre-disruption plasma current in MA. W_th_MJ Thermal stored energy in MJ. Te_pre_keV Pre-disruption electron temperature in keV. ne_pre_20 Pre-disruption electron density in 10²⁰ m⁻³. dBr_over_B_trigger Radial magnetic-perturbation fraction δB_r/B that triggers the quench.

TQResult dataclass

TQResult(tau_tq_ms, post_tq_Te_eV, heat_load_MJ_m2)

Thermal-quench phase outputs.

Attributes

tau_tq_ms Thermal-quench timescale in milliseconds. post_tq_Te_eV Residual electron temperature after the quench in eV. heat_load_MJ_m2 Peak first-wall heat load in MJ/m².

CQResult dataclass

CQResult(cq_duration_ms, Ip_trace, E_par_trace)

Current-quench phase outputs.

Attributes

cq_duration_ms Current-quench timescale (L/R) in milliseconds. Ip_trace Plasma-current trace in MA over the simulated steps. E_par_trace Induced parallel electric-field trace in V/m.

REResult dataclass

REResult(I_RE_MA, W_RE_MJ, wall_heat_load_MJ_m2)

Runaway-electron beam phase outputs.

Attributes

I_RE_MA Runaway-electron beam current in MA. W_RE_MJ Runaway-electron beam energy in MJ. wall_heat_load_MJ_m2 Localised termination heat load on the wall in MJ/m².

HaloResult dataclass

HaloResult(f_halo, tpf, F_z_MN, F_sideways_MN, within_iter_limits)

Halo-current phase outputs.

Attributes

f_halo Halo-current fraction of the initial plasma current. tpf Toroidal peaking factor of the halo current. F_z_MN Vertical halo force on the vessel in MN. F_sideways_MN Sideways halo force on the vessel in MN. within_iter_limits True if f_halo × TPF stays below the ITER 0.75 limit.

DisruptionResult dataclass

DisruptionResult(tq_result, cq_result, re_result, halo_result, total_duration_ms, wall_heat_load_MJ_m2, vessel_force_MN)

Aggregate outputs of a full four-phase disruption sequence.

Attributes

tq_result Thermal-quench phase result. cq_result Current-quench phase result. re_result Runaway-electron phase result. halo_result Halo-current phase result. total_duration_ms Combined thermal- plus current-quench duration in milliseconds. wall_heat_load_MJ_m2 Combined thermal-quench and runaway termination wall load in MJ/m². vessel_force_MN Vertical vessel force in MN.

ThermalQuench

ThermalQuench(W_th_MJ, a, R0, q, B0)

Stochastic thermal-quench model (Rechester-Rosenbluth transport).

Parameters

W_th_MJ Thermal stored energy in MJ. a Minor radius in metres. R0 Major radius in metres. q Edge safety factor (dimensionless). B0 Toroidal field on axis in tesla.

rechester_rosenbluth_chi

rechester_rosenbluth_chi(dBr_over_B, v_e)

Stochastic heat transport chi [m^2/s].

chi_stoch = v_e * pi * q * R0 * (delta B_r / B)^2.

quench_timescale

quench_timescale(dBr_over_B, Te_pre_keV)

tau_TQ [s].

heat_deposition

heat_deposition(W_th_MJ, A_wall_m2, peaking_factor=3.0)

Peak heat flux [MJ/m^2].

post_tq_temperature

post_tq_temperature(Te_pre_keV, tau_tq_ms, tau_radiation_ms=0.5)

Residual electron temperature [eV] after stochastic transport and radiation cooling.

CurrentQuench

CurrentQuench(Ip_MA, L_plasma_uH, R0, a, kappa=1.0)

L/R current-quench model with Spitzer post-quench resistivity.

Parameters

Ip_MA Pre-quench plasma current in MA. L_plasma_uH Plasma self-inductance in microhenries. R0 Major radius in metres. a Minor radius in metres. kappa Plasma elongation (dimensionless).

resistivity_post_tq

resistivity_post_tq(Te_eV, Z_eff)

Spitzer resistivity [Ohm m].

cq_timescale

cq_timescale(Te_eV, Z_eff)

tau_CQ = L / R_p [ms].

induced_electric_field

induced_electric_field(dIp_dt)

E_par = L / (2 pi R0) * |dIp/dt| [V/m] where Ip is in Amps.

evolve

evolve(Te_post_tq_eV, Z_eff, dt, n_steps)

Integrate the exponential current decay and the induced field.

Parameters

Te_post_tq_eV Post-thermal-quench electron temperature in eV. Z_eff Effective charge of the post-quench plasma. dt Time step in seconds. n_steps Number of time steps.

Returns

CQResult The quench duration and the current and electric-field traces.

REBeamPhase

REBeamPhase(re_evolution)

Runaway-electron beam current, energy, and termination heat load.

Parameters

re_evolution The runaway-electron avalanche/seed evolution model.

beam_current

beam_current(n_RE, v_par, A_beam)

I_RE [MA].

beam_energy

beam_energy(n_RE, E_max_MeV, V_beam)

W_RE [MJ].

termination_heat_load

termination_heat_load(W_RE_MJ, A_deposition_m2)

Localised wall heat load when the runaway beam terminates.

Parameters

W_RE_MJ Runaway beam energy in MJ. A_deposition_m2 Deposition footprint area in m².

Returns

float Heat load in MJ/m² (inf for a non-positive area).

HaloCurrentModel

HaloCurrentModel(Ip_MA, R0, B0, kappa)

Halo-current fractions and vessel forces during a vertical displacement.

Parameters

Ip_MA Pre-disruption plasma current in MA. R0 Major radius in metres. B0 Toroidal field on axis in tesla. kappa Plasma elongation (dimensionless).

halo_fraction

halo_fraction(dZ_dt, tau_cq_ms)

Fraction of initial Ip.

toroidal_peaking_factor

toroidal_peaking_factor(n_mode=1)

TPF for n=1 asymmetry.

vertical_force

vertical_force(f_halo, tpf)

Engineering halo-load convention F_z [MN] = I_halo * B_tor * 2πR0 * TPF.

sideways_force

sideways_force(f_halo, tpf)

F_sideways [MN].

iter_limit_check

iter_limit_check(f_halo, tpf)

f_halo * TPF < 0.75.

DisruptionSequence

DisruptionSequence(config)

Four-phase disruption simulation: TQ, CQ, runaway beam, and halo forces.

Parameters

config The pre-disruption machine and plasma state.

run

run(spi_density_target=None)

Simulate the full disruption sequence end to end.

Runs the thermal quench, current quench, runaway-electron evolution driven by the induced field, and halo-current forces in turn.

Parameters

spi_density_target Optional shattered-pellet-injection target density in 10²⁰ m⁻³; when given it sets the post-quench density and dilutes Z_eff to 1.0.

Returns

DisruptionResult The combined per-phase results and the aggregate wall load, duration, and vessel force.

with_mitigation

with_mitigation(spi_density_target)

Run the sequence with shattered-pellet-injection mitigation.

Parameters

spi_density_target SPI target density in 10²⁰ m⁻³.

Returns

DisruptionResult The mitigated disruption result.

Elm Model

elm_model

ELM crash, recovery, suppression, and pedestal-coupling model utilities.

PeelingBallooningBoundary

PeelingBallooningBoundary(q95, kappa, delta, a, R0)

Stability against peeling-ballooning modes.

ELM onset: α > α_crit (ballooning) AND j_edge > j_peel (peeling). Snyder et al. 2002, Phys. Plasmas 9, 2037, Eq. 8.

peeling_limit

peeling_limit(j_edge, n_mode=10)

Critical edge current density j_peel.

Geometry- and mode-aware peeling threshold proxy:

j_peel,crit ∝ (R0/a) * F(κ, δ) / (q95 * sqrt(n_mode))

where F(κ, δ) captures edge-shaping stabilisation and n_mode captures reduced stability margin for higher-n peeling harmonics. This keeps the Snyder-type coupled PB scaling structure while avoiding a single-parameter 1/q95 closure.

ballooning_limit

ballooning_limit(s_edge)

Critical normalised pressure gradient α_crit.

Shaping factor (1 + κ²(1 + 2δ²)) from Sauter et al. 1999, Phys. Plasmas 6, 2834.

is_unstable

is_unstable(alpha_edge, j_edge, s_edge)

Evaluate the combined peeling-ballooning criterion (elliptical boundary).

Snyder et al. 2002, Phys. Plasmas 9, 2037, Eq. 8.

stability_margin

stability_margin(alpha_edge, j_edge, s_edge)

Distance to boundary (positive = stable).

ELMCrashResult dataclass

ELMCrashResult(delta_W_MJ, T_ped_post, n_ped_post, peak_heat_flux_MW_m2, duration_ms)

Outcome of a single ELM crash applied to the pedestal.

Attributes

delta_W_MJ Energy expelled by the crash in MJ. T_ped_post Post-crash pedestal temperature (same unit as the input). n_ped_post Post-crash pedestal density (same unit as the input). peak_heat_flux_MW_m2 Peak divertor heat flux during the crash in MW/m². duration_ms Crash duration in milliseconds.

ELMCrashModel

ELMCrashModel(f_elm_fraction=0.08)

Applies a Type I ELM crash to pedestal profiles.

ΔW_ELM / W_ped ≈ 0.04–0.15 (Type I). Loarte et al. 2003, PPCF 45, 1549, Fig. 12.

crash

crash(T_ped, n_ped, W_ped, A_wet=1.0)

ΔW_ELM = f × W_ped; T and n drop by √(1 − f) (W ∝ n T).

Loarte et al. 2003, PPCF 45, 1549, Fig. 12.

apply_to_profiles

apply_to_profiles(rho, Te, ne, rho_ped)

Drop the pedestal region of the Te and ne profiles by the crash factor.

Outside the pedestal top (rho >= rho_ped) both profiles are scaled by √(1 − f_elm_fraction).

Parameters

rho Sorted normalised-radius grid, shape (n,). Te Electron-temperature profile on rho; positive. ne Electron-density profile on rho; positive. rho_ped Pedestal-top normalised radius; must lie within the grid.

Returns

tuple of AnyFloatArray The post-crash (Te, ne) profiles.

Type3ELMCrashModel

Type3ELMCrashModel(f_elm_fraction=ELM_TYPE3_FRACTION)

Bases: ELMCrashModel

Type III ELM: smaller energy loss, higher frequency, triggered at lower pedestal pressure.

Zohm 1996, PPCF 38, 105.

ELMEvent dataclass

ELMEvent(time, delta_W_MJ, f_elm_Hz, crash_type)

A single ELM crash event in a control cycle.

Attributes

time Event time in seconds. delta_W_MJ Energy expelled by the crash in MJ. f_elm_Hz Instantaneous ELM frequency in Hz. crash_type ELM type label (e.g. "Type I").

RMPSuppression

RMPSuppression(n_coils=3, I_rmp_kA=90.0, n_toroidal=3)

Resonant Magnetic Perturbation effects on ELMs.

chirikov_parameter

chirikov_parameter(q_profile, rho, delta_B_r, B0, R0)

σ_Chir = Σ w_mn / dr_mn; w_mn = 4√(R0 q' δB_r / (n B0)).

suppressed

suppressed(sigma_chir)

Return whether RMP suppresses ELMs (σ_Chirikov > 1).

Parameters

sigma_chir Chirikov overlap parameter at the pedestal.

Returns

bool True when the islands overlap and ELMs are suppressed.

pedestal_transport_enhancement

pedestal_transport_enhancement(sigma_chir)

Pedestal transport enhancement factor from RMP stochasticity.

Parameters

sigma_chir Chirikov overlap parameter.

Returns

float Multiplicative transport enhancement (1.0 below overlap).

density_pump_out

density_pump_out(sigma_chir)

Fractional pedestal density pump-out under RMP.

Parameters

sigma_chir Chirikov overlap parameter.

Returns

float Fractional density reduction (0.2 above overlap, 0.0 otherwise).

ELMCycler

ELMCycler(pb_boundary, crash_model)

Tracks pedestal state and triggers ELM events.

step

step(dt, alpha_edge, j_edge, s_edge, T_ped, n_ped, W_ped)

Advance the pedestal clock and trigger an ELM if peeling-ballooning unstable.

Parameters

dt Time step in seconds; must be positive. alpha_edge Normalised edge pressure gradient; must be non-negative. j_edge Edge current density; must be non-negative. s_edge Edge magnetic shear; must be non-negative. T_ped Pedestal temperature passed to the crash model. n_ped Pedestal density passed to the crash model. W_ped Pedestal stored energy in MJ.

Returns

ELMEvent or None The crash event when the boundary is unstable, otherwise None.

elm_power_balance_frequency

elm_power_balance_frequency(P_SOL_MW, W_ped_MJ, f_elm_fraction)

f_ELM ∝ (P_loss − P_LH) / ΔW_ELM ≈ P_SOL / (f × W_ped).

Leonard et al. 1999, J. Nucl. Mater. 266-269, 109.

Eped Pedestal

eped_pedestal

EPED-style pedestal prediction and validation-point utilities.

EPEDConfigSchema

Bases: BaseModel

Pydantic v2 schema for EPED pedestal operating-point configuration.

EPEDConfig dataclass

EPEDConfig(R0, a, B0, kappa, delta, Ip_MA, ne_ped_19, B_pol_ped, C_KBM=C_KBM_DEFAULT, n_mode_min=5, n_mode_max=30, nu_star_e=0.0)

Machine and pedestal inputs for the EPED pedestal model.

Attributes

R0 Major radius in metres. a Minor radius in metres. B0 Vacuum toroidal field in tesla. kappa Plasma elongation. delta Plasma triangularity. Ip_MA Plasma current in MA. ne_ped_19 Pedestal electron density in 10¹⁹ m⁻³. B_pol_ped Poloidal field at the pedestal in tesla. C_KBM Kinetic-ballooning-mode coefficient (Snyder 2009, Eq. 4). n_mode_min Minimum toroidal mode number scanned for peeling-ballooning. n_mode_max Maximum toroidal mode number scanned. nu_star_e Electron collisionality (0 = collisionless limit).

model_json_schema classmethod

model_json_schema()

Return the JSON Schema for serialized EPED pedestal configurations.

EPEDResult dataclass

EPEDResult(p_ped_kPa, T_ped_keV, n_ped_19, delta_ped, beta_p_ped, alpha_crit, delta_ped_collisionless=0.0)

Predicted pedestal structure from the EPED model.

Attributes

p_ped_kPa Pedestal pressure in kPa. T_ped_keV Pedestal temperature in keV. n_ped_19 Pedestal density in 10¹⁹ m⁻³. delta_ped Pedestal width in normalised poloidal flux. beta_p_ped Pedestal poloidal beta. alpha_crit Critical normalised pressure gradient at the peeling-ballooning limit. delta_ped_collisionless Pedestal width before the collisionality correction.

EPEDValidationPoint dataclass

EPEDValidationPoint(machine, shot, p_ped_measured_kPa, p_ped_eped_kPa, delta_ped_measured, delta_ped_eped)

Measured-versus-EPED pedestal comparison for one discharge.

Attributes

machine Device name. shot Shot number; must be a positive integer. p_ped_measured_kPa Measured pedestal pressure in kPa. p_ped_eped_kPa EPED-predicted pedestal pressure in kPa. delta_ped_measured Measured pedestal width. delta_ped_eped EPED-predicted pedestal width.

PedestalProfileGenerator

PedestalProfileGenerator(eped_result, Te_sep_eV=100.0, ne_sep_19=0.3)

Generates mtanh profiles from EPED predictions.

generate

generate(rho)

Produce (Te, ne) via mtanh.

rho_sym = 1 − Δ_ped/2 (centre of gradient region)

EpedPedestalPrediction dataclass

EpedPedestalPrediction(Delta_ped, T_ped_keV, p_ped_kPa)

Compact EPED prediction for the integrated transport solver.

Attributes

Delta_ped Pedestal width in normalised poloidal flux. T_ped_keV Pedestal temperature in keV. p_ped_kPa Pedestal pressure in kPa.

EpedPedestalModel

EpedPedestalModel(R0, a, B0, Ip_MA, kappa=1.7, A_ion=2.0, Z_eff=1.5)

Wrapper for integrated_transport_solver compatibility.

predict

predict(ne_ped_19, nu_star_e=0.0)

Predict the pedestal structure at a given density and collisionality.

Parameters

ne_ped_19 Pedestal electron density in 10¹⁹ m⁻³; must be positive. nu_star_e Electron collisionality; must be non-negative (0 = collisionless).

Returns

EpedPedestalPrediction The predicted pedestal width, temperature, and pressure.

eped_validation_database

eped_validation_database()

Synthetic test data for EPED model validation.

Values are approximate and do not represent real experimental measurements.

eped1_predict

eped1_predict(config)

Self-consistent (p_ped, Δ_ped) from PB + KBM constraints. [S09].

Iteration: guess Δ → α_crit(Δ) → p_ped → β_p,ped → Δ_KBM. Collisionality correction applied after convergence. [S11]

eped1_scan

eped1_scan(config, ne_ped_range)

Scan pedestal density across operating space.

GK CGYRO

gk_cgyro

CGYRO external solver interface.

Reference: Candy & Waltz, J. Comp. Phys. 186 (2003) 545.

CGYROSolver

CGYROSolver(binary='cgyro', work_dir=None, *, allow_fallback=False, allow_legacy_fallback=False)

Bases: GKSolverBase

CGYRO external solver.

is_available

is_available()

Return whether the CGYRO binary is on the PATH.

prepare_input

prepare_input(params)

Write the CGYRO input deck and return its working directory.

Parameters

params Local gyrokinetic input parameters.

Returns

Path The working directory containing input.cgyro.

run

run(input_path, *, timeout_s=30.0)

Execute CGYRO on a prepared input directory.

Fails closed when the binary is unavailable, the run fails, or the output is non-converged, unless the explicit legacy fallback is enabled.

Parameters

input_path Working directory holding input.cgyro. timeout_s Subprocess timeout in seconds.

Returns

GKOutput The parsed CGYRO result.

Raises

RuntimeError If CGYRO is unavailable, fails, or returns non-converged output and the legacy fallback is disabled.

generate_cgyro_input

generate_cgyro_input(params)

Render a CGYRO input.cgyro deck from local GK parameters.

Parameters

params Local gyrokinetic input parameters.

Returns

str The CGYRO input-deck text.

parse_cgyro_output

parse_cgyro_output(run_dir)

Parse a CGYRO run directory into a standard GK output.

Parameters

run_dir Directory containing the CGYRO out.cgyro.freq output.

Returns

GKOutput The parsed growth rate, real frequency, and diffusivities; a non-converged output when the file is missing or unreadable.

GK GENE

gk_gene

GENE (Gyrokinetic Electromagnetic Numerical Experiment) external solver.

Generates parameters_gene namelist, executes via subprocess, parses nrg_xxxx eigenvalue files.

Reference: Jenko et al., Phys. Plasmas 7 (2000) 1904.

GENESolver

GENESolver(binary='gene', work_dir=None, *, allow_fallback=False, allow_legacy_fallback=False)

Bases: GKSolverBase

GENE external solver via gene binary.

is_available

is_available()

Return whether the GENE binary is on the PATH.

prepare_input

prepare_input(params)

Write the GENE parameters deck and return its working directory.

Parameters

params Local gyrokinetic input parameters.

Returns

Path The working directory containing the parameters file.

run

run(input_path, *, timeout_s=30.0)

Execute GENE on a prepared input directory.

Fails closed when the binary is unavailable, the run fails, or the output is non-converged, unless the explicit legacy fallback is enabled.

Parameters

input_path Working directory holding the GENE parameters deck. timeout_s Subprocess timeout in seconds.

Returns

GKOutput The parsed GENE result.

Raises

RuntimeError If GENE is unavailable, fails, or returns non-converged output and the legacy fallback is disabled.

generate_gene_input

generate_gene_input(params)

Render a GENE parameters deck from local GK parameters.

Parameters

params Local gyrokinetic input parameters.

Returns

str The GENE input-deck text.

parse_gene_output

parse_gene_output(run_dir)

Parse GENE nrg output files.

GK Geometry

gk_geometry

Miller parameterisation of local magnetic equilibrium geometry for flux-tube gyrokinetic calculations.

Computes metric coefficients, field-line curvature, and the Jacobian on a ballooning-angle grid from (R0, a, kappa, delta, q, s_hat, ...).

Reference: Miller et al., Phys. Plasmas 5 (1998) 973.

MillerGeometry dataclass

MillerGeometry(theta, R, Z, B_mag, jacobian, g_rr, g_rt, g_tt, metric_determinant, kappa_n, kappa_g, b_dot_grad_theta)

Flux-tube geometry on a ballooning-angle grid.

All arrays have shape (n_theta,).

miller_geometry

miller_geometry(R0, a, rho, kappa=1.0, delta=0.0, s_kappa=0.0, s_delta=0.0, q=1.4, s_hat=0.78, alpha_MHD=0.0, dR_dr=0.0, B0=5.3, n_theta=64, n_period=2)

Compute Miller geometry on a ballooning-angle grid.

Parameters

R0 : float Major radius [m]. a : float Minor radius [m]. rho : float Normalised flux coordinate (r/a). kappa, delta : float Elongation and triangularity. s_kappa, s_delta : float Shear of elongation/triangularity: (r/kappa)(dkappa/dr), etc. q, s_hat : float Safety factor and magnetic shear s = (r/q)(dq/dr). alpha_MHD : float Shafranov shift parameter alpha = -q^2 R0 (dp/dr) / (B0^2 / 2mu0). dR_dr : float Shafranov shift gradient dR_axis/dr (typically negative). B0 : float Toroidal field at R0 [T]. n_theta : int Grid points per 2*pi period. n_period : int Number of poloidal periods (ballooning copies).

circular_geometry

circular_geometry(R0=2.78, a=1.0, rho=0.5, q=1.4, s_hat=0.78, B0=2.0, n_theta=64, n_period=2)

Circular cross-section limit (kappa=1, delta=0).

Useful for verification against analytic results and the Cyclone Base Case (Dimits et al. 2000).

GK GS2

gk_gs2

GS2 external solver interface.

Reference: Kotschenreuther et al., Comp. Phys. Comm. 88 (1995) 128.

GS2Solver

GS2Solver(binary='gs2', work_dir=None, *, allow_fallback=False, allow_legacy_fallback=False)

Bases: GKSolverBase

GS2 external solver.

is_available

is_available()

Return whether the GS2 binary is on the PATH.

prepare_input

prepare_input(params)

Write the GS2 gs2.in deck and return its working directory.

Parameters

params Local gyrokinetic input parameters.

Returns

Path The working directory containing gs2.in.

run

run(input_path, *, timeout_s=30.0)

Execute GS2 on a prepared input directory.

Fails closed when the binary is unavailable, the run fails, or the output is non-converged, unless the explicit legacy fallback is enabled.

Parameters

input_path Working directory holding gs2.in. timeout_s Subprocess timeout in seconds.

Returns

GKOutput The parsed GS2 result.

Raises

RuntimeError If GS2 is unavailable, fails, or returns non-converged output and the legacy fallback is disabled.

generate_gs2_input

generate_gs2_input(params)

Render a GS2 gs2.in deck from local GK parameters.

Parameters

params Local gyrokinetic input parameters.

Returns

str The GS2 input-deck text.

parse_gs2_output

parse_gs2_output(run_dir)

Parse GS2 NetCDF or text output.

GK Nonlinear

gk_nonlinear

Nonlinear δf gyrokinetic solver in flux-tube geometry.

Solves the gyrokinetic Vlasov equation for the perturbed distribution function δf(k_x, k_y, θ, v_∥, μ) with E×B nonlinearity computed via dealiased 2D FFT (Orszag 1971 2/3 rule).

Physics
  • Quasineutrality field solve (adiabatic electrons)
  • E×B advection: dealiased Arakawa bracket via FFT
  • Parallel streaming: 4th-order compact finite differences
  • Magnetic curvature/grad-B drift
  • Simplified Sugama collision operator
  • 4th-order hyperdiffusion for numerical stability
  • RK4 time stepping with CFL-adaptive dt

References

  • Dimits et al., Phys. Plasmas 7 (2000) 969 — CBC benchmark
  • Orszag, J. Atmos. Sci. 28 (1971) 1074 — dealiasing
  • Rosenbluth & Hinton, Phys. Rev. Lett. 80 (1998) 724 — zonal flows
  • Sugama & Watanabe, Phys. Plasmas 13 (2006) 012501 — collisions

NonlinearGKConfig dataclass

NonlinearGKConfig(n_kx=16, n_ky=16, n_theta=64, n_vpar=16, n_mu=8, n_species=2, dt=0.05, n_steps=5000, save_interval=100, Lx=80.0, Ly=62.83, vpar_max=3.0, mu_max=9.0, dealiasing='2/3', hyper_order=4, hyper_coeff=0.1, cfl_factor=0.5, cfl_adapt=True, collisions=True, nu_collision=0.01, collision_model='krook', nonlinear=True, kinetic_electrons=False, mass_ratio_me_mi=1.0 / 400.0, implicit_electrons=False, electromagnetic=False, beta_e=0.01, d_e_sq=0.0, R0=2.78, a=1.0, B0=2.0, q=1.4, s_hat=0.78, R_L_Ti=6.9, R_L_Te=6.9, R_L_ne=2.2)

Grid and physics parameters for the nonlinear solver.

NonlinearGKState dataclass

NonlinearGKState(f, phi, time, A_par=None)

Full 5D+1 state of the nonlinear solver.

NonlinearGKResult dataclass

NonlinearGKResult(chi_i, chi_e, chi_i_gB=0.0, Q_i_t=(lambda: empty(0))(), Q_e_t=(lambda: empty(0))(), phi_rms_t=(lambda: empty(0))(), zonal_rms_t=(lambda: empty(0))(), time=(lambda: empty(0))(), converged=False, final_state=None)

Time-averaged transport and diagnostics.

NonlinearGKSolver

NonlinearGKSolver(config=None)

Nonlinear δf gyrokinetic solver.

field_solve

field_solve(f)

Solve quasineutrality for φ(k_x, k_y, θ).

Adiabatic: [(1-Γ₀_i) + 1] φ = ∫ J₀_i h_i dv Kinetic e: (1-Γ₀_i + 1-Γ₀_e) φ = ∫ J₀_i h_i dv - ∫ J₀_e h_e dv

ampere_solve

ampere_solve(f)

Ampere's law for A_∥: k_perp² A_∥ = β_e Σ_s q_s ∫ v_∥ J₀ h_s dv.

Returns A_par(n_kx, n_ky, n_theta). Zero when electromagnetic=False.

exb_bracket

exb_bracket(phi, f_s)

Poisson bracket {φ, f} = ∂φ/∂x ∂f/∂y - ∂φ/∂y ∂f/∂x.

Computed via 2D FFT with 2/3 dealiasing (Orszag 1971). phi: (nkx, nky, nθ) f_s: (nkx, nky, nθ, nvpar, nμ) Returns: (nkx, nky, nθ, nvpar, nμ)

parallel_streaming

parallel_streaming(f_s)

4th-order FD for v_∥ b·∇θ ∂f/∂θ with ballooning connection BC.

At θ boundaries, kx shifts by ±s_hat×ky per poloidal turn. f_s: (nkx, nky, nθ, nvpar, nμ)

magnetic_drift

magnetic_drift(f_s)

Curvature and grad-B drift contribution.

ω_D = k_y × 2(E/T) × [κ_n(1-λB/B₀) + κ_g√(1-λB/B₀)]

collide

collide(f_s)

Dispatch to Krook or Sugama collision model.

gradient_drive

gradient_drive(phi, A_par=None)

Background gradient drive: -ik_y ω_* × (φ - v_∥ A_∥) × F_M.

EM contribution: the effective potential is φ - v_∥ A_∥/c, which adds the magnetic flutter drive for KBM/MTM at finite β.

hyperdiffusion

hyperdiffusion(f)

4th-order hyperdiffusion: -D_H × k_perp^(2p) × f.

rhs

rhs(state)

Full RHS: df/dt = -v_E·∇f - v_∥ b·∇f - ω_D f + C[f] + S + D_H.

compute_fluxes

compute_fluxes(state)

Ion and electron heat flux in gyro-Bohm units.

Q_i = Re[Σ_{ky>0} ik_y conj(φ) p_i] — radial E×B flux of pressure. The ik_y factor gives the radial component of v_E = ∇φ×b̂/B.

phi_rms

phi_rms(state)

RMS amplitude of the electrostatic potential φ over the state.

zonal_rms

zonal_rms(state)

RMS amplitude of zonal modes (k_y = 0).

total_energy

total_energy(state)

Total δf² energy (conservation diagnostic).

init_state

init_state(amplitude=1e-05, seed=42)

Random small-amplitude initial perturbation.

init_single_mode

init_single_mode(kx_idx=0, ky_idx=1, amplitude=1e-05)

Single-mode initial condition for linear growth rate recovery.

run

run(state=None)

Run the nonlinear simulation.

GK Online Learner

OnlineLearner admits finite nonnegative transport targets only when the caller-supplied OOD score is inside the configured threshold. Retraining uses a validation holdout, rolls back on non-improvement, and can persist an auditable JSON report containing every accepted or rejected update decision.

gk_online_learner

Online learning layer for surrogate transport model improvement.

Accumulates GK spot-check results as training data and periodically fine-tunes the QLKNN surrogate. Includes validation holdout and automatic rollback if performance degrades.

TrainingSample dataclass

TrainingSample(input_10d, target_3d, ood_score=0.0, source='gk_spot_check')

Single (input, target) pair from a GK spot-check.

LearnerConfig dataclass

LearnerConfig(buffer_size=100, validation_fraction=0.2, n_epochs=10, learning_rate=0.0001, max_generations=50, max_ood_score=3.0, min_validation_improvement=0.0)

Online learner parameters.

RetrainDecision dataclass

RetrainDecision(generation, accepted, val_loss, previous_best_val_loss, train_samples, validation_samples, buffer_size, reason, max_ood_score, learning_rate, n_epochs)

Auditable retraining admission decision.

OnlineLearner

OnlineLearner(config=None)

Accumulate GK data and fine-tune the surrogate when ready.

buffer_full property

buffer_full

Whether the replay buffer has reached the configured size.

add_sample

add_sample(input_10d, target_3d, *, ood_score=0.0, source='gk_spot_check')

Admit one labelled GK spot-check sample into the replay buffer.

Parameters

input_10d The 10-dimensional gyrokinetic input vector, shape (10,). target_3d The transport-coefficient target [chi_i, chi_e, D_e], shape (3,); values must be non-negative. ood_score Out-of-distribution score; must be non-negative and not exceed the configured admission threshold. source Non-empty provenance label for the sample.

Raises

ValueError If a shape, finiteness, non-negativity, OOD-threshold, or source constraint is violated.

try_retrain

try_retrain(current_weights=None)

Attempt retraining if buffer is full.

Parameters

current_weights : dict or None Current MLP weight arrays (w1, b1, ...). If None, trains from scratch on the buffer data.

Returns

dict or None Updated weights if retraining succeeded, None if skipped or rolled back due to validation loss increase.

latest_decision

latest_decision()

Return the latest retraining decision, if any.

save_retrain_report

save_retrain_report(path)

Persist retraining decisions and current learner state as JSON.

reset

reset()

Clear the buffer, generation counter, weight backup, and history.

GK QuaLiKiz

gk_qualikiz

QuaLiKiz quasilinear gyrokinetic solver interface.

Unlike TGLF/GENE/GS2/CGYRO, QuaLiKiz has a Python API (qlknn-hyper). Falls back to subprocess if the Python module is unavailable.

Reference: Bourdelle et al., Phys. Plasmas 14 (2007) 112501.

QuaLiKizSolver

QuaLiKizSolver(binary='qualikiz', work_dir=None, *, allow_fallback=False, allow_legacy_fallback=False)

Bases: GKSolverBase

QuaLiKiz solver via Python API or subprocess fallback.

is_available

is_available()

Return whether the qualikiz_tools Python package is importable.

prepare_input

prepare_input(params)

Stage the QuaLiKiz run directory and cache the input parameters.

Parameters

params Local gyrokinetic input parameters (stored for the Python API path).

Returns

Path The working directory for the run.

run

run(input_path, *, timeout_s=30.0)

Run QuaLiKiz via its Python API on the staged parameters.

Fails closed when the API is unavailable or fails, unless the explicit legacy fallback is enabled.

Parameters

input_path The staged working directory. timeout_s Run timeout in seconds.

Returns

GKOutput The parsed QuaLiKiz result.

Raises

RuntimeError If the QuaLiKiz API is unavailable or fails and the legacy fallback is disabled.

GK Species

gk_species

Species definitions and collision operator for the linear GK solver.

Velocity-space discretisation uses (energy, lambda) coordinates with Gauss-Legendre quadrature. The collision operator implements a bounded Sugama-style test-particle operator with separate pitch-angle deflection and thermal energy-relaxation coefficients.

Limitations
  • Test-particle collision coefficients only (no field-particle response)
  • No inter-species collisions (no electron-ion drag)
  • Not the full linearised Fokker-Planck operator This bounded operator is adequate for local ITG/TEM regression ordering but not for quantitative collisional damping rates.

References

  • Sugama & Watanabe, Phys. Plasmas 13 (2006) 012501
  • Abel et al., Rep. Prog. Phys. 76 (2013) 116201, §4.4

GKSpecies dataclass

GKSpecies(mass_amu, charge_e, temperature_keV, density_19, R_L_T, R_L_n, is_adiabatic=False)

Single plasma species for the GK solver.

Parameters

mass_amu : float Mass in atomic mass units (2.0 for deuterium). charge_e : float Charge in units of e (+1 for ions, -1 for electrons). temperature_keV : float Temperature [keV]. density_19 : float Density [10^19 m^-3]. R_L_T : float Normalised temperature gradient R/L_T. R_L_n : float Normalised density gradient R/L_n. is_adiabatic : bool If True, use adiabatic response instead of kinetic.

mass_kg property

mass_kg

Species mass in kilograms.

thermal_speed property

thermal_speed

v_th = sqrt(2 T / m) [m/s].

larmor_radius property

larmor_radius

rho_s = m v_th / (|q| B) — requires B to be set externally.

Returns rho_s / B for later scaling.

VelocityGrid dataclass

VelocityGrid(n_energy=16, n_lambda=24)

Energy-lambda velocity-space grid with Gauss-Legendre weights.

Energy E normalised to T_s. Lambda = mu B0 / E ∈ [0, 1].

n_total property

n_total

Total number of velocity-space grid points (energy × lambda).

DiamagneticFrequencies dataclass

DiamagneticFrequencies(density, temperature, pressure)

Normalised species diamagnetic frequencies for GK drive terms.

Frequencies are normalised to v_th / R. Positive ion temperature or density gradients therefore propagate in the ion diamagnetic direction, while electron frequencies carry the opposite sign through charge_e.

deuterium_ion

deuterium_ion(T_keV=8.0, n_19=10.0, R_L_T=6.9, R_L_n=2.2)

Construct a deuterium main-ion species for gyrokinetic input.

Parameters

T_keV Temperature in keV. n_19 Density in 10¹⁹ m⁻³. R_L_T Normalised temperature gradient R / L_T. R_L_n Normalised density gradient R / L_n.

Returns

GKSpecies The deuterium-ion species (mass 2 amu, charge +1).

electron

electron(T_keV=8.0, n_19=10.0, R_L_T=6.9, R_L_n=2.2, adiabatic=True)

Construct an electron species for gyrokinetic input.

Parameters

T_keV Temperature in keV. n_19 Density in 10¹⁹ m⁻³. R_L_T Normalised temperature gradient R / L_T. R_L_n Normalised density gradient R / L_n. adiabatic Whether to treat the electrons as adiabatic.

Returns

GKSpecies The electron species (mass m_e/m_p amu, charge -1).

diamagnetic_frequencies

diamagnetic_frequencies(species, k_y_rho_s)

Return density, temperature, and pressure diamagnetic frequencies.

The returned values are dimensionless frequencies normalised to v_th/R:

omega_*n = -sign(q_s) k_y rho_s R/L_n omega_*T = -sign(q_s) k_y rho_s R/L_T omega_*p = omega_*n + omega_*T

This is a local-drive bookkeeping utility for bounded GK regression and controller-adapter contracts; it does not introduce an external-code validation claim.

bessel_j0

bessel_j0(x)

J_0(x) via scipy or polynomial approximation.

For the GK solver, x = k_perp * rho_s * sqrt(2 * lambda * E). At small x (long wavelength limit), J_0 → 1.

collision_frequencies

collision_frequencies(species, n_e_19, T_e_keV, Z_eff=1.0, ln_lambda=17.0)

Compute deflection and energy-diffusion collision frequencies.

Returns (nu_D, nu_E) normalised to v_th / R.

Bounded Sugama-style test-particle coefficients

nu_D is the pitch-angle deflection rate. nu_E is a thermal energy-relaxation rate with the expected electron-ion mass-ratio suppression for heavy species and field temperature dependence. Field-particle and inter-species momentum conservation terms are intentionally excluded by the traceability contract.

pitch_angle_operator

pitch_angle_operator(n_lambda, lam, B_ratio=1.0)

Pitch-angle scattering operator matrix L_pitch on lambda grid.

L_pitch f = (1/2) d/d(xi) (1-xi^2) df/d(xi) where xi = v_∥ / v = sqrt(1 - lambda * B/B0).

Returns shape (n_lambda, n_lambda).

GK TGLF

gk_tglf

TGLF (Trapped Gyro-Landau Fluid) external solver interface.

Generates TGLF input namelists, executes the tglf binary via subprocess, and parses growth-rate / flux output files.

Reference: Staebler et al., Phys. Plasmas 14 (2007) 055909.

TGLFSolver

TGLFSolver(binary='tglf', work_dir=None)

Bases: GKSolverBase

TGLF external solver via GACODE tglf binary.

Parameters

binary : str Path or name of the tglf executable. work_dir : Path or None Persistent working directory. If None, uses a tempdir per call.

is_available

is_available()

Return whether the TGLF binary is on the PATH.

prepare_input

prepare_input(params)

Write the TGLF input.tglf deck and return its working directory.

Parameters

params Local gyrokinetic input parameters.

Returns

Path The working directory containing input.tglf.

run

run(input_path, *, timeout_s=30.0)

Execute TGLF on a prepared input directory.

Fails closed when the binary is unavailable, the run fails, or the output is non-converged, unless the explicit legacy fallback is enabled.

Parameters

input_path Working directory holding input.tglf. timeout_s Subprocess timeout in seconds.

Returns

GKOutput The parsed TGLF result.

Raises

RuntimeError If TGLF is unavailable, fails, or returns non-converged output and the legacy fallback is disabled.

TGLFExecutionError

Bases: RuntimeError

Raised when external TGLF execution or output validation fails.

generate_tglf_input

generate_tglf_input(params)

Render a TGLF namelist string from local plasma parameters.

parse_tglf_output

parse_tglf_output(run_dir)

Parse TGLF output files from run_dir.

Expects out.tglf.run with growth rates and out.tglf.transport with quasilinear fluxes. If files are missing, returns a zero-flux unconverged result.

GK TGLF Native

gk_tglf_native

Native TGLF-equivalent quasilinear transport model.

Provides SAT0/SAT1/SAT2 spectral saturation, E×B shear quench, multi-scale ITG-ETG coupling, and trapped-particle damping. No external binary.

References

  • Staebler et al., Phys. Plasmas 14 (2007) 055909 — SAT0/SAT1
  • Staebler et al., Phys. Plasmas 24 (2017) 055906 — SAT2
  • Maeyama et al., Phys. Rev. Lett. 114 (2015) 255002 — cross-scale
  • Waltz et al., Phys. Plasmas 4 (1997) 2482 — E×B shear quench
  • Connor et al., Nucl. Fusion 14 (1974) 185 — trapped-particle modes

TGLFNativeConfig dataclass

TGLFNativeConfig(sat_model='SAT1', exb_shear_model='linear', multiscale=False, n_ky_ion=16, n_ky_etg=0, n_theta=32, alpha_exb=ALPHA_EXB_DEFAULT, alpha_cs=ALPHA_CS_DEFAULT)

SAT model selection and solver parameters.

TGLFNativeResult dataclass

TGLFNativeResult(chi_i, chi_e, D_e, D_i=0.0, V_e=0.0, k_y=(lambda: empty(0))(), gamma=(lambda: empty(0))(), omega_r=(lambda: empty(0))(), gamma_net=(lambda: empty(0))(), phi_sq=(lambda: empty(0))(), gamma_exb=0.0, dominant_mode='stable', sat_model='SAT0', converged=True, chi_e_etg=0.0)

Transport fluxes and spectral diagnostics.

TGLFNativeSolver

TGLFNativeSolver(config=None)

Bases: GKSolverBase

Native TGLF-equivalent quasilinear transport model.

Pure Python implementation of SAT0/SAT1/SAT2 spectral saturation applied to the native linear GK eigenvalue spectrum.

is_available

is_available()

Return whether the native solver is available (always True).

prepare_input

prepare_input(params)

Unsupported for the native solver; use :meth:run_from_params.

Raises

NotImplementedError Always; the native model has no external input deck.

run

run(input_path, *, timeout_s=30.0)

Unsupported for the native solver; use :meth:run_from_params.

Raises

NotImplementedError Always; the native model takes parameters, not an input file.

run_from_params

run_from_params(params, *, timeout_s=30.0)

Run the native TGLF model and return a standard GK output.

Parameters

params Local gyrokinetic input parameters. timeout_s Accepted for interface compatibility; the native solve is bounded internally.

Returns

GKOutput Transport diffusivities, growth rate, real frequency, dominant mode, and convergence flag.

solve

solve(params)

Compute the native TGLF-equivalent quasilinear transport result.

Parameters

params Local gyrokinetic input parameters.

Returns

TGLFNativeResult The SAT-model transport fluxes, growth rates, and mode spectrum.

exb_shear_rate

exb_shear_rate(params)

E×B shearing rate normalised to c_s/a.

Proxy: γ_ExB ≈ |s_hat/q| × ε × R/L_Ti × 0.1 Waltz et al. 1997, Eq. (3).

trapped_fraction

trapped_fraction(epsilon)

f_t = sqrt(2ε/(1+ε)). Wesson, Tokamaks, Ch. 3.

trapped_particle_damping

trapped_particle_damping(params)

Growth-rate damping factor from trapped particles.

Connor et al. 1974: scales with f_t × ν*/ω_bounce. Returns multiplicative factor in (0.1, 1.0].

gamma_0_flr

gamma_0_flr(b)

Γ₀(b) = I₀(b)e^{-b} ≈ 1/(1+b). Padé approximant, <5% error for b<3.

spectral_weight

spectral_weight(gamma_net, k_y)

Normalised spectral intensity I_k = (γ_net/k_y) / Σ(γ_net/k_y).

Staebler 2007, Eq. (7).

sat0

sat0(linear, gamma_exb, tp_factor, cfg)

SAT0: mixing-length saturation with E×B shear quench.

φ²_k = γ_net / (k_y² |ω_r|), where γ_net = max(γ·tp - α|γ_ExB|, 0).

sat1

sat1(linear, gamma_exb, tp_factor, cfg)

SAT1: spectral saturation with E×B shear quench.

Staebler et al. 2007: amplitude set by peak mode, distributed by spectral weight. φ²_k = I_k × γ_max_net / k_y_max².

sat2

sat2(linear, gamma_exb, tp_factor, cfg)

SAT2: multi-scale spectral saturation with ITG-ETG cross-scale coupling.

Staebler et al. 2017: ion-scale follows SAT1. ETG modes enhanced by α_cs × (γ_ETG / γ_ITG_max) from Maeyama et al. 2015.

quasilinear_weights

quasilinear_weights(linear, phi_sq, gamma_net, ion_mass_amu, ion_T_keV, params)

Velocity-space integrated QL weights → physical fluxes.

Returns (chi_i, chi_e, D_e, V_e, chi_e_etg) in [m²/s].

GK Verification Report

gk_verification_report

Per-session verification report for hybrid surrogate+GK transport.

Tracks spot-check statistics, correction factors, OOD trigger rates, and error distributions across a simulation session.

VerificationReport dataclass

VerificationReport(total_steps=0, steps_verified=0, total_spot_checks=0, ood_triggers=0, records=list(), correction_factors=list())

Accumulated verification statistics for one simulation session.

verification_fraction property

verification_fraction

Fraction of steps that were verified against the GK reference.

max_rel_error property

max_rel_error

Largest absolute relative error in ion heat diffusivity across records.

mean_rel_error property

mean_rel_error

Mean absolute relative error in ion heat diffusivity across records.

add_step

add_step(verified, n_spot_checks=0, n_ood=0)

Record one simulation step and its spot-check and OOD counts.

Parameters

verified Whether this step was verified against the GK reference. n_spot_checks Number of GK spot checks performed this step; must be non-negative. n_ood Number of out-of-distribution triggers this step; must be non-negative.

add_records

add_records(records)

Append GK correction records to the session log.

Parameters

records Correction records to append; each must be a CorrectionRecord.

Raises

ValueError If any entry is not a CorrectionRecord.

add_correction_factor

add_correction_factor(factor)

Append a surrogate-to-GK correction factor.

Parameters

factor The correction factor; must be finite.

to_dict

to_dict()

Return a rounded summary of the session verification statistics.

to_json

to_json(path=None)

Serialise the report to JSON, optionally writing it to a file.

Parameters

path Optional destination path; when given the JSON is written there.

Returns

str The JSON text of the report summary.

Impurity Transport

impurity_transport

Impurity transport, tungsten accumulation, and radiative-loss diagnostic utilities.

ImpuritySpecies dataclass

ImpuritySpecies(element, Z_nucleus, mass_amu, source_rate=0.0)

An impurity species for transport modelling.

Attributes

element Element symbol (e.g. "W", "Ne"). Z_nucleus Nuclear charge number. mass_amu Atomic mass in amu. source_rate Edge source rate in particles/s.

CoolingCurve

CoolingCurve(element)

Parametric cooling rate coefficient L_Z(T_e) [W m³].

Log-normal fits to

W: Putterich et al. 2010, Nucl. Fusion 50, 025012, Fig. 3. C/Ar/Ne: Post et al. 1977, At. Data Nucl. Data Tables 20, 397.

L_z

L_z(Te_eV)

Return cooling coefficients for positive finite electron temperatures [W m³].

ImpurityTransportSolver

ImpurityTransportSolver(rho, R0, a, species)

Radial impurity-transport solver with diffusion and neoclassical pinch.

Parameters

rho Normalised-radius grid (resampled to an edge grid). R0 Major radius in metres; must be positive. a Minor radius in metres. species The impurity species to transport.

step

step(dt, ne, Te_eV, Ti_eV, D_anom, V_pinch)

1D transport advance for each impurity species.

Flux: Γ = -(D_anom + D_neo) dn/dr + V n Upwind differencing for convection; Crank-Nicolson implicit for diffusion.

neoclassical_impurity_pinch

neoclassical_impurity_pinch(Z, ne, Te_eV, Ti_eV, q, rho, R0, a, epsilon)

Neoclassical impurity pinch velocity V_neo [m/s].

Full formula (banana regime, trace impurity limit): V_neo,Z = -D_neo [Z ∇n/n + (Z/2 - H_Z) ∇T_i/T_i] where H_Z = 0.5 is the ion screening factor. Hirshman & Sigmar 1981, Nucl. Fusion 21, 1079, Eq. 4.17.

The thermal-gradient term dominates for peaked T_i profiles and produces an inward (negative) pinch when dT_i/dr < 0. Sign convention: V < 0 → inward (toward axis).

D_neo = q² ρ_i² ν_ii / ε^(3/2) (banana regime) Hinton & Hazeltine 1976, Rev. Mod. Phys. 48, 239, Eq. 4.62. Nominal scale D_neo ≈ 0.1 m²/s for ITER-relevant parameters.

total_radiated_power

total_radiated_power(ne, n_impurity, Te_eV, rho, R0, a)

Total radiated power P_rad [MW] integrated over plasma volume.

Local power density

p_rad(r) = n_e(r) n_Z(r) L_Z(T_e(r)) [W m^-3]

Post et al. 1977, At. Data Nucl. Data Tables 20, 397.

Volume element for circular cross-section torus

dV = 4π² R0 a² ρ dρ

tungsten_accumulation_diagnostic

tungsten_accumulation_diagnostic(n_W, ne)

Assess core tungsten accumulation from density profiles.

Parameters

n_W Tungsten density profile in m⁻³ (core to edge). ne Electron density profile in m⁻³ on the same grid.

Returns

dict[str, Any] Core and edge tungsten concentration, the peaking factor, and a "danger_level" of "safe", "warning", or "critical".

JAX GK Nonlinear

jax_gk_nonlinear

JAX-accelerated nonlinear δf gyrokinetic solver.

Wraps the same physics as gk_nonlinear.py but uses JAX for: - FFT-based E×B bracket (~100× over NumPy on GPU) - jax.checkpoint for memory-efficient RK4

Falls back to the NumPy solver when JAX is unavailable.

JaxNonlinearGKSolver

JaxNonlinearGKSolver(config=None, *, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

JAX-accelerated nonlinear δf solver.

Uses JAX for FFT, lax.scan for the time loop, and jax.checkpoint for RK4 memory efficiency. Falls back to NumPy when JAX is absent.

run

run(state=None)

Run the nonlinear gyrokinetic solver, JAX-accelerated when available.

Falls back to the NumPy solver when JAX is unavailable, unless the legacy fallback is disabled.

Parameters

state Optional initial solver state; a fresh state is initialised when None.

Returns

NonlinearGKResult The nonlinear gyrokinetic solution.

Raises

RuntimeError If JAX is unavailable and the NumPy fallback is disabled.

jax_available

jax_available()

Return whether JAX is importable in this environment.

JAX GK Solver

The linear JAX GK solver includes a schema-versioned parity artifact producer for backend reproducibility. build_jax_gk_parity_artifact() and write_jax_gk_parity_artifact() bind the native local-dispersion comparison, backend metadata, dtype/X64 state, solver kwargs, tolerances, and canonical payload SHA-256 digest while preserving the backend-parity-only claim boundary. Artifacts also bind case-parameter digests, native/JAX mode spectra, dominant mode labels, and case acceptance limits for CBC, kinetic-electron TEM, and low-drive stable-mode parity evidence. validation/validate_jax_gk_parity.py can require named cases and named backends before admitting an evidence directory, so archived single-case artifacts cannot be replayed as full parity coverage.

jax_gk_solver

JAX-accelerated linear GK eigenvalue solver.

Re-implements the native local-dispersion eigenvalue contract from gk_eigenvalue.py using jax.numpy for batched velocity-space quadrature and jax.grad for the transport-stiffness proxy d(chi_i)/d(R_L_Ti). JAX execution is explicit and fails closed when JAX is unavailable.

References

  • Dimits et al., Phys. Plasmas 7 (2000) 969 — Cyclone Base Case
  • Kotschenreuther et al., Comp. Phys. Comm. 88 (1995) 128

has_jax

has_jax()

Return whether JAX is importable in this environment.

solve_linear_gk_jax

solve_linear_gk_jax(species_list=None, geom=None, vgrid=None, R0=2.78, a=1.0, B0=2.0, q=1.4, s_hat=0.78, Z_eff=1.0, nu_star=0.01, n_ky_ion=16, n_theta=64)

JAX-accelerated linear GK solver. Batched over k_y via vmap.

Parameters match solve_linear_gk from gk_eigenvalue.py. Uses JAX to build the local-dispersion velocity integrals for all k_y simultaneously, then applies the same damped Newton root finder used by the native solver. This keeps backend parity on the physical growth/frequency path while preserving JAX acceleration for the high-volume quadrature assembly.

transport_stiffness_jax

transport_stiffness_jax(R_L_Ti, R0=2.78, a=1.0, B0=2.0, q=1.4, s_hat=0.78, Z_eff=1.0, nu_star=0.01, n_ky_ion=8, n_theta=32)

d(chi_i_proxy)/d(R_L_Ti) via jax.grad.

Returns the transport stiffness — sensitivity of the ion heat flux proxy to the normalised temperature gradient. Positive above critical gradient, near-zero below.

gk_stiffness_chi_i_profile_jax

gk_stiffness_chi_i_profile_jax(R_L_Ti, rho, base_chi_i=0.1, stiffness_scale=1e-06, R0=2.78, a=1.0, B0=2.0, q=1.4, s_hat=0.78, Z_eff=1.0, nu_star=0.01, n_ky_ion=8, n_theta=32)

Map JAX GK stiffness into a bounded ion heat diffusivity profile.

The closure is intentionally conservative: it uses the existing differentiable transport_stiffness_jax scalar as a local stiffness amplitude and applies a smooth edge-weighted radial shape. It is a controller-tuning closure, not a replacement for external GK validation.

build_jax_gk_parity_artifact

build_jax_gk_parity_artifact(*, case='cyclone_base_case', solver_kwargs=None, native_result=None, jax_result=None, gamma_relative_tolerance=0.25, omega_absolute_tolerance=0.25, executed_at=None)

Build a tamper-evident JAX/native gyrokinetic parity artifact.

The artifact is backend-parity evidence for the repository's native local-dispersion formulation. It is intentionally not a cross-code validation artifact and always records that external validation remains required before quantitative facility claims.

write_jax_gk_parity_artifact

write_jax_gk_parity_artifact(output_path, *, case='cyclone_base_case', solver_kwargs=None, gamma_relative_tolerance=0.25, omega_absolute_tolerance=0.25, executed_at=None)

Persist a JAX/native GK parity artifact and return payload plus path.

gk_stiffness_chi_i_profile_jax

gk_stiffness_chi_i_profile_jax(R_L_Ti, rho, base_chi_i=0.1, stiffness_scale=1e-06, R0=2.78, a=1.0, B0=2.0, q=1.4, s_hat=0.78, Z_eff=1.0, nu_star=0.01, n_ky_ion=8, n_theta=32)

Map JAX GK stiffness into a bounded ion heat diffusivity profile.

The closure is intentionally conservative: it uses the existing differentiable transport_stiffness_jax scalar as a local stiffness amplitude and applies a smooth edge-weighted radial shape. It is a controller-tuning closure, not a replacement for external GK validation.

build_jax_gk_parity_artifact

build_jax_gk_parity_artifact(*, case='cyclone_base_case', solver_kwargs=None, native_result=None, jax_result=None, gamma_relative_tolerance=0.25, omega_absolute_tolerance=0.25, executed_at=None)

Build a tamper-evident JAX/native gyrokinetic parity artifact.

The artifact is backend-parity evidence for the repository's native local-dispersion formulation. It is intentionally not a cross-code validation artifact and always records that external validation remains required before quantitative facility claims.

write_jax_gk_parity_artifact

write_jax_gk_parity_artifact(output_path, *, case='cyclone_base_case', solver_kwargs=None, gamma_relative_tolerance=0.25, omega_absolute_tolerance=0.25, executed_at=None)

Persist a JAX/native GK parity artifact and return payload plus path.

JAX GS Solver

jax_gs_solver

JAX-differentiable fixed-boundary Grad-Shafranov equilibrium solver.

Picard iteration with damped Jacobi inner sweeps, all in pure JAX for JIT compilation and automatic differentiation. Enables jax.grad through the full equilibrium solve — closing the autodiff depth gap with TORAX (JAX) and FUSE (Julia AD).

The GS* operator in cylindrical (R, Z) coordinates:

d²ψ/dR² - (1/R) dψ/dR + d²ψ/dZ² = -μ₀ R J_φ

where J_φ = R p'(ψ_norm) + FF'(ψ_norm) / (μ₀ R).

Key function

jax_gs_solve — Fixed-boundary GS equilibrium, differentiable via jax.grad

NumPy fallback provided when JAX is unavailable.

has_jax

has_jax()

Return whether JAX is importable in this environment.

gs_solve_np

gs_solve_np(R_min, R_max, Z_min, Z_max, NR, NZ, Ip_target, mu0=4e-07 * pi, n_picard=80, n_jacobi=200, alpha=0.1, omega_j=0.667, beta_mix=0.5)

Fixed-boundary GS solve via Picard iteration (NumPy fallback).

Returns psi on the (NZ, NR) grid with zero Dirichlet boundary.

jax_gs_solve

jax_gs_solve(R_min=0.1, R_max=2.0, Z_min=-1.5, Z_max=1.5, NR=33, NZ=33, Ip_target=1000000.0, mu0=4e-07 * pi, n_picard=80, n_jacobi=200, alpha=0.1, omega_j=0.667, beta_mix=0.5, *, use_jax=True, allow_numpy_fallback=False, allow_legacy_numpy_fallback=False)

Fixed-boundary Grad-Shafranov equilibrium solve.

Picard iteration with damped Jacobi inner sweeps. When JAX is available, the solve is JIT-compiled and differentiable via jax.grad. Falls back to NumPy otherwise.

Parameters

R_min, R_max : radial domain [m] Z_min, Z_max : vertical domain [m] NR, NZ : grid resolution Ip_target : target plasma current [A] mu0 : vacuum permeability [H/m] n_picard : outer Picard iterations (fixed count for differentiability) n_jacobi : inner Jacobi sweeps per Picard step alpha : Picard under-relaxation factor omega_j : Jacobi damping (2/3 standard, Hackbusch 1985 §4.3) beta_mix : pressure vs current profile weight use_jax : attempt JAX backend

Returns

psi : (NZ, NR) poloidal flux, zero on boundary

jax_gs_solve_from_grid

jax_gs_solve_from_grid(R_grid, psi_init, dR, dZ, Ip_target=1000000.0, mu0=4e-07 * pi, n_picard=80, n_jacobi=200, alpha=0.1, omega_j=0.667, beta_mix=0.5)

GS solve on a pre-existing grid. JAX-only, differentiable.

Parameters

R_grid : (NZ, NR) meshgrid of major radius values psi_init : (NZ, NR) initial flux with boundary conditions applied dR, dZ : grid spacing Ip_target : target plasma current [A]

jax_gs_grad_Ip

jax_gs_grad_Ip(Ip_target, R_min=0.1, R_max=2.0, Z_min=-1.5, Z_max=1.5, NR=33, NZ=33, mu0=4e-07 * pi, n_picard=40, n_jacobi=100, alpha=0.1, omega_j=0.667, beta_mix=0.5)

Compute d(sum(psi))/d(Ip_target) via JAX autodiff.

Demonstrates full-stack differentiability through the GS solve.

Kinetic EFIT

kinetic_efit

Kinetic-EFIT constraints and reconstruction wrapper for pressure and q-profile coupling.

FastIonPressure

FastIonPressure(E_fast_keV, n_fast_frac, anisotropy_sigma=0.0)

Anisotropic fast ion pressure model.

p_perp

p_perp(rho, ne_19)

Perpendicular fast-ion pressure profile.

Parameters

rho Normalised-radius grid. ne_19 Electron density in 10¹⁹ m⁻³ on rho.

Returns

AnyFloatArray Perpendicular fast-ion pressure in pascals.

p_par

p_par(rho, ne_19)

Parallel fast-ion pressure profile.

Parameters

rho Normalised-radius grid. ne_19 Electron density in 10¹⁹ m⁻³ on rho.

Returns

AnyFloatArray Parallel fast-ion pressure in pascals, p_perp (1 - sigma).

p_isotropic_equivalent

p_isotropic_equivalent(rho, ne_19)

Isotropic-equivalent scalar fast-ion pressure (2 p_perp + p_par)/3.

Parameters

rho Normalised-radius grid. ne_19 Electron density in 10¹⁹ m⁻³ on rho.

Returns

AnyFloatArray Scalar fast-ion pressure in pascals.

KineticConstraints dataclass

KineticConstraints(Te_points, ne_points, Ti_points, mse_points)

Measured kinetic constraints for kinetic-EFIT reconstruction.

Attributes

Te_points Electron-temperature constraints as (R, Z, Te_keV) tuples. ne_points Electron-density constraints as (R, Z, ne_19) tuples. Ti_points Ion-temperature constraints as (R, Z, Ti_keV) tuples. mse_points Motional-Stark-effect constraints as (R, Z, pitch_angle_deg) tuples.

KineticReconstructionResult dataclass

KineticReconstructionResult(psi, p_prime_coeffs, ff_prime_coeffs, shape, chi_squared, n_iterations, wall_time_ms, p_kinetic, p_equilibrium, pressure_consistency, q_profile, beta_fast, sigma_anisotropy, *, coil_currents=None)

Bases: ReconstructionResult

Kinetic-EFIT result extending the magnetic reconstruction.

Attributes

p_kinetic Kinetic pressure profile in pascals. p_equilibrium Equilibrium pressure profile in pascals. pressure_consistency Relative agreement between the kinetic and equilibrium pressure. q_profile Safety-factor profile. beta_fast Fast-ion beta fraction. sigma_anisotropy Fast-ion anisotropy 1 - p_par/p_perp profile.

KineticEFITClaimEvidence dataclass

KineticEFITClaimEvidence(schema_version, source, source_id, diagnostic_source, profile_source, fast_ion_source, mse_calibration_source, model_id, interpolation_geometry, n_te_points, n_ne_points, n_ti_points, n_mse_points, fast_ion_energy_keV, fast_ion_density_fraction, anisotropy_sigma, pressure_consistency, beta_fast, q_axis, q_edge, pressure_relative_error, q_profile_relative_error, anisotropy_abs_error, pressure_relative_tolerance, q_profile_relative_tolerance, anisotropy_abs_tolerance, facility_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for kinetic-EFIT claims.

KineticEFIT

KineticEFIT(diagnostics, kinetic, fast_ions, R_grid, Z_grid)

Bases: RealtimeEFIT

Kinetic equilibrium reconstruction adding kinetic and MSE constraints.

Extends the magnetic :class:RealtimeEFIT with kinetic-profile and fast-ion pressure constraints for a kinetic-EFIT-style reconstruction.

Parameters

diagnostics Magnetic diagnostic set for the base reconstruction. kinetic Measured kinetic constraints. fast_ions Fast-ion pressure model. R_grid Major-radius grid in metres. Z_grid Vertical grid in metres.

reconstruct

reconstruct(measurements, *, coils=None, mode='psi_n', max_iter=25, tol=1e-05, regularization=1e-09, rel_sigma=0.02)

Reconstruct the equilibrium with kinetic and fast-ion constraints.

Runs the magnetic least-squares reconstruction (forwarding the inverse options to :meth:RealtimeEFIT.reconstruct), then folds in the kinetic pressure profiles and the fast-ion pressure to produce a kinetic equilibrium.

Parameters

measurements Magnetic and kinetic measurement payload for this reconstruction. coils, mode, max_iter, tol, regularization, rel_sigma Magnetic-inverse options forwarded to the base EFIT reconstruction; coils selects the free-boundary inverse (fits the coil currents).

Returns

KineticReconstructionResult The kinetic reconstruction with pressure consistency, q-profile, and fast-ion beta and anisotropy.

kinetic_efit_claim_evidence

kinetic_efit_claim_evidence(result, kinetic, fast_ions, *, source, source_id, diagnostic_source, profile_source, fast_ion_source, mse_calibration_source, model_id='bounded_kinetic_efit', reference_pressure=None, reference_q_profile=None, reference_anisotropy_sigma=None, pressure_relative_tolerance=0.05, q_profile_relative_tolerance=0.05, anisotropy_abs_tolerance=0.05)

Build fail-closed kinetic-EFIT evidence for bounded or facility claims.

Facility claims require a recognised facility reference source plus matched pressure, q-profile, and anisotropy references inside declared tolerances. Synthetic regression evidence is retained as bounded controller evidence only and is never promoted to a facility-grade P-EFIT claim.

assert_kinetic_efit_facility_claim_admissible

assert_kinetic_efit_facility_claim_admissible(evidence)

Raise when kinetic-EFIT evidence is insufficient for a facility claim.

save_kinetic_efit_claim_evidence

save_kinetic_efit_claim_evidence(evidence, path)

Persist kinetic-EFIT claim evidence as deterministic JSON.

mse_pitch_angle

mse_pitch_angle(B_R, B_Z, B_phi, v_beam, R)

Forward motional-Stark-effect pitch-angle estimate for a tangential beam.

KineticEFITClaimEvidence dataclass

KineticEFITClaimEvidence(schema_version, source, source_id, diagnostic_source, profile_source, fast_ion_source, mse_calibration_source, model_id, interpolation_geometry, n_te_points, n_ne_points, n_ti_points, n_mse_points, fast_ion_energy_keV, fast_ion_density_fraction, anisotropy_sigma, pressure_consistency, beta_fast, q_axis, q_edge, pressure_relative_error, q_profile_relative_error, anisotropy_abs_error, pressure_relative_tolerance, q_profile_relative_tolerance, anisotropy_abs_tolerance, facility_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for kinetic-EFIT claims.

kinetic_efit_claim_evidence

kinetic_efit_claim_evidence(result, kinetic, fast_ions, *, source, source_id, diagnostic_source, profile_source, fast_ion_source, mse_calibration_source, model_id='bounded_kinetic_efit', reference_pressure=None, reference_q_profile=None, reference_anisotropy_sigma=None, pressure_relative_tolerance=0.05, q_profile_relative_tolerance=0.05, anisotropy_abs_tolerance=0.05)

Build fail-closed kinetic-EFIT evidence for bounded or facility claims.

Facility claims require a recognised facility reference source plus matched pressure, q-profile, and anisotropy references inside declared tolerances. Synthetic regression evidence is retained as bounded controller evidence only and is never promoted to a facility-grade P-EFIT claim.

assert_kinetic_efit_facility_claim_admissible

assert_kinetic_efit_facility_claim_admissible(evidence)

Raise when kinetic-EFIT evidence is insufficient for a facility claim.

save_kinetic_efit_claim_evidence

save_kinetic_efit_claim_evidence(evidence, path)

Persist kinetic-EFIT claim evidence as deterministic JSON.

L-H Transition

lh_transition

L-H transition trigger and predator-prey edge-transition controller utilities.

PredatorPreyResult dataclass

PredatorPreyResult(epsilon_trace, V_ZF_trace, p_trace, time, regime)

Time traces from a predator-prey L–H transition simulation.

Attributes

epsilon_trace Turbulence-intensity trace, one sample per step. V_ZF_trace Zonal-flow shear trace, one sample per step. p_trace Pressure-gradient trace, one sample per step. time Time grid in seconds. regime Final confinement regime label (e.g. "L", "I-phase", "H").

PredatorPreyModel

PredatorPreyModel(gamma_L=_KD_GAMMA_L, alpha1=_KD_ALPHA1, alpha2=_KD_ALPHA2, alpha3=_KD_ALPHA3, gamma_damp=_KD_GAMMA_DAMP)

Zonal-flow turbulence model — Kim & Diamond (2003).

confinement_time

confinement_time(epsilon)

Energy confinement time degraded by turbulence intensity.

Parameters

epsilon Turbulence-intensity level; must be non-negative.

Returns

float Confinement time τ_E0 / (1 + C ε) in seconds.

zonal_flow_growth_rate

zonal_flow_growth_rate()

Characteristic ZF growth rate γ_ZF ≡ α₃ / (α₁ γ_damp) * γ_L² — Kim & Diamond (2003), Eq. 6.

Used by IPhaseFrequency to estimate the I-phase limit-cycle frequency.

step

step(state, dt, Q_heating)

Advance the predator-prey state one explicit Euler step.

Parameters

state Three-element state [epsilon, V_ZF, pressure]; non-negative. dt Time step in seconds; must be positive. Q_heating Heating power source term; must be non-negative.

Returns

FloatArray The updated [epsilon, V_ZF, pressure] state, floored at zero.

evolve

evolve(Q_heating, t_span, dt)

Integrate the predator-prey model over a time span.

Parameters

Q_heating Constant heating power; must be non-negative. t_span (t_start, t_end) in seconds with t_end > t_start. dt Time step in seconds; must be positive and at most the span.

Returns

PredatorPreyResult The turbulence, zonal-flow, and pressure traces and the final regime.

LHTrigger

LHTrigger(model)

L→H transition power-threshold finder for the predator-prey model.

Parameters

model The predator-prey turbulence model to probe.

find_threshold

find_threshold(Q_range)

Bisect Q_range to find onset of L→H bifurcation.

MartinThreshold

L-H power threshold scaling — Martin et al. (2008).

power_threshold_MW staticmethod

power_threshold_MW(ne_19, B_T, S_m2)

P_LH [MW] via Martin scaling.

Martin et al., J. Phys.: Conf. Ser. 123, 012033 (2008), Eq. (1).

Parameters

ne_19: Line-averaged electron density in units of 10¹⁹ m⁻³. B_T: Toroidal field in T. S_m2: Plasma surface area in m².

Notes

The published fit uses n₂₀ = n_e / 10²⁰ m⁻³. Since ne_19 is in 10¹⁹ m⁻³, n₂₀ = ne_19 / 10.

power_threshold_with_low_density_branch_MW staticmethod

power_threshold_with_low_density_branch_MW(ne_19, B_T, S_m2, I_p_MA, a_m)

P_LH including the low-density upswing branch.

Below n_min ≈ 0.4 n_GW the threshold increases as (n_min/n_e)² (Ryter et al., Nucl. Fusion 54, 083003 (2014), Fig. 3).

Parameters

ne_19: Line-averaged electron density in units of 10¹⁹ m⁻³. B_T: Toroidal field in T. S_m2: Plasma surface area in m². I_p_MA: Plasma current in MA (for Greenwald density). a_m: Minor radius in m (for Greenwald density).

Notes

Greenwald density: n_GW [10²⁰ m⁻³] = I_p [MA] / (π a²) [MA m⁻²]. Converted to 10¹⁹ m⁻³: n_GW_19 = 10 * I_p / (π a²).

IPhaseDetector

IPhaseDetector(window_size=100)

Detects I-phase limit-cycle oscillations in turbulence traces.

detect

detect(epsilon_trace)

Return True if the recent trace shows relative std > 10 %.

IPhaseFrequency

I-phase limit-cycle frequency estimate.

Tynan et al., Nucl. Fusion 53, 073053 (2013), Sec. 4. f_I ≈ γ_ZF / (2π), where γ_ZF is the zonal-flow linear growth rate evaluated at the turbulent saturation level.

estimate_hz staticmethod

estimate_hz(model)

Return f_I in Hz.

LHTransitionController

LHTransitionController(model, Q_target)

Heating-power controller that drives and holds the L→H transition.

Parameters

model The predator-prey turbulence model. Q_target Target heating power; must be non-negative.

step

step(epsilon_measured, Q_current, dt)

Ramp Q toward Q_target; hold when H-mode is confirmed.

Locked Mode

locked_mode

Error-field amplification, mode locking, and post-lock island growth chain.

Key references

La Haye 2006 : R.J. La Haye, Phys. Plasmas 13, 055501 (2006). [RFA formula Eq. 6; locking condition Eq. 12; MRE in locked-mode regime Eq. 8; C_lock coefficient] Fitzpatrick 1993: R. Fitzpatrick, Nucl. Fusion 33, 1049 (1993). [electromagnetic torque Eq. 28; locking criterion] Rutherford 1973 : P.H. Rutherford, Phys. Fluids 16, 1903 (1973). [classical tearing stability dw/dt ∝ Delta']

ErrorFieldSpectrum

ErrorFieldSpectrum(B0, n_corrections=0)

Error-field harmonic spectrum b_mn for a tokamak.

Holds the resonant error-field components in tesla, seeded from intrinsic estimates (La Haye 2006, §II) and adjustable by coil misalignment or active correction.

Parameters

B0 Toroidal field on axis in tesla; must be positive. n_corrections Number of active correction coils; must be non-negative.

set_coil_misalignment

set_coil_misalignment(delta_R_mm, delta_Z_mm)

Set the (2,1) and (3,2) error fields from a coil centroid shift.

The error-field amplitude scales linearly with the displacement magnitude (La Haye 2006, §II).

Parameters

delta_R_mm Radial coil misalignment in millimetres; must be non-negative. delta_Z_mm Vertical coil misalignment in millimetres; must be non-negative.

B_mn

B_mn(m, n)

Return the (m, n) error-field component in tesla.

Parameters

m Poloidal mode number; must be positive. n Toroidal mode number; must be positive.

Returns

float The error-field amplitude in tesla (0.0 if the harmonic is absent).

corrected_B_mn

corrected_B_mn(m, n, I_correction)

Return the (m, n) error field after active-coil correction.

Applies a first-order linear reduction proportional to the correction current, floored at zero.

Parameters

m Poloidal mode number. n Toroidal mode number. I_correction Correction-coil current in amperes; must be non-negative.

Returns

float The corrected error-field amplitude in tesla.

ResonantFieldAmplification

ResonantFieldAmplification(beta_N, beta_N_nowall)

Ideal-MHD resonant-field amplification (RFA) model.

B_res = B_err / (1 - beta_N / beta_N_nowall)

La Haye 2006, Phys. Plasmas 13, 055501, Eq. 6. Denominator → 0 as beta_N → beta_N_nowall (no-wall stability boundary).

amplification_factor

amplification_factor()

RFA factor; La Haye 2006, Eq. 6.

resonant_field

resonant_field(B_err)

Amplified resonant field at the rational surface.

Parameters

B_err Applied error-field amplitude in tesla; must be non-negative.

Returns

float The resonant field in tesla, B_err times the RFA factor.

RotationEvolution dataclass

RotationEvolution(omega_trace, locked, lock_time)

Toroidal-rotation evolution under electromagnetic braking.

Attributes

omega_trace Toroidal angular-rotation trace in rad/s, one sample per step. locked True if the mode locked within the simulated steps. lock_time Time of locking in seconds, or -1 if the mode did not lock.

ModeLocking

ModeLocking(R0, a, B0, Ip_MA, omega_phi_0, ne_19=None, kappa=1.7, m_i_kg=3.3e-27)

Toroidal rotation under electromagnetic braking from a resonant error field.

Electromagnetic torque (per unit toroidal angle): T_em = -n² m ψ_ext² sin(Δφ) / (μ₀ R r_s²) Fitzpatrick 1993, Nucl. Fusion 33, 1049, Eq. 28.

Mode-locking condition

|T_em| > T_viscous where T_viscous = ρ χ_φ ω V_island

La Haye 2006, Phys. Plasmas 13, 055501, Eq. 12.

In steady-state the mode locks when the electromagnetic drag exceeds the viscous restoring torque that maintains plasma rotation.

em_torque

em_torque(B_res, r_s, m, n)

Electromagnetic braking torque on the (m, n) mode.

Derived from Fitzpatrick 1993, Nucl. Fusion 33, 1049, Eq. 28: T_em = -n² m ψ_ext² sin(Δφ) / (μ₀ R r_s²) where ψ_ext ~ B_res × r_s² / n (flux loop estimate) and sin(Δφ) → 1 at the locking threshold. The sign convention here gives a positive magnitude; the caller interprets the torque as opposing rotation.

viscous_torque

viscous_torque(omega, r_s, rho_chi=1.0)

Viscous restoring torque.

T_viscous = ρ χ_φ ω V_island La Haye 2006, Phys. Plasmas 13, 055501, Eq. 12.

rho_chi = ρ × χ_φ [kg m⁻¹ s⁻¹], V_island ~ 4 π² R₀ r_s (shell volume at the rational surface, thin-shell limit).

evolve_rotation

evolve_rotation(B_res, r_s, tau_visc, dt, n_steps)

Integrate dω/dt = -(ω - ω₀)/τ_visc - T_em / I_eff.

Mode locks when ω ≤ ω_eq (equilibrium with sustained braking). Locking condition: T_em > T_viscous (La Haye 2006, Eq. 12).

IslandGrowth dataclass

IslandGrowth(w_trace, overlap_time, stochastic)

Locked-island width evolution from the Modified Rutherford Equation.

Attributes

w_trace Island half-width trace in metres, one sample per step. overlap_time Time at which the island exceeds the stochastic-overlap threshold in seconds, or -1 if never reached. stochastic True if island overlap (stochastic field) was reached.

LockedModeIsland

LockedModeIsland(r_s, m, n, a, R0, delta_prime)

Post-locking island growth via the Modified Rutherford Equation.

dw/dt = (η / μ₀ r_s) [r_s Δ' + C_lock r_s / w]

La Haye 2006, Phys. Plasmas 13, 055501, Eq. 8. C_lock accounts for the locked-mode bootstrap drive (same physical origin as the NTM bootstrap term but evaluated at ω = 0). Classical tearing drive: Rutherford 1973, Phys. Fluids 16, 1903.

grow

grow(w0, eta, dt, n_steps, delta_r_mn=0.3)

Integrate MRE for a locked island; stochastic when w > delta_r_mn.

dw/dt = (η / μ₀ r_s) [r_s Δ' + C_lock r_s / w] La Haye 2006, Phys. Plasmas 13, 055501, Eq. 8.

ChainResult dataclass

ChainResult(lock_time, island_width_at_tq, tq_trigger_time, disruption, warning_time_ms)

Outcome of the error-field-to-disruption chain.

Attributes

lock_time Mode-locking time in seconds, or -1 if no lock occurred. island_width_at_tq Island half-width at the thermal-quench trigger in metres. tq_trigger_time Thermal-quench trigger time in seconds, or -1 if no disruption. disruption True if the chain reached a disruption. warning_time_ms Warning time between island overlap and quench in milliseconds, or -1 if no disruption.

ErrorFieldToDisruptionChain

ErrorFieldToDisruptionChain(config)

Full error-field → RFA → locking → island-growth → disruption chain.

Parameters

config Machine and plasma parameters (R0, a, B0, Ip_MA, beta_N, beta_N_nowall); missing keys fall back to ITER-like defaults.

run

run(B_err_n1, omega_phi_0)

Run the chain from an applied n=1 error field to disruption.

Amplifies the error field (RFA), evolves the rotation to locking, then grows the locked island until stochastic overlap triggers a thermal quench. Short-circuits to a non-disruptive result if locking or island overlap is not reached.

Parameters

B_err_n1 Applied n=1 error-field amplitude in tesla. omega_phi_0 Initial toroidal angular rotation in rad/s.

Returns

ChainResult The locking time, island width, and disruption outcome.

MARFE

marfe

MARFE radiation-condensation and density-limit stability utilities.

RadiationCondensation

RadiationCondensation(impurity, ne_20, f_imp)

Radiation-condensation (MARFE precursor) instability model.

Evaluates the local thermal-radiative instability growth rate and the critical density from the impurity cooling curve (Drake 1987, Phys. Fluids 30, 2429).

Parameters

impurity Impurity species symbol for the cooling curve. ne_20 Electron density in 10²⁰ m⁻³; must be positive. f_imp Impurity fraction relative to electron density, in (0, 1].

growth_rate

growth_rate(Te_eV, k_par, kappa_par)

Radiation condensation growth rate.

γ = -(κ_∥ k_∥² + n_e n_Z dL/dT) / (n c_v)

Positive γ ↔ unstable: requires dL/dT < 0 and n_e n_Z |dL/dT| > κ_∥ k_∥². Drake 1987, Phys. Fluids 30, 2429, Eq. 5.

is_unstable

is_unstable(Te_eV, k_par, kappa_par)

Return True when dL/dT < 0 and the radiation term exceeds parallel-conduction damping.

onset_temperature

onset_temperature(Te_scan)

Estimate T_MARFE as the highest T where dL_Z/dT first turns negative.

Lipschultz 1987, J. Nucl. Mater. 145-147, 15: MARFE forms near the temperature where the radiative cooling function has a negative slope. Returns the onset T in eV, or nan if dL/dT is positive everywhere.

critical_density

critical_density(Te_eV, k_par, kappa_par)

n_crit where γ = 0.

From Drake 1987, Eq. 5 with γ = 0: n_e² f_imp |dL/dT| = κ_∥ k_∥²

MARFEFrontModel

MARFEFrontModel(L_par, kappa_par, q_perp, impurity, f_imp)

1-D parallel heat-front model of MARFE formation along a field line.

Integrates (3/2) n ∂_t T = κ_∥ ∂_s² T − n_e n_Z L_Z(T) + q_⊥ on a field-aligned grid from the core boundary to the X-point, where impurity radiation can condense into a cold front.

Parameters

L_par Parallel connection length in metres; must be positive. kappa_par Parallel heat conductivity in W m⁻¹ eV⁻¹; must be positive. q_perp Perpendicular heat input per unit volume; must be non-negative. impurity Impurity species symbol for the cooling curve. f_imp Impurity fraction relative to electron density, in (0, 1].

step

step(dt, ne_20)

Advance the front temperature one implicit diffusion step.

Parameters

dt Time step in seconds; must be positive. ne_20 Electron density in 10²⁰ m⁻³; must be positive.

Returns

FloatArray The updated parallel temperature profile in eV, shape (n_s,), floored at 1 eV.

equilibrium

equilibrium(ne_20)

Relax the front to steady state over a fixed number of steps.

Parameters

ne_20 Electron density in 10²⁰ m⁻³; must be positive.

Returns

FloatArray The relaxed parallel temperature profile in eV, shape (n_s,).

is_marfe

is_marfe()

MARFE criterion: localised cold spot with T_min < 20 eV and T_max > 50 eV.

Lipschultz 1987, J. Nucl. Mater. 145-147, 15.

DensityLimitPredictor

Greenwald and heuristic MARFE-onset density limits.

greenwald_limit staticmethod

greenwald_limit(Ip_MA, a)

n_GW = I_p / (π a²) [10^20 m^-3].

Greenwald 2002, Plasma Phys. Control. Fusion 44, R27, Eq. 1. I_p in MA, a in m.

marfe_limit staticmethod

marfe_limit(Ip_MA, a, P_SOL_MW, impurity, f_imp)

Heuristic MARFE-onset density tied to the Greenwald limit.

n_marfe ~ n_GW * sqrt(P_SOL) / (10 * sqrt(f_imp)) Scaling motivated by Drake 1987 radiation condensation criterion with parallel conduction set by P_SOL and impurity fraction f_imp.

MARFEStabilityDiagram

MARFEStabilityDiagram(R0, a, q95, impurity)

MARFE stability map over the density–SOL-power plane for a device.

Parameters

R0 Major radius in metres; must be positive. a Minor radius in metres; must be positive and below R0. q95 Edge safety factor q95; must be positive. impurity Impurity species symbol for the cooling curve.

connection_length_m property

connection_length_m

Approximate edge parallel connection length L_parallel = pi q95 R0.

scan_density_power

scan_density_power(ne_range, P_SOL_range)

Map MARFE stability over a density and SOL-power grid.

Parameters

ne_range Strictly increasing electron densities in 10²⁰ m⁻³. P_SOL_range Strictly increasing scrape-off-layer powers in MW.

Returns

FloatArray A (len(ne_range), len(P_SOL_range)) map: +1 where the plasma is MARFE-stable and -1 where MARFE onset is predicted.

MDSplus Acquisition

mdsplus_acquisition

Optional MDSplus shot acquisition with manifest provenance output.

MDSplusSignalSpec dataclass

MDSplusSignalSpec(name, node, units, timebase)

Single MDSplus signal acquisition request.

MDSplusAcquisitionRequest dataclass

MDSplusAcquisitionRequest(tree, shot, source_uri, access_policy, licence, signals)

Versioned MDSplus acquisition request loaded from JSON.

MDSplusAcquisitionResult dataclass

MDSplusAcquisitionResult(output_npz, manifest_json, dataset_id, checksum_sha256)

Paths and manifest identity produced by an acquisition run.

load_mdsplus_acquisition_request

load_mdsplus_acquisition_request(path)

Load a versioned MDSplus acquisition request JSON file.

acquire_mdsplus_shot

acquire_mdsplus_shot(*, tree, shot, signals, output_npz, manifest_json, source_uri, access_policy, licence, retrieved_at=None, mdsplus_module=None)

Acquire selected signals from MDSplus, write NPZ, and emit a manifest.

Momentum Transport

momentum_transport

Toroidal momentum-transport and rotation-profile evolution utilities.

RotationDiagnostics

Toroidal-rotation diagnostics: Mach number and RWM stabilisation.

mach_number staticmethod

mach_number(omega_phi, Ti_keV, R0)

Toroidal sonic Mach number |v_phi| / c_s.

Parameters

omega_phi Toroidal angular-rotation profile in rad/s. Ti_keV Ion-temperature profile in keV (same shape as omega_phi); must be positive. R0 Major radius in metres; must be positive.

Returns

FloatArray The sonic Mach-number profile.

rwm_stabilization_criterion staticmethod

rwm_stabilization_criterion(omega_phi, tau_wall)

Whether the rotation satisfies the RWM wall-stabilisation criterion.

Applies the ω τ_wall > O(1) condition (Bondeson & Ward 1994).

Parameters

omega_phi Toroidal angular-rotation profile in rad/s; must be non-empty. tau_wall Resistive-wall time in seconds; must be positive.

Returns

bool True when the core rotation stabilises the resistive-wall mode.

MomentumTransportSolver

MomentumTransportSolver(rho, R0, a, B0, prandtl=PRANDTL_MOMENTUM)

Toroidal angular-momentum transport solver with a Prandtl closure.

Parameters

rho Normalised-radius grid (resampled to an edge grid). R0 Major radius in metres; must be positive. a Minor radius in metres; must be positive. B0 Toroidal field on axis in tesla; must be positive. prandtl Momentum Prandtl number χ_φ / χ_i (Peeters 2011).

step

step(dt, chi_i, ne, Ti_keV, T_nbi, T_intrinsic, *, momentum_damping_frequency_s=None)

Advance rotation profile one step [rad/s].

Momentum equation (cylindrical approximation): ∂(ρ_m R² ω)/∂t + (1/r) ∂/∂r(r Π_φ) = T_tot − ν_φ ρ_m R² ω Π_φ = −χ_φ ∂(ρ_m R² ω)/∂r (pinch neglected) Wesson 2011, "Tokamaks", §4.10. The optional ν_φ profile is a non-negative bounded linear momentum damping frequency [s⁻¹], treated implicitly so zero-source, zero-diffusion cells decay as ωⁿ⁺¹ = ωⁿ / (1 + Δt ν_φ).

nbi_torque

nbi_torque(P_nbi_profile, R0, v_beam, theta_inj_deg)

NBI torque density [N·m/m³].

T_NBI = P_NBI · R_tang / v_beam where R_tang = R₀ sin(θ). Stacey & Sigmar 1985, Phys. Fluids 28, 2800.

intrinsic_rotation_torque

intrinsic_rotation_torque(grad_Ti, grad_ne, R0, a)

Residual-stress torque driving intrinsic counter-current rotation [N·m/m³].

Residual stress Π_res ∝ −∇T_i → counter-current rotation. Empirical Rice scaling: v_φ,intr ∝ W_p / I_p. Rice et al. 2007, Nucl. Fusion 47, 1618, Eq. 3.

exb_shearing_rate

exb_shearing_rate(omega_phi, B_theta, B0, R0, rho, a)

E×B shearing rate [rad/s].

ω_E×B = (R B_θ / B) · d/dr (E_r / R B_θ) Burrell 1997, Phys. Plasmas 4, 1499, Eq. 1.

In the rotation-dominated limit E_r ≈ v_φ B_θ = R₀ ω_φ B_θ, so E_r / (R₀ B_θ) ≈ ω_φ and ω_E×B ≈ (R₀ B_θ / B) · dω_φ/dr.

turbulence_suppression_factor

turbulence_suppression_factor(omega_ExB, gamma_max)

Suppression factor on anomalous transport.

F = 1 / (1 + (ω_E×B / γ_max)²) Biglari, Diamond & Terry 1990, Phys. Fluids B 2, 1.

radial_electric_field

radial_electric_field(ne, Ti_keV, omega_phi, B_theta, B0, R0, rho, a)

E_r [V/m] from radial force balance (neoclassical), Z_i = 1.

E_r = (1 / Z_i e n_i) dp_i/dr + v_φ B_θ (v_θ ≈ 0) Hinton & Hazeltine 1976, Rev. Mod. Phys. 48, 239, Eq. 2.3.

rice_intrinsic_velocity

rice_intrinsic_velocity(W_p_MJ, I_p_MA)

Intrinsic toroidal velocity [km/s] from Rice scaling.

v_φ,intr ≈ c_Rice · (W_p / I_p) Rice et al. 2007, Nucl. Fusion 47, 1618, Eq. 3.

c_Rice = 3.5 km s⁻¹ MA MJ⁻¹ (empirical, Ohmic + NBI plasmas, Fig. 4).

Neoclassical

neoclassical

Neoclassical transport and bootstrap current models.

Implements Chang-Hinton ion thermal diffusivity, Pfirsch-Schlüter diffusivity, collisionality regime auto-detection, and the full three-coefficient Sauter bootstrap current model for general axisymmetry.

Key references

Chang & Hinton 1982 : C.S. Chang, F.L. Hinton, Phys. Fluids 25, 1493 (1982). Hinton & Hazeltine 1976 : F.L. Hinton, R.D. Hazeltine, Rev. Mod. Phys. 48, 239 (1976). Sauter 1999 : O. Sauter et al., Phys. Plasmas 6, 2834 (1999). Wesson 2011 : J. Wesson, "Tokamaks", 4th ed., Oxford (2011). Galeev & Sagdeev 1979 : A.A. Galeev, R.Z. Sagdeev, in Reviews of Plasma Physics, vol. 7 (1979).

collisionality

collisionality(n_e_19, T_kev, q, R, epsilon, mass_amu=2.0, z_eff=1.0)

Dimensionless neoclassical collisionality nu_star.

nu_star = nu_ii * q * R / (epsilon^1.5 * v_th)

Hinton & Hazeltine 1976, Eq. 4.53.

Parameters

n_e_19 : float Electron density [10^19 m^-3]. T_kev : float Species temperature [keV]. q : float Safety factor. R : float Major radius [m]. epsilon : float Inverse aspect ratio r/R. mass_amu : float Species mass in AMU (default 2.0 for D). z_eff : float Effective charge.

Returns

float Dimensionless collisionality.

chang_hinton_chi

chang_hinton_chi(q, epsilon, nu_star, rho_i, nu_ii)

Chang-Hinton banana-regime ion thermal diffusivity.

Chang & Hinton 1982, Phys. Fluids 25, 1493, Eq. 10.

Parameters

q : float Safety factor. epsilon : float Inverse aspect ratio r/R. nu_star : float Ion collisionality. rho_i : float Ion Larmor radius [m]. nu_ii : float Ion-ion collision frequency [s^-1].

Returns

float Ion thermal diffusivity [m^2/s].

plateau_chi

plateau_chi(q, rho_i, v_thi, R)

Plateau-regime ion thermal diffusivity.

Valid for 1 < nu_star < q^2 / epsilon^1.5. Hinton & Hazeltine 1976, Rev. Mod. Phys. 48, 239, Eq. 4.66.

Parameters

q : float Safety factor. rho_i : float Ion Larmor radius [m]. v_thi : float Ion thermal velocity [m/s]. R : float Major radius [m].

Returns

float Ion thermal diffusivity [m^2/s].

pfirsch_schluter_chi

pfirsch_schluter_chi(q, rho_i, nu_ii)

Pfirsch-Schlüter regime ion thermal diffusivity.

Valid for nu_star > q^2 / epsilon^1.5 (collisional regime). Wesson 2011, "Tokamaks" 4th ed., Eq. 14.5.7. Galeev & Sagdeev 1979, Reviews of Plasma Physics, vol. 7.

Parameters

q : float Safety factor. rho_i : float Ion Larmor radius [m]. nu_ii : float Ion-ion collision frequency [s^-1].

Returns

float Ion thermal diffusivity [m^2/s].

neoclassical_chi

neoclassical_chi(nu_star, q, epsilon, rho_i, nu_ii, v_thi, R)

Neoclassical ion thermal diffusivity with automatic regime selection.

Regime boundaries follow Hinton & Hazeltine 1976, Rev. Mod. Phys. 48, 239, Section IV and Wesson 2011, Ch. 14.5: - nu_star < epsilon^1.5 : banana (Chang & Hinton 1982, Eq. 10) - epsilon^1.5 <= nu_star < q^2/epsilon^1.5 : plateau (Hinton & Hazeltine 1976, Eq. 4.66) - nu_star >= q^2/epsilon^1.5 : Pfirsch-Schlüter (Wesson 2011, Eq. 14.5.7)

Parameters

nu_star : float Dimensionless collisionality. q : float Safety factor. epsilon : float Inverse aspect ratio r/R. rho_i : float Ion Larmor radius [m]. nu_ii : float Ion-ion collision frequency [s^-1]. v_thi : float Ion thermal velocity [m/s]. R : float Major radius [m].

Returns

float Ion thermal diffusivity [m^2/s].

banana_plateau_chi

banana_plateau_chi(q, epsilon, nu_star, z_eff)

Banana-plateau neoclassical diffusivity with Z_eff dependence.

Hinton & Hazeltine 1976, Rev. Mod. Phys. 48, 239, Eq. 4.61.

Parameters

q : float Safety factor. epsilon : float Inverse aspect ratio. nu_star : float Collisionality. z_eff : float Effective charge.

Returns

float Ion thermal diffusivity (dimensionless units, normalised form).

sauter_bootstrap

sauter_bootstrap(rho, Te, Ti, ne, q, R0, a, B0=5.3, z_eff=1.5)

Sauter bootstrap current density profile with full L31+L32+L34 coefficients.

Sauter et al. 1999, Phys. Plasmas 6, 2834, Eqs. 14–16.

j_bs = -(p_e / B_pol) * [L31 * d(ln p_e)/dr + L32 * d(ln T_e)/dr + L34 * (T_i/T_e) * d(ln T_i)/dr]

Parameters

rho : AnyFloatArray Normalised radial grid. Te, Ti : AnyFloatArray Electron and ion temperatures [keV]. ne : AnyFloatArray Electron density [10^19 m^-3]. q : AnyFloatArray Safety factor profile. R0, a : float Major and minor radii [m]. B0 : float Toroidal magnetic field [T]. z_eff : float Effective charge.

Returns

AnyFloatArray Bootstrap current density [A/m^2].

Neural Turbulence

neural_turbulence

QLKNN-class neural turbulence surrogate, training, and flux prediction utilities.

NeuralTurbulenceClaimEvidence dataclass

NeuralTurbulenceClaimEvidence(schema_version, model_id, source, source_id, weights_path, weights_sha256, feature_schema, local_sample_count, local_q_i_rmse_gB, local_q_e_rmse_gB, local_gamma_e_rmse_gB, local_flux_relative_mae, local_critical_gradient_accuracy, reference_source, reference_dataset_id, reference_artifact_sha256, reference_sample_count, q_i_rmse_gB, q_e_rmse_gB, gamma_e_rmse_gB, flux_relative_mae, critical_gradient_accuracy, q_i_rmse_tolerance_gB, q_e_rmse_tolerance_gB, gamma_e_rmse_tolerance_gB, flux_relative_mae_tolerance, critical_gradient_accuracy_min, quantitative_claim_allowed, claim_status)

Serialisable admission evidence for neural-turbulence surrogate claims.

QLKNNSurrogate

QLKNNSurrogate(hidden_layers=None, activation='elu', pretrained=True)

Pure NumPy inference for a QLKNN-shaped neural network.

Predicts turbulent fluxes [Q_i, Q_e, Gamma_e] from 10 parameters. Architecture follows van de Plassche et al., Phys. Plasmas 27, 022310 (2020); the weights are NOT the published QLKNN10D weights.

Default construction self-trains on an analytic Jenko et al. (2001) critical-gradient target, so out-of-the-box predictions reproduce that analytic model, not QuaLiKiz. Load externally trained weights before citing QLKNN-class fidelity.

forward

forward(x)

Map turbulence features x (batch_size, 10) to fluxes (batch_size, 3).

load_weights

load_weights(path)

Load weights and biases from a NumPy .npz archive.

The archive is opened with allow_pickle=False so a hostile .npz cannot execute code through a pickled object array while it is loaded.

Parameters

path Path to an .npz file with arrays w{i} and b{i} for each layer i.

Raises

ValueError If the archive stores an object array, which numpy refuses to materialise without pickling.

save_weights

save_weights(path)

Save weights and biases to a NumPy .npz archive.

Parameters

path Destination .npz path; each layer is stored as w{i} and b{i}.

TransportInputNormalizer

Convert physical plasma profiles into the 10 dimensionless QLKNN inputs.

from_profiles staticmethod

from_profiles(Te, Ti, ne, q, R0, a, B0, r)

Convert physical profiles into the 10 dimensionless QLKNN inputs.

TrainingDataGenerator

Synthetic training-data generator for the QLKNN flux surrogate.

generate_parameter_scan staticmethod

generate_parameter_scan(n_samples, rng=None)

Uniform random sampling in 10D QLKNN parameter space.

generate_analytic_targets staticmethod

generate_analytic_targets(inputs)

Compute bounded analytic quasilinear flux targets.

Returns [Q_i, Q_e, Gamma_e] in gyro-Bohm units.

NeuralTransportTrainer

Backpropagation trainer for the QLKNN gyro-Bohm flux surrogate.

train

train(X, y, epochs=200, lr=0.001, val_frac=0.2)

Train a QLKNN surrogate by gradient descent with clipping.

Parameters

X Input samples, shape (n_samples, 10) of QLKNN inputs. y Target gyro-Bohm fluxes, shape (n_samples, 3). epochs Number of training epochs. lr Learning rate. val_frac Fraction of samples held out for validation.

Returns

dict[str, Any] Training history with "train_loss" and "val_loss" lists.

TransportFluxes dataclass

TransportFluxes(Q_i_W_m2, Q_e_W_m2, Gamma_e_inv_m2_s)

Dimensional turbulent transport fluxes on a radial grid.

Attributes

Q_i_W_m2 Ion heat flux in W/m². Q_e_W_m2 Electron heat flux in W/m². Gamma_e_inv_m2_s Electron particle flux in m⁻² s⁻¹.

QLKNNTransportModel

QLKNNTransportModel(surrogate)

QLKNN surrogate transport model mapping profiles to dimensional fluxes.

Parameters

surrogate The trained gyro-Bohm flux surrogate network.

compute_fluxes

compute_fluxes(Te, Ti, ne, q, R0, a, B0, r)

Predict dimensional turbulent fluxes from physical profiles.

Normalises the profiles to QLKNN inputs, evaluates the surrogate for gyro-Bohm fluxes, then de-normalises with the local gyro-Bohm unit.

Parameters

Te Electron-temperature profile in keV. Ti Ion-temperature profile in keV. ne Electron-density profile in 10¹⁹ m⁻³. q Safety-factor profile (dimensionless). R0 Major radius in metres. a Minor radius in metres. B0 Toroidal field on axis in tesla. r Strictly increasing minor-radius grid in metres.

Returns

TransportFluxes The ion and electron heat fluxes and the electron particle flux.

cross_validate_neural_turbulence

cross_validate_neural_turbulence(surrogate=None, *, n_samples=256, seed=20240531)

Compare the surrogate against bounded analytic quasilinear targets.

neural_turbulence_claim_evidence

neural_turbulence_claim_evidence(validation_result, *, source, source_id, model_id='neural_turbulence_qlknn_facade', weights_path=None, reference_artifact_path=None)

Build fail-closed evidence for neural-turbulence quantitative claims.

assert_neural_turbulence_quantitative_claim_admissible

assert_neural_turbulence_quantitative_claim_admissible(evidence)

Return evidence only when strict matched-reference admission passed.

save_neural_turbulence_claim_evidence

save_neural_turbulence_claim_evidence(evidence, path)

Persist neural-turbulence claim evidence as deterministic JSON.

NeuralTurbulenceClaimEvidence dataclass

NeuralTurbulenceClaimEvidence(schema_version, model_id, source, source_id, weights_path, weights_sha256, feature_schema, local_sample_count, local_q_i_rmse_gB, local_q_e_rmse_gB, local_gamma_e_rmse_gB, local_flux_relative_mae, local_critical_gradient_accuracy, reference_source, reference_dataset_id, reference_artifact_sha256, reference_sample_count, q_i_rmse_gB, q_e_rmse_gB, gamma_e_rmse_gB, flux_relative_mae, critical_gradient_accuracy, q_i_rmse_tolerance_gB, q_e_rmse_tolerance_gB, gamma_e_rmse_tolerance_gB, flux_relative_mae_tolerance, critical_gradient_accuracy_min, quantitative_claim_allowed, claim_status)

Serialisable admission evidence for neural-turbulence surrogate claims.

cross_validate_neural_turbulence

cross_validate_neural_turbulence(surrogate=None, *, n_samples=256, seed=20240531)

Compare the surrogate against bounded analytic quasilinear targets.

neural_turbulence_claim_evidence

neural_turbulence_claim_evidence(validation_result, *, source, source_id, model_id='neural_turbulence_qlknn_facade', weights_path=None, reference_artifact_path=None)

Build fail-closed evidence for neural-turbulence quantitative claims.

assert_neural_turbulence_quantitative_claim_admissible

assert_neural_turbulence_quantitative_claim_admissible(evidence)

Return evidence only when strict matched-reference admission passed.

save_neural_turbulence_claim_evidence

save_neural_turbulence_claim_evidence(evidence, path)

Persist neural-turbulence claim evidence as deterministic JSON.

Orbit Following

orbit_following

Guiding-centre orbit-following, classification, and Monte Carlo ensemble utilities.

EnsembleResult dataclass

EnsembleResult(loss_fraction, heating_profile, current_drive, n_passing, n_trapped, n_lost)

Aggregate outcome of a Monte-Carlo fast-ion orbit ensemble.

Attributes

loss_fraction Fraction of launched particles lost to the wall. heating_profile Radial power-deposition profile from confined particles. current_drive Net driven current from passing particles in amperes. n_passing Number of passing particles. n_trapped Number of trapped particles. n_lost Number of lost particles.

OrbitFollowingClaimEvidence dataclass

OrbitFollowingClaimEvidence(schema_version, source, source_id, geometry_source, particle_source, collision_model, loss_boundary_source, model_id, q, rho_L_m, epsilon, banana_width_m, first_orbit_loss_fraction, ensemble_particles, ensemble_loss_fraction, ensemble_passing, ensemble_trapped, ensemble_lost, banana_width_relative_error, loss_fraction_abs_error, banana_width_relative_tolerance, loss_fraction_abs_tolerance, external_orbit_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for orbit-following claims.

GuidingCenterOrbit

GuidingCenterOrbit(m_amu, Z, E_keV, pitch_angle, R0_init, Z0_init)

Guiding-center equations of motion in (R, Z, phi, v_par) for an axisymmetric tokamak.

Drift decomposition follows Boozer 2004, Rev. Mod. Phys. 76, 1071, Eqs. 16–18: dR/dt = v_par * b_R + v_drift_R dZ/dt = v_par * b_Z + v_drift_Z dphi/dt = v_par * b_phi/R + v_drift_phi/R

where v_drift combines grad-B and curvature drifts

v_drift = (mu/q)(b × ∇B)/B + (m v_par²/q)(b × κ)/B = [(m v_par² + mu B) / (q B²)] (B × ∇B)

The combined form uses the identity κ = (b·∇)b = ∇B/B for large-aspect- ratio equilibria; Boozer 2004, Eq. 18.

step

step(B_field, dt)

Advance the guiding-centre orbit one RK4 step.

Parameters

B_field Callable (R, Z) -> (B_R, B_Z, B_phi) giving the field in tesla. dt Time step in seconds; must be positive.

Returns

tuple of float The updated (R, Z, phi, v_par) state in metres, metres, radians, and m/s.

OrbitClassifier

Classify a guiding-centre orbit as passing, trapped, or lost.

classify staticmethod

classify(orbit_R, orbit_Z, v_par, R_wall, Z_wall_upper)

Classify an orbit trajectory against the wall and the v_par sign.

Parameters

orbit_R Major-radius trajectory in metres. orbit_Z Vertical trajectory in metres, same shape as orbit_R. v_par Parallel-velocity trajectory in m/s, same shape as orbit_R. R_wall Outer wall major radius in metres; must be positive. Z_wall_upper Upper wall height in metres; must be positive.

Returns

str "lost" if the orbit leaves the wall, "trapped" if v_par reverses sign, otherwise "passing".

MonteCarloEnsemble

MonteCarloEnsemble(n_particles, E_birth_keV, R0, a, B0)

Monte-Carlo ensemble of fast-ion guiding-centre orbits.

Parameters

n_particles Number of test particles; must be a positive integer. E_birth_keV Birth energy in keV; must be positive. R0 Major radius in metres; must be positive. a Minor radius in metres; must be positive and below R0. B0 Toroidal field on axis in tesla; must be positive.

initialize

initialize(ne_profile, Te_profile, rho)

Seed the ensemble with randomly distributed birth orbits.

Parameters

ne_profile Electron-density profile on rho; must be positive. Te_profile Electron-temperature profile on rho; must be positive. rho Strictly increasing normalised-radius grid in [0, 1].

follow

follow(B_field, n_bounces=10, dt=1e-07)

Integrate the ensemble and aggregate confinement statistics.

Parameters

B_field Callable (R, Z) -> (B_R, B_Z, B_phi) giving the field in tesla. n_bounces Number of bounce times to follow; must be a positive integer. dt Orbit time step in seconds; must be positive.

Returns

EnsembleResult Loss fraction, heating profile, driven current, and the passing, trapped, and lost counts.

SlowingDown

Stix slowing-down model for fast ions (e.g. fusion alphas).

All formulae from Stix 1972, Plasma Physics 14, 367.

critical_energy staticmethod

critical_energy(Te_keV, A_fast, A_bg, Z_bg, ne_20)

Critical energy E_crit where electron and ion drag are equal.

Stix 1972, Eq. 12 (thermally-averaged form): E_crit = (A_fast / A_e) * T_e * (3√π / 4 * Σ n_j Z_j² / (n_e A_j))^(2/3)

Simplified to leading term for a single-ion background

E_crit = (A_fast m_p / m_e)^(1/3) * (A_fast / (2 A_bg))^(1/3) * (3√π/4)^(2/3) * T_e

We use the standard Cordey 1981 (Nucl. Fusion 21, 1293) simplification: v_c³ = (3√π / 4) * (m_e / m_fast) * Σ_j (n_j Z_j² / n_e) * v_te³

with v_te = √(2 T_e / m_e), giving E_crit = ½ m_fast v_c².

critical_velocity staticmethod

critical_velocity(Te_keV, ne_20)

v_c for a DT-plasma background (Z_bg=1, A_bg=2.5).

Stix 1972, Eq. 12.

tau_sd staticmethod

tau_sd(Te_keV, ne_20, Z_eff)

Spitzer electron slowing-down time τ_s for a fast ion.

Stix 1972, Eq. 7 (see also Wesson 2011, Eq. 7.4.7): τ_s = (6 π^(3/2) ε₀² m_e^(1/2) m_fast T_e^(3/2)) / (n_e Z_fast² e⁴ ln Λ * 2^(1/2))

Pre-evaluated for m_fast = 4 m_p (alpha), Z_fast = 2: τ_s [s] ≈ 0.198 A_fast T_e[keV]^(3/2) / (n_e[10²⁰ m⁻³] Z_fast² ln Λ) with A_fast=4, Z_fast=2, ln Λ = 17. Reference: Stix 1972, Plasma Physics 14, 367, Eq. 7.

dE_dt staticmethod

dE_dt(E_keV, E_crit_keV, tau_s)

Stix slowing-down power loss.

dE/dt = -(E / τ_s) * (1 + (E_crit / E)^(3/2)).

Reference: Stix 1972, Plasma Physics 14, 367, Eq. 12.

Below E_crit the (E_crit/E)^(3/2) term > 1, so ion drag dominates. Above E_crit the term < 1, so electron drag dominates.

heating_partition staticmethod

heating_partition(v, v_c)

Fraction of power deposited into ions vs electrons.

Stix 1972, Eq. 16: f_ion = v_c³ / (v³ + v_c³) f_electron = v³ / (v³ + v_c³)

At v = v_c: equal partition (f_ion = f_electron = 0.5). At v >> v_c: all to electrons. At v << v_c: all to ions.

orbit_following_claim_evidence

orbit_following_claim_evidence(*, source, source_id, geometry_source, particle_source, collision_model, loss_boundary_source, q, rho_L_m, epsilon, first_orbit_loss_fraction, ensemble_result=None, model_id='bounded_guiding_centre_orbit_following', reference_banana_width_m=None, reference_loss_fraction=None, banana_width_relative_tolerance=0.05, loss_fraction_abs_tolerance=0.02)

Build fail-closed evidence for bounded or externally referenced orbit claims.

assert_orbit_following_external_claim_admissible

assert_orbit_following_external_claim_admissible(evidence)

Raise when orbit-following evidence is insufficient for an external-code claim.

save_orbit_following_claim_evidence

save_orbit_following_claim_evidence(evidence, path)

Persist orbit-following claim evidence as deterministic JSON.

first_orbit_loss

first_orbit_loss(R0, a, B0, Ip_MA, E_alpha_keV=3520.0)

Prompt loss fraction for birth alphas on their first orbit.

Scaling: f_lost ≈ (ρ_orbit / a)² / I_p Reference: Goldston 1981, J. Comput. Phys. 43, 61, Eq. 15 (orbit width ∝ ρ_L / √ε, loss ∝ (ρ_orbit/a)²/I_p)

ρ_orbit = ρ_L q / √ε — banana orbit width (see banana_orbit_width) Here we use the simpler large-aspect-ratio estimate ρ_orbit ≈ 2 ρ_L valid for passing particles at the loss boundary (Goldston 1981, §3).

banana_orbit_width

banana_orbit_width(q, rho_L, epsilon)

Banana orbit width: Δ_b = q ρ_L / √ε.

Reference: Wesson 2011, Tokamaks 4th ed., Eq. 5.4.14

Parameters

q : safety factor at the flux surface rho_L : Larmor radius [m] epsilon : inverse aspect ratio r/R

OrbitFollowingClaimEvidence dataclass

OrbitFollowingClaimEvidence(schema_version, source, source_id, geometry_source, particle_source, collision_model, loss_boundary_source, model_id, q, rho_L_m, epsilon, banana_width_m, first_orbit_loss_fraction, ensemble_particles, ensemble_loss_fraction, ensemble_passing, ensemble_trapped, ensemble_lost, banana_width_relative_error, loss_fraction_abs_error, banana_width_relative_tolerance, loss_fraction_abs_tolerance, external_orbit_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for orbit-following claims.

orbit_following_claim_evidence

orbit_following_claim_evidence(*, source, source_id, geometry_source, particle_source, collision_model, loss_boundary_source, q, rho_L_m, epsilon, first_orbit_loss_fraction, ensemble_result=None, model_id='bounded_guiding_centre_orbit_following', reference_banana_width_m=None, reference_loss_fraction=None, banana_width_relative_tolerance=0.05, loss_fraction_abs_tolerance=0.02)

Build fail-closed evidence for bounded or externally referenced orbit claims.

assert_orbit_following_external_claim_admissible

assert_orbit_following_external_claim_admissible(evidence)

Raise when orbit-following evidence is insufficient for an external-code claim.

save_orbit_following_claim_evidence

save_orbit_following_claim_evidence(evidence, path)

Persist orbit-following claim evidence as deterministic JSON.

Pedestal

pedestal

Pedestal profile model using the modified tanh (mtanh) parameterisation.

Provides H-mode pedestal structures for temperature and density profiles, including width scaling based on the EPED1 model.

PedestalParams dataclass

PedestalParams(f_ped, f_sep=0.1, x_ped=0.94, delta=0.04, a=0.2)

Parameters for the mtanh pedestal profile.

Parameters

f_ped : float Value at the pedestal top (e.g. T_ped in keV). f_sep : float Value at the separatrix (edge). x_ped : float Normalized radial position of the pedestal symmetry point (typically 0.94). delta : float Pedestal width in normalized radius (typically 0.04). a : float Slope parameter for the pedestal top (typically 0.1-0.3).

PedestalProfile

PedestalProfile(params)

Modified tanh (mtanh) pedestal profile generator.

Reference: Groebner, R.J. et al., Nucl. Fusion 41, 1789 (2001).

mtanh

mtanh(y)

Core mtanh function: ((1+ay)e^y - e^-y) / (e^y + e^-y).

evaluate

evaluate(rho)

Evaluate the pedestal profile at given radial points.

f(x) = (f_ped + f_sep)/2 + (f_ped - f_sep)/2 * mtanh((x_ped - x)/delta)

pedestal_width_eped1

pedestal_width_eped1(beta_p_ped, delta_psi=0.0)

Compute pedestal width using EPED1-like scaling.

Scaling: Delta_psi = 0.076 * sqrt(beta_p_ped) Reference: Snyder, P.B. et al., Phys. Plasmas 16, 056118 (2009).

Parameters

beta_p_ped : float Poloidal beta at the pedestal top. delta_psi : float Optional offset or alternative scaling parameter.

Returns

float — Predicted pedestal width in poloidal flux (or rho approx).

Pellet Injection

pellet_injection

Pellet ablation, trajectory, deposition, and fuelling-control utilities.

PelletParams dataclass

PelletParams(r_p_mm, v_p_m_s, M_p=2.0, injection_side='HFS', injection_angle_deg=0.0)

Cryogenic fuel-pellet launch parameters.

Attributes

r_p_mm Pellet radius in millimetres; must be positive. v_p_m_s Pellet injection speed in m/s; must be positive. M_p Atomic mass of the pellet species in amu (2.0 for deuterium). injection_side Launch side, "HFS" (high-field) or "LFS" (low-field). injection_angle_deg Injection angle relative to the midplane in degrees.

PelletResult dataclass

PelletResult(penetration_depth, deposition_profile, total_particles, drift_displacement)

Outcome of a pellet ablation/deposition simulation.

Attributes

penetration_depth Normalised radius rho at which the pellet was fully ablated. deposition_profile Particle deposition density per radial cell in m⁻³, drift-shifted. total_particles Total particles deposited into the plasma. drift_displacement ∇B drift displacement of the deposition in normalised radius.

PelletInjectionCommand dataclass

PelletInjectionCommand(inject_time, pellet_params)

Command to launch a pellet at a scheduled time.

Attributes

inject_time Scheduled injection time in seconds. pellet_params The pellet launch parameters.

PelletTrajectory

PelletTrajectory(params, R0, a, B0)

Radial pellet trajectory with neutral-gas-shielding ablation and ∇B drift.

Integrates the Parks-Turnbull NGS ablation rate along an inward radial path, depositing the ablated particles and applying a ∇B drift shift of the deposition profile (Pegourie 2005, Plasma Phys. Control. Fusion 47, 17).

Parameters

params The pellet launch parameters. R0 Major radius in metres; must be positive. a Minor radius in metres; must be positive and below R0. B0 Toroidal field on axis in tesla; must be positive.

simulate

simulate(rho, ne, Te_eV)

Integrate ablation and deposition along the inward radial path.

Parameters

rho Strictly increasing normalised-radius grid in [0, 1], shape (n_rho,) with at least two points. ne Electron-density profile in 10¹⁹ m⁻³ on rho; non-negative. Te_eV Electron-temperature profile in eV on rho; non-negative.

Returns

PelletResult The penetration depth, drift-shifted deposition profile, deposited particle count, and drift displacement.

Raises

ValueError If rho is not a strictly increasing grid in [0, 1] or the profiles have the wrong shape or are negative.

PelletFuelingController

PelletFuelingController(target_density, pellet_params)

Pellet-pacing controller maintaining a volume-averaged density target.

Parameters

target_density Target volume-averaged electron density in 10¹⁹ m⁻³; must be positive. pellet_params The pellet launch parameters used for each injection.

required_frequency

required_frequency(ne_current, tau_p, V_plasma)

Pellet frequency that sustains the target density inventory.

From the particle balance dN/dt = -N/τ_p + f N_pellet at the target, f = N_target / (τ_p N_pellet).

Parameters

ne_current Current volume-averaged density in 10¹⁹ m⁻³; must be non-negative. tau_p Particle confinement time in seconds; must be positive. V_plasma Plasma volume in m³; must be positive.

Returns

float The required injection frequency in Hz.

step

step(ne_profile, Te_profile, dt, V_plasma)

Advance the controller clock and decide whether to inject.

A pellet is scheduled when the mean density falls below 95% of target and the time since the last injection exceeds the required pacing period.

Parameters

ne_profile Electron-density profile in 10¹⁹ m⁻³; non-empty and non-negative. Te_profile Electron-temperature profile in eV, same shape as ne_profile. dt Time step in seconds; must be positive. V_plasma Plasma volume in m³; must be positive.

Returns

PelletInjectionCommand or None A launch command when an injection is due, otherwise None.

ngs_ablation_rate

ngs_ablation_rate(r_p, ne, Te_eV, M_p)

Parks & Turnbull (1978) NGS ablation rate [atoms/s].

pellet_pacing_elm_control

pellet_pacing_elm_control(f_pellet_Hz, f_elm_natural_Hz, w_elm_natural_MJ)

Return (f_ELM, delta_W_ELM).

Plasma Startup

plasma_startup

Plasma-startup sequence, avalanche, burn-through, and command-planning utilities.

PaschenBreakdown

PaschenBreakdown(gas='D2', R0=6.2, a=2.0)

Paschen curve breakdown model for D₂ gas.

Paschen (1889), Wied. Ann. 37, 69. Quantitative coefficients from Lieberman & Lichtenberg (2005), Ch. 14.3. Breakdown voltage: V_bd(pd) = B·pd / (ln(A·pd) - ln(ln(1/γ+1))) Minimum voltage: V_min = e·B/A · ln(1/γ+1) at (pd)_min = e/A · ln(1/γ+1) Reference: Lieberman 2005, Eq. 14.3.2.

v_paschen_min property

v_paschen_min

Paschen-curve minimum breakdown voltage [V].

pd_at_minimum property

pd_at_minimum

Pressure·distance at Paschen minimum [Pa·m].

breakdown_voltage

breakdown_voltage(p_Pa, connection_length_m)

Paschen breakdown voltage [V].

Lieberman 2005 Eq. 14.3.1: V_bd = B·(pd) / (ln(A·pd) - C₂) where C₂ = ln(ln(1/γ+1)).

is_breakdown

is_breakdown(V_loop, p_Pa, connection_length_m=100.0)

Return whether the loop voltage exceeds the Paschen breakdown voltage.

Parameters

V_loop Applied loop voltage in volts. p_Pa Prefill gas pressure in pascals. connection_length_m Field-line connection length in metres (the effective gap).

Returns

bool True when V_loop exceeds :meth:breakdown_voltage.

paschen_curve

paschen_curve(p_range, connection_length_m=100.0)

Evaluate the Paschen breakdown voltage across a pressure range.

Parameters

p_range Pressures in pascals; one-dimensional and finite. connection_length_m Field-line connection length in metres.

Returns

FloatArray Breakdown voltage in volts at each pressure (inf where no breakdown solution exists), same shape as p_range.

optimal_prefill_pressure

optimal_prefill_pressure(V_loop_max, connection_length_m=100.0)

Return the prefill pressure that sits at the Paschen minimum.

The voltage-minimising (pd)_min (Lieberman 2005, Eq. 14.3.2) divided by the connection length gives the easiest-breakdown pressure.

Parameters

V_loop_max Available loop-voltage budget in volts; must be positive. connection_length_m Field-line connection length in metres; must be positive.

Returns

float Optimal prefill pressure in pascals.

AvalancheResult dataclass

AvalancheResult(ne_trace, Te_trace, time_to_full_ionization_ms)

Time traces from a Townsend avalanche simulation.

Attributes

ne_trace Electron-density trace in m⁻³, one sample per step. Te_trace Electron-temperature trace in eV, one sample per step. time_to_full_ionization_ms Time to reach 99% of the neutral density in milliseconds, or -1 if not reached within the simulated steps.

TownsendAvalanche

TownsendAvalanche(V_loop, p_Pa, R0, a)

Townsend avalanche and burn-through pre-ionisation model.

Energy balance follows Lieberman & Lichtenberg (2005), Ch. 14.4: d(n_e)/dt = n_e · k_iz · n_0 Ionisation rate coefficient (Maxwellian electrons): k_iz = σ_0 · √(8kT_e/(πm_e)) · exp(−E_iz/T_e) Reference: Janev et al. (1987), Ch. 2, rate coefficient parameterisation.

ionization_rate

ionization_rate(Te_eV)

Rate coefficient k_iz [m³ s⁻¹] × n_neutral [m⁻³] → [s⁻¹].

k_iz = σ_0 · √(8kT_e/(πm_e)) · exp(−E_iz/T_e) Janev et al. (1987) Ch. 2; σ_0 = 1e-20 m², E_iz = 15.4 eV for D₂.

evolve

evolve(dt, n_steps)

Integrate the avalanche ionisation and electron energy balance.

Steps the seed plasma forward with ohmic heating against ionisation energy loss until the electron density saturates at the neutral density.

Parameters

dt Time step in seconds; must be positive. n_steps Number of integration steps; must be a positive integer.

Returns

AvalancheResult The density and temperature traces and the full-ionisation time.

BurnThroughResult dataclass

BurnThroughResult(Te_trace, success, time_to_burn_through_ms)

Outcome of a burn-through energy-balance simulation.

Attributes

Te_trace Electron-temperature trace in eV, one sample per step. success True if the temperature crossed the 100 eV burn-through threshold. time_to_burn_through_ms Time to cross the threshold in milliseconds, or -1 if not reached.

BurnThrough

BurnThrough(R0, a, B0, V_loop)

Burn-through energy balance model.

Criterion: P_ohmic > P_radiation_barrier. Energy balance: 3/2 · n_e · dT_e/dt = P_ohmic − P_rad Reference: Wesson (2011), "Tokamaks", 4th ed., §6.4, Eq. 6.4.1–6.4.3. Spitzer resistivity: η = 1.65×10⁻⁹ · Z_eff · ln_Λ / T_e[keV]^{3/2} Reference: Wesson 2011 Eq. 2.5.4.

ohmic_power

ohmic_power(Te_eV, ne_19, Ip_kA)

Ohmic heating power from Spitzer resistivity (Wesson 2011, Eq. 2.5.4).

Parameters

Te_eV Electron temperature in eV; must be positive. ne_19 Electron density in 10¹⁹ m⁻³; must be positive. Ip_kA Plasma current in kA; must be non-negative.

Returns

float Ohmic power in watts.

radiation_barrier

radiation_barrier(Te_eV, ne_19, f_imp, impurity='C')

Impurity line-radiation power that opposes burn-through.

Uses the impurity cooling curve L_z(T_e) over the plasma volume: P_rad = n_e² f_imp L_z V.

Parameters

Te_eV Electron temperature in eV; must be positive. ne_19 Electron density in 10¹⁹ m⁻³; must be positive. f_imp Impurity fraction relative to electron density; must be non-negative. impurity Impurity species symbol passed to :class:CoolingCurve.

Returns

float Radiated power in watts.

burn_through_condition

burn_through_condition(Te_eV, ne_19, Ip_kA, f_imp, impurity='C')

Return whether ohmic power exceeds the impurity radiation barrier.

Parameters

Te_eV Electron temperature in eV. ne_19 Electron density in 10¹⁹ m⁻³. Ip_kA Plasma current in kA. f_imp Impurity fraction relative to electron density. impurity Impurity species symbol.

Returns

bool True when :meth:ohmic_power exceeds :meth:radiation_barrier.

critical_impurity_fraction

critical_impurity_fraction(Te_eV, ne_19, Ip_kA, impurity)

Maximum impurity fraction for which burn-through still succeeds.

Solves P_ohmic = n_e² f_crit L_z V for f_crit.

Parameters

Te_eV Electron temperature in eV; must be positive. ne_19 Electron density in 10¹⁹ m⁻³; must be positive. Ip_kA Plasma current in kA; must be non-negative. impurity Impurity species symbol passed to :class:CoolingCurve.

Returns

float Critical impurity fraction (1.0 when the cooling curve vanishes).

evolve

evolve(ne_19, f_imp, dt, n_steps, impurity='C')

Integrate the burn-through temperature equation with a current ramp.

Advances 3/2 n_e V dT_e/dt = P_ohmic − P_rad (Wesson 2011, Eq. 6.4.3); once T_e exceeds 20 eV the plasma current ramps at 1 MA/s.

Parameters

ne_19 Electron density in 10¹⁹ m⁻³; must be positive. f_imp Impurity fraction relative to electron density; must be non-negative. dt Time step in seconds; must be positive. n_steps Number of integration steps; must be a positive integer. impurity Impurity species symbol.

Returns

BurnThroughResult The temperature trace and the burn-through success and timing.

StartupResult dataclass

StartupResult(breakdown_time_ms, burn_through_time_ms, Ip_at_100ms_kA, Te_at_100ms_eV, success)

Summary of a full startup sequence.

Attributes

breakdown_time_ms Time to full ionisation in milliseconds, or -1 if breakdown failed. burn_through_time_ms Time to burn-through in milliseconds, or -1 if not achieved. Ip_at_100ms_kA Plasma current at 100 ms in kA. Te_at_100ms_eV Electron temperature at the end of the burn-through trace in eV. success True if the sequence reached burn-through.

StartupSequence

StartupSequence(R0, a, B0, V_loop, p_prefill_Pa, f_imp=0.01)

End-to-end startup: Paschen breakdown, avalanche, then burn-through.

Parameters

R0 Major radius in metres. a Minor radius in metres. B0 Toroidal field on axis in tesla. V_loop Applied loop voltage in volts. p_prefill_Pa Prefill gas pressure in pascals. f_imp Impurity fraction relative to electron density.

run

run()

Execute the breakdown → avalanche → burn-through sequence.

Returns

StartupResult The timing and success summary; a failed breakdown short-circuits to an unsuccessful result.

StartupPhase

Bases: Enum

Discrete phases of the startup sequence.

StartupCommand dataclass

StartupCommand(V_loop, gas_puff_rate, phase)

Actuator command issued during a startup phase.

Attributes

V_loop Commanded loop voltage in volts. gas_puff_rate Commanded gas-puff rate in facility units. phase The startup phase that produced this command.

StartupController

StartupController(V_loop_max, gas_puff_max)

Phase-machine controller scheduling voltage and gas puff during startup.

Parameters

V_loop_max Maximum loop voltage in volts; must be non-negative. gas_puff_max Maximum gas-puff rate in facility units; must be non-negative.

step

step(ne, Te, Ip, t, dt)

Advance the phase machine and return the actuator command.

Phase transitions: gas puff → breakdown at t > 0.1 s, → burn-through once n_e exceeds 1e18 m⁻³, → ramp once T_e exceeds 50 eV.

Parameters

ne Electron density in m⁻³. Te Electron temperature in eV. Ip Plasma current (validated non-negative). t Elapsed time in seconds. dt Time step in seconds; must be positive.

Returns

StartupCommand The voltage and gas-puff command for the current phase.

Plasma Wall Interaction

plasma_wall_interaction

Plasma-wall thermal loading and divertor lifetime-assessment utilities.

SputteringYield

SputteringYield(target='W', projectile='D')

Physical sputtering yield Y [atoms ion^{-1}] using Eckstein analytical formula.

Y = Q · S_n(ε) / (1 + Γ · ε^{0.3}) · (1 − (E_th/E)^{2/3}) · (1 − E_th/E)²

Eckstein & Preuss 2003, J. Nucl. Mater. 320, 209. D→W parameters: M1=2, M2=183.8, Z1=1, Z2=74.

threshold_energy

threshold_energy()

E_th for D→W sputtering [eV]. Behrisch & Eckstein 2007, Table 3.1.

yield_at_energy

yield_at_energy(E_ion_eV, theta_deg=0.0)

Eckstein formula (normal incidence + angular correction).

Y = Q · S_n(ε) / (1 + Γ · ε^{0.3}) · (1 − (E_th/E)^{2/3}) · (1 − E_th/E)² Eckstein & Preuss 2003, J. Nucl. Mater. 320, 209.

ErosionModel

ErosionModel(material='W', n_atom=6.31e+28)

Gross and net erosion rates from sputtering.

Γ_erode = Γ_ion · Y(E, θ). Eckstein & Preuss 2003, J. Nucl. Mater. 320, 209.

gross_erosion_rate

gross_erosion_rate(ion_flux, E_ion_eV, theta_deg=0.0)

Γ_erode = Γ_ion · Y(E, θ) [atoms m^{-2} s^{-1}].

net_erosion_rate

net_erosion_rate(gross_rate, f_redeposition=0.97)

Net rate accounting for redeposition [atoms m^{-2} s^{-1}].

depth_rate

depth_rate(net_rate)

Erosion depth rate [m s^{-1}].

lifetime_estimate

lifetime_estimate(wall_thickness_mm, net_rate_m_s)

Erosion lifetime [years].

tritium_retention

tritium_retention(ion_fluence_D_m2)

Retained T fraction (relative to ion fluence) for W at room temperature.

~1% per 10^24 D m^{-2}. Roth et al. 2009, J. Nucl. Mater. 390-391, 1.

WallThermalModel

WallThermalModel(material='W', thickness_mm=10.0, n_nodes=50)

1D explicit heat diffusion into the wall.

step

step(dt, q_surface_MW_m2)

Advance the 1-D wall heat-conduction model one step.

Integrates explicit conduction with a radiating front face and a fixed coolant boundary, sub-cycling to satisfy the diffusion CFL limit.

Parameters

dt Time step in seconds; must be positive. q_surface_MW_m2 Incident surface heat flux in MW/m²; must be non-negative.

Returns

float The updated front-surface temperature in kelvin.

is_melted

is_melted()

Return whether the front surface has exceeded the melting point.

TransientThermalLoad

TransientThermalLoad(wall)

Transient (ELM/disruption) surface heating and fatigue estimates.

Parameters

wall The wall thermal model providing material properties.

elm_load

elm_load(delta_W_MJ, A_wet_m2, tau_IR_ms=0.25)

Peak surface ΔT [K] from an ELM; ΔT = 2q√(τ / (π κ ρ c_p)).

disruption_load

disruption_load(W_th_MJ, A_wet_m2, tau_TQ_ms=1.0)

Peak surface temperature rise from a thermal-quench heat pulse.

Parameters

W_th_MJ Thermal energy deposited in MJ; must be non-negative. A_wet_m2 Wetted area in m². tau_TQ_ms Thermal-quench duration in milliseconds.

Returns

float Peak surface temperature rise in kelvin.

n_elm_cycles_to_fatigue

n_elm_cycles_to_fatigue(delta_T_K, T_base_K=600.0)

Coffin-Manson empirical rule for W; at ΔT = 1000 K, N ~ 10^4.

LifetimeReport dataclass

LifetimeReport(erosion_mm_per_year, peak_T_steady_K, peak_T_elm_K, n_cycles_to_fatigue, lifetime_years, limiting_factor)

Divertor-target lifetime assessment summary.

Attributes

erosion_mm_per_year Net erosion rate in mm/year. peak_T_steady_K Steady-state peak surface temperature in kelvin. peak_T_elm_K Peak surface temperature including the ELM transient in kelvin. n_cycles_to_fatigue Estimated ELM cycles to thermal fatigue. lifetime_years Estimated component lifetime in years. limiting_factor The lifetime-limiting mechanism ("Erosion", "Fatigue", or "Melting").

DivertorLifetimeAssessment

DivertorLifetimeAssessment(sol, sputtering, erosion, wall)

Divertor lifetime assessment coupling SOL, sputtering, erosion, and heat.

Parameters

sol Two-point SOL model for target conditions. sputtering Sputtering-yield model. erosion Erosion-rate model. wall Wall thermal model.

assess

assess(P_SOL_MW, n_u_19, f_ELM_Hz, delta_W_ELM_MJ)

Estimate divertor lifetime from steady, erosion, and ELM-fatigue limits.

Parameters

P_SOL_MW Scrape-off-layer power in MW. n_u_19 Upstream density in 10¹⁹ m⁻³. f_ELM_Hz ELM frequency in Hz. delta_W_ELM_MJ Energy expelled per ELM in MJ.

Returns

LifetimeReport Erosion rate, peak temperatures, fatigue cycles, lifetime, and the limiting mechanism.

Real Data Manifest

real_data_manifest

Validation contracts for real-shot and synthetic-shot data manifests.

The manifest is intentionally strict: synthetic fixtures are useful for CI, but they must never be counted as experimental validation evidence by accident.

RealDataManifestError

Bases: ValueError

Raised when a data manifest cannot support its claimed validation role.

SignalManifest dataclass

SignalManifest(name, path, units, timebase)

Single measured or generated signal entry in a manifest.

DataSourceManifest dataclass

DataSourceManifest(kind, uri, access)

Acquisition source and provenance for a manifest.

ArtifactManifest dataclass

ArtifactManifest(uri, checksum_sha256)

Local artefact covered by a manifest checksum.

RealDataManifest dataclass

RealDataManifest(schema_version, dataset_id, machine, shot, synthetic, source, signals, retrieved_at=None, checksum_sha256=None, licence=None, synthetic_generator=None, synthetic_seed=None, artifacts=())

Validated manifest separating real-shot evidence from synthetic fixtures.

kind property

kind

Return the validation role claimed by this manifest.

load_real_data_manifest

load_real_data_manifest(path, *, verify_artifact=False)

Load and validate a JSON real-data manifest.

verify_manifest_artifact

verify_manifest_artifact(manifest, *, manifest_path)

Verify local artefact checksums for manifests that reference local files.

Remote acquisition sources such as MDSplus URIs are provenance records rather than local files, so they return None here.

validate_real_data_manifest

validate_real_data_manifest(payload)

Validate a real-shot or synthetic-shot manifest.

Real manifests require stable provenance, licence, units, checksum, and a non-synthetic acquisition source. Synthetic manifests require generator metadata so CI fixtures cannot masquerade as experimental evidence.

Runaway Electrons

runaway_electrons

Runaway-electron generation, avalanche, evolution, and mitigation-assessment utilities.

RunawayParams dataclass

RunawayParams(ne_20, Te_keV, E_par, Z_eff, B0, R0, a=2.0)

Plasma parameters for runaway-electron generation.

Attributes

ne_20 Electron density in 10²⁰ m⁻³. Te_keV Electron temperature in keV. E_par Parallel electric field in V/m. Z_eff Effective ion charge. B0 Toroidal field on axis in tesla. R0 Major radius in metres. a Minor radius in metres.

RunawayEvolution

RunawayEvolution(params)

Runaway-electron density evolution from Dreicer and avalanche generation.

Parameters

params The plasma parameters driving runaway generation.

step

step(dt, n_RE, E_par)

Advance the runaway density one step with the current field.

Parameters

dt Time step in seconds; must be positive. n_RE Current runaway-electron density; must be non-negative. E_par Parallel electric field in V/m.

Returns

float The updated runaway-electron density.

evolve

evolve(n_RE_0, E_par_profile, t_span, dt)

Integrate the runaway density over a time span with a field profile.

Parameters

n_RE_0 Initial runaway-electron density; must be non-negative. E_par_profile Callable t -> E_par giving the parallel field in V/m. t_span (t_start, t_end) in seconds with t_end > t_start. dt Time step in seconds; must be positive.

Returns

tuple[FloatArray, FloatArray] The time grid and the runaway-density trace.

current_fraction

current_fraction(n_RE, I_p_MA)

Fraction of total plasma current carried by REs (v ≈ c).

RunawayMitigationAssessment

Runaway-electron mitigation thresholds such as collisional suppression.

required_density_for_suppression staticmethod

required_density_for_suppression(E_par, Z_eff, Te_keV=1.0)

Electron density [10^20 m^-3] required to raise E_c above E_par.

Derived by inverting critical_field: n_e = E_par (4π ε₀² m_e c²) / (e³ ln Λ). Uses the temperature-dependent Coulomb log (Wesson 2011, Eq. 2.12.4).

maximum_re_energy staticmethod

maximum_re_energy(B0, R0, E_par=10.0, ne_20=10.0, Te_keV=1.0)

Maximum RE energy [MeV] from synchrotron balance.

Martin-Solis et al., Phys. Plasmas 13, 062509 (2006), Eq. 12. Defaults represent a disruption scenario: E_par=10 V/m, post-TQ ne_20=10, Te_keV=1.

wall_heat_load staticmethod

wall_heat_load(n_RE, E_max_MeV, A_wet, volume=800.0)

Deposited energy density [MJ/m^2] assuming instantaneous loss.

Assumes mean RE energy ≈ E_max / 2 (flat distribution upper bound).

coulomb_log

coulomb_log(ne_20, Te_keV)

Coulomb logarithm for a thermal plasma.

For T_e > 10 eV: ln Λ = 14.9 - 0.5 ln(n_e / 10^20) + ln(T_e / 10^3) where n_e is in m^-3 and T_e in eV.

Wesson 2011, Tokamaks 4th ed., Eq. 2.12.4.

dreicer_field

dreicer_field(ne_20, Te_keV)

Dreicer field E_D = n_e e^3 ln Λ / (4π ε₀² T_e).

Connor & Hastie, Nucl. Fusion 15, 415 (1975).

critical_field

critical_field(ne_20, Te_keV=1.0)

Critical electric field E_c = n_e e^3 ln Λ / (4π ε₀² m_e c²).

Rosenbluth & Putvinski, Nucl. Fusion 37, 1355 (1997). Note: E_D / E_c = m_e c² / T_e ≈ 51 at T_e = 10 keV.

dreicer_generation_rate

dreicer_generation_rate(params)

Primary (Dreicer) seed runaway generation rate [m^-3 s^-1].

Connor & Hastie, Nucl. Fusion 15, 415 (1975). Smith et al., Phys. Plasmas 15, 072502 (2008) — hot-tail context uses same Dreicer base rate.

avalanche_growth_rate

avalanche_growth_rate(params, n_RE)

Avalanche multiplication rate [m^-3 s^-1].

Full pitch-angle-scattering formula

dn_RE/dt = n_RE (E/E_c - 1) / (τ_s ln Λ) × F(E/E_c, Z_eff)^{-1/2}

where F = 1 - E_c/E + 4π(Z+1)² / [3(Z+1)(E/E_c)²]

Rosenbluth & Putvinski, Nucl. Fusion 37, 1355 (1997), Eq. 15.

hot_tail_seed

hot_tail_seed(Te_pre_keV, Te_post_keV, ne_20, quench_time_ms)

Seed RE density from thermal-quench hot-tail mechanism [m^-3].

Smith et al., Phys. Plasmas 15, 072502 (2008). Parametric fit to Smith (2008) Fig. 3: v_c/v_te scales as τ_q^0.2 at the V_C_V_TE_REF = 4.0 reference (τ_q = 1 ms).

synchrotron_energy_limit

synchrotron_energy_limit(E_par, E_c)

Maximum RE energy [MeV] limited by synchrotron radiation.

E_max = m_e c² (E/E_c)^{1/3} (3 / (4 α_f))^{1/3}

Martin-Solis et al., Phys. Plasmas 13, 062509 (2006), Eq. 12. α_f = 7.297e-3 is the fine-structure constant (CODATA 2018).

Stellarator Geometry

stellarator_geometry

Stellarator flux-surface geometry in Boozer coordinates, neoclassical transport, and ISS04 confinement scaling.

References

Boozer, A. H., Phys. Fluids 24 (1981) 1999. Magnetic coordinate system with straight field lines; ∇ψ × ∇θ_B = B/B². Grieger, G. et al., Phys. Fluids B 4 (1992) 2081. Wendelstein 7-X design parameters and modular coil geometry. Yamada, H. et al., Nucl. Fusion 45 (2005) 1684. ISS04 empirical scaling: τ_E ∝ a^2.28 R^0.64 P^-0.61 n_e19^0.54 B^0.84 ι^0.41. Nemov, V. V. et al., Phys. Plasmas 6 (1999) 4622. Effective ripple ε_eff via field-line tracing (DKES/NEO-2 basis, Eq. 30). Beidler, C. D. et al., Nucl. Fusion 51 (2011) 076001. Quasi-isodynamic neoclassical transport optimization for W7-X.

StellaratorConfigSchema

Bases: BaseModel

Pydantic v2 schema for stellarator device and magnetic parameters.

validate_positive_major_radius_margin

validate_positive_major_radius_margin()

Ensure the helical flux surface cannot cross non-positive major radius.

StellaratorConfig dataclass

StellaratorConfig(N_fp=5, R0=5.5, a=0.53, B0=2.5, iota_0=0.87, iota_a=1.0, mirror_ratio=0.07, helical_excursion=0.05, name='custom')

Stellarator device and magnetic configuration parameters.

Parameters

N_fp : int Positive number of toroidal field periods. W7-X: 5 — Grieger et al. 1992, Phys. Fluids B 4, 2081. R0 : float Positive major radius [m]. W7-X: 5.5 m. a : float Positive average minor radius [m]. W7-X: 0.53 m. B0 : float Positive on-axis magnetic field [T]. W7-X standard: 2.5 T. iota_0 : float Positive rotational transform at magnetic axis (s=0). Boozer 1981, Phys. Fluids 24, 1999 — straight field-line coordinate. iota_a : float Positive rotational transform at plasma edge (s=1). mirror_ratio : float Non-negative helical mirror ratio ε_h = δB / B₀. W7-X: ~0.07. helical_excursion : float Non-negative helical axis excursion amplitude [m]; R0 must exceed a plus this excursion.

__post_init__

__post_init__()

Validate and normalise configuration values through the Pydantic schema.

model_json_schema classmethod

model_json_schema()

Return the Pydantic JSON Schema for stellarator configuration.

w7x_config

w7x_config()

Wendelstein 7-X standard configuration.

Grieger et al., Phys. Fluids B 4 (1992) 2081 — coil geometry and ι profile. Klinger et al., Nucl. Fusion 59 (2019) 112004 — operational parameters.

iota_profile

iota_profile(config, s)

Rotational transform ι(s) via linear interpolation.

Boozer 1981, Phys. Fluids 24, 1999: ι = dψ_pol/dψ_tor in Boozer coordinates.

Parameters

config : StellaratorConfig s : float or array Normalised toroidal flux label, s ∈ [0, 1].

Raises

ValueError If the configuration is non-physical or s leaves [0, 1].

stellarator_flux_surface

stellarator_flux_surface(config, s, n_theta=64, n_phi=64)

Compute a single flux surface in Boozer coordinates.

Boozer 1981, Phys. Fluids 24, 1999: (θ_B, ζ_B) are 2π-periodic straight field-line angles such that B · ∇θ_B / B · ∇ζ_B = ι.

|B| is represented as B₀(1 − ε_t cos θ − ε_h cos(N_fp ζ − ι θ)). This is the standard two-harmonic Boozer spectrum used for stellarator neoclassical calculations; Grieger et al. 1992, Phys. Fluids B 4, 2081.

Parameters

config : StellaratorConfig s : float Normalised toroidal flux, s ∈ (0, 1]. n_theta, n_phi : int Poloidal and toroidal grid resolution.

Returns

R : ndarray, shape (n_theta, n_phi) Major radius [m]. Z : ndarray, shape (n_theta, n_phi) Vertical position [m]. B : ndarray, shape (n_theta, n_phi) Magnetic field magnitude [T].

Raises

ValueError If the configuration is non-physical, s leaves (0, 1], or either grid resolution is below two points.

effective_ripple

effective_ripple(config, s)

Effective helical ripple ε_eff for neoclassical transport.

Nemov et al., Phys. Plasmas 6 (1999) 4622, Eq. 30. Analytic proxy: ε_eff ~ ε_h^(3/2) / √N_fp. Full evaluation requires field-line tracing (DKES/NEO-2).

Parameters

config : StellaratorConfig s : float Normalised toroidal flux, s ∈ (0, 1].

Returns

float Effective ripple ε_eff ∈ (0, 1).

Raises

ValueError If the configuration is non-physical or s leaves (0, 1].

iss04_scaling

iss04_scaling(config, n_e, P_heat)

ISS04 energy confinement scaling law for stellarators.

Yamada et al., Nucl. Fusion 45 (2005) 1684, Eq. 4: τ_E = 0.134 · a^2.28 · R^0.64 · P^-0.61 · n_e19^0.54 · B^0.84 · ι_{2/3}^0.41

Parameters

config : StellaratorConfig n_e : float Line-averaged electron density [10^19 m^-3]. P_heat : float Absorbed heating power [MW].

Returns

float Predicted energy confinement time [s].

Raises

ValueError If the configuration, density, or heating power is outside the positive finite ISS04 domain.

stellarator_neoclassical_chi

stellarator_neoclassical_chi(config, s, T_keV, n_e19)

Neoclassical thermal diffusivity in the 1/ν regime.

Beidler et al., Nucl. Fusion 51 (2011) 076001 — quasi-isodynamic transport. 1/ν regime: χ_neo ~ ε_eff^(3/2) · v_th² / (ν · R · N_fp)

Collision frequency from Braginskii

ν_ii ~ n_e e⁴ ln Λ / (4π ε₀² m_i² v_th³)

Parameters

config : StellaratorConfig s : float Normalised toroidal flux, s ∈ (0, 1]. T_keV : float Ion temperature [keV]. n_e19 : float Electron density [10^19 m^-3].

Returns

float Neoclassical χ [m²/s].

Raises

ValueError If the configuration is non-physical, s leaves (0, 1], or plasma inputs are not positive finite values.

Tearing Mode Coupling

tearing_mode_coupling

Coupled tearing mode dynamics including Chirikov overlap and inter-mode coupling.

Key references

Rutherford 1973 : P.H. Rutherford, Phys. Fluids 16, 1903 (1973). [classical tearing: dw/dt ∝ Δ'] La Haye 2006 : R.J. La Haye, Phys. Plasmas 13, 055501 (2006). [MRE coefficient fits a1 = 6.35 for bootstrap drive] La Haye & Buttery : R.J. La Haye & R.J. Buttery, Phys. Plasmas 16, 022107 2009 (2009). [cross-mode coupling coefficient Eq. 8] Chirikov 1979 : B.V. Chirikov, Phys. Rep. 52, 263 (1979). [overlap criterion σ = (w1 + w2)/(2 Δr) > 1 → stochastic, Eq. 3.1]

CoupledResult dataclass

CoupledResult(w1_trace, w2_trace, chirikov_trace, overlap_time, disruption)

Time traces from a coupled two-mode tearing evolution.

Attributes

w1_trace Island half-width of the first mode in metres, per step. w2_trace Island half-width of the second mode in metres, per step. chirikov_trace Chirikov overlap parameter σ per step. overlap_time Time at which the islands overlap (σ > 1) in seconds, or -1 if never. disruption True if island overlap triggered a disruption.

ChirikovOverlap

Overlap criterion for magnetic island stochasticity.

σ = (w₁ + w₂) / (2 Δr)

σ > 1 → overlapping separatrices → field-line stochasticity. Chirikov 1979, Phys. Rep. 52, 263, Eq. 3.1.

parameter staticmethod

parameter(w1, w2, delta_r)

Chirikov overlap parameter σ; Chirikov 1979, Eq. 3.1.

is_stochastic staticmethod

is_stochastic(sigma)

Return True when σ > 1 (Chirikov 1979, Eq. 3.1).

stochastic_region_width staticmethod

stochastic_region_width(w1, w2, delta_r)

Radial extent of stochastic region when σ > 1.

CoupledTearingModes

CoupledTearingModes(mode1, mode2, r_s1, r_s2, a, R0, B0)

Modified Rutherford Equation for two toroidally coupled tearing modes.

MRE for mode k (k = 1, 2): dw_k/dt = (r_sk / τ_Rk) [r_sk Δ'_k + a1 (j_bs/j_φ)(r_sk/w_k) × w_k²/(w_k² + w_d²) + C_kj w_j² / (r_s1 r_s2)]

Bootstrap drive coefficient a1 = 6.35: La Haye 2006, Phys. Plasmas 13, 055501, Table I. Cross-mode coupling coefficient C_kj: La Haye & Buttery 2009, Phys. Plasmas 16, 022107, Eq. 8. Classical tearing stability Δ': Rutherford 1973, Phys. Fluids 16, 1903.

coupling_coefficient

coupling_coefficient(m1, n1, m2, n2)

Cross-mode coupling coefficient C₁₂.

La Haye & Buttery 2009, Phys. Plasmas 16, 022107, Eq. 8: C₁₂ ≈ 0.5 × (a / R₀) This captures the leading-order inverse-aspect-ratio toroidal coupling between the 3/2 and 2/1 modes via the n=1 sideband. Modes with different n do not couple directly at this order.

delta_prime_1

delta_prime_1(w1, w2)

Effective Δ' for mode 1, modified by presence of mode 2.

Base linear Δ'₀ = −2 m₁ / r_s1 (classically stable). Rutherford 1973, Phys. Fluids 16, 1903, §III. Nonlinear cross-modification: La Haye & Buttery 2009, §II.

delta_prime_2

delta_prime_2(w1, w2)

Effective Δ' for mode 2; same references as delta_prime_1.

evolve

evolve(w1_0, w2_0, j_bs, j_phi, eta, dt, n_steps, seed_time=-1.0, seed_amplitude=0.0)

Integrate coupled MRE for both modes.

a1 (j_bs/j_φ) (r_s/w) w²/(w²+w_d²)

— La Haye 2006, Phys. Plasmas 13, 055501, Eq. 5; a1 = 6.35 Table I.

Coupling term: C₁₂ w₂²/(r_s1 r_s2) — La Haye & Buttery 2009, Phys. Plasmas 16, 022107, Eq. 8. Stochastic criterion: σ = (w1+w2)/(2Δr) > 1 — Chirikov 1979, Phys. Rep. 52, 263, Eq. 3.1.

SawtoothNTMSeeding

SawtoothNTMSeeding(sawtooth_cycler)

Sawtooth-crash seeding of neoclassical tearing modes.

Parameters

sawtooth_cycler Optional sawtooth-cycle model; None uses the analytic seed scaling.

seed_amplitude

seed_amplitude(crash_energy_MJ, r_s)

Seed island width ~ sqrt(δW_sawtooth).

Scaling from Porcelli et al. 1996, Plasma Phys. Control. Fusion 38, 2163, §4: w_seed ∝ √(δW) where δW is the sawtooth crash free energy.

seed_probability

seed_probability(crash_energy, threshold)

Probability that a sawtooth crash seeds an NTM.

Zero below the threshold crash energy, then 1 − exp(−(E − E_th)).

Parameters

crash_energy Sawtooth crash free energy; must be non-negative. threshold Seeding threshold energy; must be non-negative.

Returns

float Seeding probability in [0, 1].

DisruptionPath dataclass

DisruptionPath(warning_time_ms, avoidable)

Outcome of a seeded-NTM disruption-path assessment.

Attributes

warning_time_ms Time from seeding to island overlap in milliseconds, or -1 if no disruption occurred. avoidable True if removing the bootstrap drive (ideal ECCD) prevents the disruption.

DisruptionTriggerAssessment

DisruptionTriggerAssessment(coupled)

Assess whether a seeded NTM drives a disruption and if ECCD avoids it.

Parameters

coupled The coupled two-mode tearing model.

run_scenario

run_scenario(j_bs, j_phi, omega_phi, seed_energy)

Evolve a seeded NTM and test ECCD avoidance.

Seeds an island from the crash energy, evolves the coupled modes, then re-runs with the bootstrap drive zeroed to test avoidability.

Parameters

j_bs Bootstrap current density at the rational surface; must be non-negative. j_phi Total toroidal current density; must be positive. omega_phi Toroidal rotation frequency in rad/s; must be positive. seed_energy Sawtooth seed energy; must be non-negative.

Returns

DisruptionPath The warning time and whether the disruption is ECCD-avoidable.

TearingModeStabilityMap

Heuristic NTM stability map over the beta_N–internal-inductance plane.

scan_beta_li

scan_beta_li(beta_N_range, li_range)

Heuristic stability map: beta_N × l_i > 3.0 → unstable.

Rough guideline from ITER Physics Basis Ch.3 (Nucl. Fusion 39, 2175, 1999): combined beta–internal inductance drives NTM onset at high beta_N l_i product.

Vessel Model

vessel_model

Vacuum vessel eddy current model using a lumped-circuit approach.

Models the induction and decay of currents in the conducting vessel walls, providing passive stability effects and flux perturbations.

VesselElement dataclass

VesselElement(R, Z, resistance, cross_section, inductance, wall_thickness=0.04, conductivity=1350000.0)

Discrete conducting element of the vacuum vessel.

Parameters

R : float Major radius of the element center [m]. Z : float Vertical position of the element center [m]. resistance : float Electrical resistance of the toroidal loop [Ohm]. cross_section : float Cross-sectional area of the element [m^2]. inductance : float Self-inductance of the toroidal loop [H]. wall_thickness : float Effective wall thickness for τ_vessel calculation [m]. conductivity : float Electrical conductivity of wall material [S/m]. Stainless steel 316L: σ ≈ 1.35e6 S/m (Wesson 2011, App. C).

VesselModel

VesselModel(elements)

Lumped-circuit model for vessel eddy currents.

Solves M dI/dt + R I = −V_ext, where M is the mutual inductance matrix and V_ext is the induced loop voltage.

step

step(dt, dphi_ext_dt)

Advance eddy currents by one time step (explicit Euler).

M dI/dt + R I = −dΦ_ext/dt dI/dt = M⁻¹(−R I − dΦ_ext/dt)

Parameters

dt : float Time step [s]. dphi_ext_dt : np.ndarray Rate of change of external poloidal flux through each element [Wb/s].

Returns

np.ndarray — Updated eddy currents [A].

psi_vessel

psi_vessel(R, Z)

Poloidal flux contribution from vessel currents at observation points.

Parameters

R, Z : np.ndarray Observation points.

Returns

np.ndarray — Flux contribution [Wb/rad].

vessel_time_constant

vessel_time_constant(conductivity, wall_thickness, major_radius)

Return the resistive wall time constant τ_vessel.

τ_vessel = μ₀ σ d R

Wesson 2011, "Tokamaks", 4th ed., Eq. 6.6.6.

Parameters

conductivity : float Wall conductivity σ [S/m]. wall_thickness : float Wall thickness d [m]. major_radius : float Major radius R [m].

Returns

float — Time constant [s].

halo_current

halo_current(plasma_current, f_halo=0.3)

Return peak halo current I_halo = f_halo × TPF × I_p.

ITER Physics Basis 1999, Ch. 3, §3.8.3: f_halo is the fraction of plasma current flowing in the halo (ITER default 0.3), TPF ≈ 2 is the toroidal peaking factor.

Parameters

plasma_current : float Total plasma current I_p [A]. f_halo : float Halo current fraction (default 0.3, ITER Physics Basis).

Returns

float — Peak halo current [A].

halo_em_force

halo_em_force(halo_current_a, b_poloidal, path_length)

Return electromagnetic force on the vessel from halo currents.

F = I_halo × B_pol × L

Noll et al. 1993, Fusion Eng. Des. 22, 315 — electromagnetic loads on the first wall and blanket due to disruption halo currents.

Parameters

halo_current_a : float Peak halo current [A]. b_poloidal : float Poloidal field at the wall [T]. path_length : float Length of the current path in the wall [m].

Returns

float — Electromagnetic force [N].

VMEC Lite

vmec_lite

Simplified spectral 3D MHD equilibrium solver (VMEC-lite, fixed-boundary).

References

Hirshman, S. P. & Whitson, J. C., Phys. Fluids 26 (1983) 3553. Variational moment method for 3D MHD equilibria; spectral representation R(s,θ,ζ) = Σ R_mn cos(mθ − nζ), Z = Σ Z_mn sin(mθ − nζ) [Eqs. 1–2]. Freidberg, J. P., "Ideal MHD" (Cambridge, 2014), Ch. 3. Force balance ∇p = J × B in 3D geometry. Wesson, J., "Tokamaks" 4th ed. (Oxford, 2011), Ch. 3. Toroidal field B_φ ∝ 1/R; poloidal field from rotational transform.

VMECResult dataclass

VMECResult(R_mn, Z_mn, B_mn, force_residual, iterations, converged)

Converged VMEC-lite equilibrium solution.

Attributes

R_mn Fourier coefficients of the major-radius surface geometry. Z_mn Fourier coefficients of the vertical surface geometry. B_mn Fourier coefficients of the magnetic-field strength. force_residual Final MHD force-balance residual. iterations Number of iterations performed. converged Whether the force residual met the convergence tolerance.

VMECLiteClaimEvidence dataclass

VMECLiteClaimEvidence(schema_version, source, source_id, geometry_source, profile_source, current_assumption, model_id, n_s, m_pol, n_tor, n_fp, n_modes, force_residual, iterations, converged, min_major_radius, max_abs_z, pressure_axis, pressure_edge, iota_min, iota_max, q_min, q_max, r_mn_relative_error, z_mn_relative_error, iota_relative_error, residual_tolerance, spectral_relative_tolerance, iota_relative_tolerance, full_vmec_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for VMEC-lite claims.

SpectralBasis

SpectralBasis(m_pol, n_tor, n_fp)

Fourier basis for VMEC spectral representation.

Hirshman & Whitson 1983, Phys. Fluids 26, 3553, Eqs. 1–2: R(s,θ,ζ) = Σ_{m,n} R_mn(s) cos(mθ − n N_fp ζ) Z(s,θ,ζ) = Σ_{m,n} Z_mn(s) sin(mθ − n N_fp ζ)

The basis requires non-negative poloidal and toroidal truncation orders and a positive integer number of field periods.

evaluate

evaluate(coeffs_mn, theta, zeta, is_sin=False)

Evaluate spectral expansion at (θ, ζ) grid points.

Hirshman & Whitson 1983, Eq. 1–2: Σ C_mn cos(mθ − n N_fp ζ) or Σ C_mn sin(mθ − n N_fp ζ)

VMECLiteSolver

VMECLiteSolver(n_s=21, m_pol=3, n_tor=2, n_fp=1)

Spectral 3D equilibrium solver using steepest descent on the MHD energy.

Hirshman & Whitson 1983, Phys. Fluids 26, 3553 — variational moment method. Force balance ∇p = J × B (Freidberg 2014, Ch. 3) drives the Shafranov shift on the R₀₀ mode; radial tension (finite-difference Laplacian) regularises the spectral coefficients.

n_s must provide at least axis, interior, and boundary surfaces. Profile inputs are finite one-dimensional arrays; pressure is non-negative and rotational transform is positive for the reduced q-profile calculation.

set_boundary

set_boundary(R_bound, Z_bound)

Fix finite boundary Fourier coefficients at s = 1.

set_profiles

set_profiles(pressure, iota)

Interpolate finite pressure and rotational-transform profiles onto the solver grid.

solve

solve(max_iter=100, tol=0.0001)

Steepest-descent equilibrium iteration.

Hirshman & Whitson 1983, Phys. Fluids 26, 3553.

Force balance ∇p = J × B (Freidberg 2014, Ch. 3) drives the pressure-gradient Shafranov shift on R₀₀ via q²(dp/ds)/R₀. Radial tension (d²/ds² finite difference) regularises spectral modes.

max_iter must be a positive integer and tol must be positive and finite; invalid controls are rejected before iteration.

AxisymmetricTokamakBoundary

Low-order Fourier boundary for a shaped axisymmetric tokamak.

from_parameters staticmethod

from_parameters(R0, a, kappa, delta)

Low-order Fourier approximation of shaped tokamak boundary.

R = R₀ + a cos(θ + δ sin θ), Z = a κ sin θ Hirshman & Whitson 1983, Phys. Fluids 26, 3553, Eqs. 1–2.

StellaratorBoundary

Fourier boundary presets for stellarator configurations.

w7x_standard staticmethod

w7x_standard()

W7-X standard configuration boundary.

Grieger et al., Phys. Fluids B 4 (1992) 2081 — modular coil geometry. N_fp = 5.

vmec_lite_claim_evidence

vmec_lite_claim_evidence(solver, result, *, source, source_id, geometry_source, profile_source, current_assumption, model_id='bounded_vmec_lite', reference_R_mn=None, reference_Z_mn=None, reference_iota=None, residual_tolerance=0.001, spectral_relative_tolerance=0.05, iota_relative_tolerance=0.02)

Build fail-closed evidence for bounded VMEC-lite or full VMEC claims.

assert_vmec_lite_full_vmec_claim_admissible

assert_vmec_lite_full_vmec_claim_admissible(evidence)

Raise when VMEC-lite evidence is insufficient for a full VMEC claim.

save_vmec_lite_claim_evidence

save_vmec_lite_claim_evidence(evidence, path)

Persist VMEC-lite claim evidence as deterministic JSON.

VMECLiteClaimEvidence dataclass

VMECLiteClaimEvidence(schema_version, source, source_id, geometry_source, profile_source, current_assumption, model_id, n_s, m_pol, n_tor, n_fp, n_modes, force_residual, iterations, converged, min_major_radius, max_abs_z, pressure_axis, pressure_edge, iota_min, iota_max, q_min, q_max, r_mn_relative_error, z_mn_relative_error, iota_relative_error, residual_tolerance, spectral_relative_tolerance, iota_relative_tolerance, full_vmec_claim_allowed, claim_status)

Serialisable provenance and reference-comparison evidence for VMEC-lite claims.

vmec_lite_claim_evidence

vmec_lite_claim_evidence(solver, result, *, source, source_id, geometry_source, profile_source, current_assumption, model_id='bounded_vmec_lite', reference_R_mn=None, reference_Z_mn=None, reference_iota=None, residual_tolerance=0.001, spectral_relative_tolerance=0.05, iota_relative_tolerance=0.02)

Build fail-closed evidence for bounded VMEC-lite or full VMEC claims.

assert_vmec_lite_full_vmec_claim_admissible

assert_vmec_lite_full_vmec_claim_admissible(evidence)

Raise when VMEC-lite evidence is insufficient for a full VMEC claim.

save_vmec_lite_claim_evidence

save_vmec_lite_claim_evidence(evidence, path)

Persist VMEC-lite claim evidence as deterministic JSON.

Phase Modules

GK UPDE Bridge

gk_upde_bridge

Bridge between gyrokinetic transport fluxes and the 8-layer UPDE Kuramoto phase dynamics system.

Maps GK-computed growth rates and diffusivities into adaptive K_nm coupling modulation for layers P0 (microturbulence), P1 (zonal flows), P4 (transport barrier), and P5 (current profile).

Reference layer mappings

P0 ← max(gamma_ITG, gamma_TEM): turbulence drive P1 ← chi_e suppression ratio: zonal flow damping of transport P4 ← chi_i pedestal / chi_i core: transport barrier strength P5 ← bootstrap current contribution (via pressure gradient)

GKCouplingGains dataclass

GKCouplingGains(p0_p1_turbulence=0.5, p1_p4_transport=0.3, p1_p4_chi_clip_max=2.0, p3_p4_pedestal=0.4, diffusivity_floor=1e-10)

Calibration gains for GK-driven K_nm coupling modulation.

These are dimensionless SCPN phase-coupling calibration gains — multipliers on the baseline coupling that set how strongly each gyrokinetic transport channel modulates its UPDE layer pair. They are model tuning parameters, not values imported from external literature; they are exposed here so callers can recalibrate the bridge without editing the kernel.

Parameters

p0_p1_turbulence : float, default 0.5 Gain on the P0-P1 (microturbulence to zonal-flow) coupling driven by the dominant growth rate through tanh(max_gamma / gamma_ref). p1_p4_transport : float, default 0.3 Gain on the P1-P4 (zonal-flow to transport-barrier) coupling driven by the normalised electron heat diffusivity chi_e / chi_ref. p1_p4_chi_clip_max : float, default 2.0 Upper clip on the normalised chi_e / chi_ref ratio, bounding the P1-P4 modulation; the lower clip is fixed at 0. p3_p4_pedestal : float, default 0.4 Gain on the P3-P4 (sawtooth/ELM to transport-barrier) coupling driven by the pedestal-to-core ion-diffusivity ratio. diffusivity_floor : float, default 1e-10 Lower floor [m^2/s] applied to mean diffusivities before ratios are formed, preventing division by zero at vanishing transport.

__post_init__

__post_init__()

Validate that all gains are finite and physically admissible.

adaptive_knm

adaptive_knm(K_base, gk_output, chi_i_profile=None, gamma_ref=0.2, chi_ref=1.0, gains=None)

Modulate K_nm based on GK fluxes.

Parameters

K_base : array, shape (L, L) Baseline coupling matrix from build_knm_plasma(). gk_output : GKOutput GK solver output (growth rates, fluxes). chi_i_profile : array or None Full chi_i(rho) profile for pedestal ratio calculation. gamma_ref : float Reference growth rate for tanh scaling [c_s/a]. chi_ref : float Reference chi_e for transport modulation [m^2/s]. gains : GKCouplingGains or None, optional Calibration gains for the coupling modulation. Defaults to :class:GKCouplingGains with the standard SCPN values.

Returns

numpy.ndarray, shape (L, L) The modulated coupling matrix. A copy of K_base is returned unmodulated when L < 6: the P0-P5 layer mapping needs at least six layers, and a warning is logged in that case rather than silently passing the matrix through.

Raises

ValueError If K_base is not square or not finite, chi_e is negative or non-finite, gamma_ref or chi_ref is non-positive, or chi_i_profile contains non-finite or negative values.

gk_natural_frequencies

gk_natural_frequencies(omega_base, gk_output, gamma_scale=0.1)

Adjust layer-0 natural frequency based on GK growth rate.

The turbulence layer's effective frequency increases with the dominant instability growth rate.

SCPN Compiler and Replay Modules

FPGA Export

scpn_control.scpn.fpga_export writes bounded HDL project files and tamper-evident export evidence. It does not claim to emit a deployable FPGA bitstream. Facility or hardware claims require qualified synthesis report evidence through assert_hdl_export_claim_admissible.

fpga_export

Export a CompiledNet to generated HDL project files for FPGA toolchains.

Generates a bounded project directory with HDL source, weight ROM initialisation, timing constraints, and a Makefile for Vivado or Quartus. The repository does not claim to emit a device bitstream; hardware claims require separate synthesis, timing, and bitstream evidence through HDLExportEvidence.

FPGAConfig dataclass

FPGAConfig(target='xilinx', clock_mhz=100.0, lif_bit_width=16, n_neurons_max=256, fifo_depth=16)

FPGA synthesis target configuration.

HDLExportEvidence dataclass

HDLExportEvidence(schema_version, generated_utc, controller_artifact_sha256, target, target_part, clock_mhz, lif_bit_width, n_neurons, n_places, bitstream_length, hdl_format, hdl_sha256, weights_mem_sha256, constraints_sha256, makefile_sha256, project_sha256, resource_estimate_sha256, estimated_lut_count, estimated_ff_count, estimated_bram_blocks, weight_rom_depth, estimated_fmax_mhz, synthesis_toolchain, synthesis_report_sha256, synthesis_report_uri, timing_slack_ns, facility_claim_allowed, claim_status, payload_sha256)

Tamper-evident FPGA HDL export and synthesis-admission evidence.

compile_to_verilog

compile_to_verilog(compiled_net, config)

Generate synthesisable Verilog HDL for the LIF neuron array.

compile_to_vhdl

compile_to_vhdl(compiled_net, config)

Generate synthesisable VHDL for the LIF neuron array.

estimate_resources

estimate_resources(compiled_net, config)

Estimate FPGA resource usage for the compiled net.

Returns dict with keys: lut_count, ff_count, bram_blocks, estimated_fmax_mhz.

export_bitstream_project

export_bitstream_project(compiled_net, config, output_dir)

Write a bounded FPGA HDL project directory.

Creates

top.v / top.vhd — HDL source weights.mem — weight ROM init constraints.xdc / constraints.sdc — timing Makefile — synthesis flow

The historical function name is retained for API compatibility. This function does not run a vendor toolchain or emit a device bitstream.

hdl_export_evidence

hdl_export_evidence(compiled_net, config, project_dir, *, controller_artifact_sha256, target_part, generated_utc=None, synthesis_toolchain=None, synthesis_report_sha256=None, synthesis_report_uri=None, timing_slack_ns=None, facility_claim_allowed=False)

Build tamper-evident FPGA HDL export evidence for a generated project.

assert_hdl_export_claim_admissible

assert_hdl_export_claim_admissible(evidence, *, artifact_root=None)

Fail closed unless HDL export evidence supports a facility hardware claim.

save_hdl_export_evidence

save_hdl_export_evidence(evidence, output_path)

Persist HDL export evidence as sorted JSON.

load_hdl_export_evidence

load_hdl_export_evidence(path, *, require_facility_claim=False, artifact_root=None)

Load HDL export evidence with duplicate-key and digest admission.

HDLExportEvidence dataclass

HDLExportEvidence(schema_version, generated_utc, controller_artifact_sha256, target, target_part, clock_mhz, lif_bit_width, n_neurons, n_places, bitstream_length, hdl_format, hdl_sha256, weights_mem_sha256, constraints_sha256, makefile_sha256, project_sha256, resource_estimate_sha256, estimated_lut_count, estimated_ff_count, estimated_bram_blocks, weight_rom_depth, estimated_fmax_mhz, synthesis_toolchain, synthesis_report_sha256, synthesis_report_uri, timing_slack_ns, facility_claim_allowed, claim_status, payload_sha256)

Tamper-evident FPGA HDL export and synthesis-admission evidence.

hdl_export_evidence

hdl_export_evidence(compiled_net, config, project_dir, *, controller_artifact_sha256, target_part, generated_utc=None, synthesis_toolchain=None, synthesis_report_sha256=None, synthesis_report_uri=None, timing_slack_ns=None, facility_claim_allowed=False)

Build tamper-evident FPGA HDL export evidence for a generated project.

assert_hdl_export_claim_admissible

assert_hdl_export_claim_admissible(evidence, *, artifact_root=None)

Fail closed unless HDL export evidence supports a facility hardware claim.

save_hdl_export_evidence

save_hdl_export_evidence(evidence, output_path)

Persist HDL export evidence as sorted JSON.

load_hdl_export_evidence

load_hdl_export_evidence(path, *, require_facility_claim=False, artifact_root=None)

Load HDL export evidence with duplicate-key and digest admission.

Geometry Neutral Contracts

geometry_neutral_contracts

Geometry-neutral control contracts for non-tokamak replay adapters.

MagneticConfiguration dataclass

MagneticConfiguration(name, device_class, field_periods, coordinate_system, reference)

Magnetic-device metadata independent of tokamak axis observations.

to_dict

to_dict()

Return the JSON-serialisable representation of the configuration.

ActuatorChannel dataclass

ActuatorChannel(name, unit, min_value, max_value, slew_rate_per_s, latency_steps=0, failure_mode='none')

Bounded actuator channel with slew-rate and latency semantics.

clamp

clamp(value)

Clamp a value to the channel's [min, max] envelope.

Parameters

value Requested actuator value in the channel unit; must be finite.

Returns

float The value clamped to [min_value, max_value].

apply_slew

apply_slew(*, previous, requested, dt_s)

Apply slew-rate and range limits to a requested set-point.

Parameters

previous Previous actuator value in the channel unit; must be finite. requested Requested new value; clamped to the channel envelope first. dt_s Time step in seconds; must be positive.

Returns

float The new value, limited to a slew_rate_per_s × dt_s change and the channel envelope.

to_dict

to_dict()

Return the JSON-serialisable representation of the channel.

ActuatorSet dataclass

ActuatorSet(channels)

Unique named actuator channels.

by_name

by_name(name)

Return the actuator channel with the given name.

Parameters

name Channel name to look up.

Returns

ActuatorChannel The matching channel.

Raises

KeyError If no channel has that name.

to_dict

to_dict()

Return the JSON-serialisable representation of the actuator set.

DiagnosticChannel dataclass

DiagnosticChannel(name, value, unit, sigma, provenance)

One measured or replayed diagnostic value.

to_dict

to_dict()

Return the JSON-serialisable representation of the diagnostic channel.

DiagnosticFrame dataclass

DiagnosticFrame(step, time_s, channels)

Replay diagnostic frame at one control tick.

as_mapping

as_mapping()

Return a {channel name: value} mapping for this frame.

to_dict

to_dict()

Return the JSON-serialisable representation of the diagnostic frame.

ControlObjective dataclass

ControlObjective(target_metrics, weights, constraints)

Named targets, weights, and hard constraints for a replay scenario.

to_dict

to_dict()

Return the JSON-serialisable representation of the objective.

ReplayScenario dataclass

ReplayScenario(name, seed, steps, dt_s, magnetic_configuration, actuator_set, objective, initial_frame, fault_schedule)

Deterministic replay scenario contract for compact control adapters.

to_dict

to_dict()

Return the JSON-serialisable representation of the replay scenario.

Geometry Neutral Replay

geometry_neutral_replay

Compact geometry-neutral stellarator replay through the SCPN controller.

GeometryNeutralReplayEvidence dataclass

GeometryNeutralReplayEvidence(schema_version, generated_utc, replay_schema_version, replay_report_sha256, scenario_digest, trace_digest, metrics_digest, thresholds_digest, magnetic_configuration_reference, actuator_calibration, latency_model, fault_model, final_fieldline_spread, improvement_fraction, max_abs_current_A, p95_latency_us, deterministic, passes_thresholds, measured_or_benchmark_artefact_sha256, device_claim_allowed, claim_status, payload_sha256)

Tamper-evident geometry-neutral replay evidence admission object.

load_replay_schema

load_replay_schema(version)

Load a bundled geometry-neutral replay JSON Schema document.

register_v1_1_schema

register_v1_1_schema()

Return the bundled v1.1 replay schema after self-identification checks.

assert_v1_replay_loadable_under_v1_1_schema_bundle

assert_v1_replay_loadable_under_v1_1_schema_bundle(report)

Validate a v1 replay report while the v1.1 schema bundle is installed.

build_aer_admission_metadata

build_aer_admission_metadata(*, admission_report, decode_strategy, decode_window_ns, n_features, feature_normalisation='unit', require_monotonic=False, feature_vector=None)

Build replay-safe AER admission metadata from a decoded observation.

attach_aer_admission_metadata

attach_aer_admission_metadata(report, aer_admission)

Attach digest-bound AER admission metadata to a replay v1.1 report.

generate_report

generate_report(*, steps=12, seed=314159)

Generate a deterministic compact SCPN-control replay report.

generate_geometry_neutral_report

generate_geometry_neutral_report(*, steps=12, seed=314159)

Public package alias for :func:generate_report.

validate_report

validate_report(report)

Validate the compact replay report contract without external packages.

validate_geometry_neutral_report

validate_geometry_neutral_report(report)

Public package alias for :func:validate_report.

save_geometry_neutral_replay_report

save_geometry_neutral_replay_report(report, output_path)

Persist a validated geometry-neutral replay report as stable JSON.

load_geometry_neutral_replay_report

load_geometry_neutral_replay_report(path)

Load and validate a geometry-neutral replay report with duplicate-key checks.

geometry_neutral_replay_evidence

geometry_neutral_replay_evidence(report, *, generated_utc=None, measured_or_benchmark_artefact_sha256=None, device_claim_allowed=False)

Build tamper-evident replay evidence over an admitted replay report.

assert_geometry_neutral_replay_claim_admissible

assert_geometry_neutral_replay_claim_admissible(evidence)

Fail closed unless replay evidence supports a device-control claim.

save_geometry_neutral_replay_evidence

save_geometry_neutral_replay_evidence(evidence, output_path)

Persist geometry-neutral replay evidence as sorted JSON.

load_geometry_neutral_replay_evidence

load_geometry_neutral_replay_evidence(path, *, require_device_claim=False)

Load geometry-neutral replay evidence with duplicate-key and digest admission.

render_markdown

render_markdown(report)

Render a geometry-neutral-replay report as a Markdown document.

Parameters

report The benchmark report mapping produced by the replay run.

Returns

str The Markdown report text.

render_geometry_neutral_markdown

render_geometry_neutral_markdown(report)

Public package alias for :func:render_markdown.

main

main(argv=None)

Run the geometry-neutral stellarator replay benchmark CLI.

Parameters

argv Command-line arguments; defaults to sys.argv when None.

Returns

int Process exit code (non-zero on a strict-mode failure).

GeometryNeutralReplayEvidence dataclass

GeometryNeutralReplayEvidence(schema_version, generated_utc, replay_schema_version, replay_report_sha256, scenario_digest, trace_digest, metrics_digest, thresholds_digest, magnetic_configuration_reference, actuator_calibration, latency_model, fault_model, final_fieldline_spread, improvement_fraction, max_abs_current_A, p95_latency_us, deterministic, passes_thresholds, measured_or_benchmark_artefact_sha256, device_claim_allowed, claim_status, payload_sha256)

Tamper-evident geometry-neutral replay evidence admission object.

geometry_neutral_replay_evidence

geometry_neutral_replay_evidence(report, *, generated_utc=None, measured_or_benchmark_artefact_sha256=None, device_claim_allowed=False)

Build tamper-evident replay evidence over an admitted replay report.

assert_geometry_neutral_replay_claim_admissible

assert_geometry_neutral_replay_claim_admissible(evidence)

Fail closed unless replay evidence supports a device-control claim.

save_geometry_neutral_replay_evidence

save_geometry_neutral_replay_evidence(evidence, output_path)

Persist geometry-neutral replay evidence as sorted JSON.

load_geometry_neutral_replay_evidence

load_geometry_neutral_replay_evidence(path, *, require_device_claim=False)

Load geometry-neutral replay evidence with duplicate-key and digest admission.

Studio Vertical

CONTROL's SCPN STUDIO vertical expresses its verbs and evidence on the locked scpn-studio-platform contract (installed via the optional studio extra). It consumes the platform SDK rather than forking it: verbs are declared as platform Verb records, results map to platform EvidenceBundle records, and the capability manifest is the platform CapabilityManifest.

Studio Verbs

verbs

The SCPN-CONTROL studio's verbs, expressed on the locked platform contract.

CONTROL is the control-grade integration laboratory of the SCPN ecosystem, so its verbs are control actions and evidence/admission surfaces rather than solver math. Six are drawn from the shared :data:scpn_studio_platform.verbs.CORE_VERBS spine (reconstruct, simulate, analyse, validate, benchmark, replay); five are domain-distinctive to a control studio (regulate, certify, predict, mitigate, monitor).

Each is declared as a :class:scpn_studio_platform.verbs.Verb carrying the attribute contract the Hub federates and gates against. Two declarations exercise CONTROL's distinctive contributions to the v1 contract directly:

  • regulate is the only verb in the ecosystem so far that is both realtime (a hard deadline_us from the Rust control cycle) and live-hardware (it drives real actuators through HIL/CODAC/EPICS), so the Hub must hard-gate it per tenant.
  • certify is a certified-tier verb whose evidence is machine-checked (formally-proven Petri/Z3/Lean certificates on the bundle), not a measurement.

STUDIO_ID module-attribute

STUDIO_ID = 'scpn-control'

The studio identifier this vertical implements (also the federation name).

RECONSTRUCT module-attribute

RECONSTRUCT = Verb(name='reconstruct', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(INTERACTIVE), fidelity=REDUCED_ORDER, produces=(EFIT_RECONSTRUCTION_SCHEMA,), backends=('python', 'rust'))

Reconstruct an equilibrium from magnetic diagnostics (real-time EFIT-lite).

SIMULATE module-attribute

SIMULATE = Verb(name='simulate', safety_tier=RESEARCH, side_effect=SIMULATED, timing=Timing(BATCH), fidelity=REDUCED_ORDER, produces=(SCENARIO_SIMULATION_SCHEMA,), backends=('python', 'rust'))

Roll out a closed-loop control scenario on a low-order plant model.

ANALYSE module-attribute

ANALYSE = Verb(name='analyse', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(BATCH), produces=(EQUILIBRIUM_ANALYSIS_SCHEMA,), backends=('python',))

Extract macroscopic shape and stability descriptors from an equilibrium.

VALIDATE module-attribute

VALIDATE = Verb(name='validate', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(BATCH), produces=(PHYSICS_VALIDATION_SCHEMA,), backends=('python',))

Check a bounded physics claim against its traceability reference.

BENCHMARK module-attribute

BENCHMARK = Verb(name='benchmark', safety_tier=RESEARCH, side_effect=SIMULATED, timing=Timing(BATCH), produces=(CONTROLLER_LATENCY_SCHEMA,), backends=('python', 'rust'))

Measure per-controller and native-handoff control-cycle latency.

REPLAY module-attribute

REPLAY = Verb(name='replay', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(BATCH), produces=(GEOMETRY_NEUTRAL_REPLAY_SCHEMA,), backends=('python',))

Replay a recorded geometry-neutral control run with provenance.

REGULATE module-attribute

REGULATE = Verb(name='regulate', safety_tier=CERTIFIED, side_effect=LIVE_HARDWARE, timing=Timing(REALTIME, deadline_us=CONTROL_CYCLE_DEADLINE_US, schedulability=DECLARED), fidelity=REDUCED_ORDER, produces=(CONTROLLER_RUN_SCHEMA,), backends=('python', 'rust'))

Run a controller loop against a plant under explicit constraints (control/regulate).

CERTIFY module-attribute

CERTIFY = Verb(name='certify', safety_tier=CERTIFIED, side_effect=READ_ONLY, timing=Timing(BATCH), produces=(SAFETY_CERTIFICATE_SCHEMA,), backends=('python',))

Issue a machine-checked runtime safety certificate (admit/certify).

PREDICT module-attribute

PREDICT = Verb(name='predict', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(INTERACTIVE), fidelity=REDUCED_ORDER, produces=(DISRUPTION_PREDICTION_SCHEMA,), backends=('python',))

Predict disruption risk from toroidal-asymmetry observables.

MITIGATE module-attribute

MITIGATE = Verb(name='mitigate', safety_tier=CERTIFIED, side_effect=LIVE_HARDWARE, timing=Timing(INTERACTIVE), produces=(DISRUPTION_MITIGATION_SCHEMA,), backends=('python',))

Actuate shattered-pellet / disruption mitigation on the coil set.

MONITOR module-attribute

MONITOR = Verb(name='monitor', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(INTERACTIVE), produces=(PHASE_SYNC_MONITOR_SCHEMA,), backends=('python',))

Stream live phase-sync coherence and guard-halt telemetry.

VERIFY module-attribute

VERIFY = Verb(name='verify', safety_tier=RESEARCH, side_effect=READ_ONLY, timing=Timing(BATCH), fidelity=REDUCED_ORDER, produces=(PARITY_REFUTATION_SCHEMA,), backends=('python', 'rust'))

Verify cross-implementation numerical parity between the Python and Rust backends (verify/cross-check).

CONTROL_VERBS module-attribute

Every verb the CONTROL studio advertises, in manifest order.

core_verbs

core_verbs()

Return the CONTROL verbs drawn from the shared core spine.

Returns

tuple[Verb, ...] The subset of :data:CONTROL_VERBS whose names are in the platform :data:scpn_studio_platform.verbs.CORE_VERBS.

domain_verbs

domain_verbs()

Return the CONTROL verbs distinctive to a control studio.

Returns

tuple[Verb, ...] The subset of :data:CONTROL_VERBS not present in the platform core spine (regulate, certify, predict, mitigate, monitor).

evidence_schemas

evidence_schemas()

Return the distinct evidence schemas every CONTROL verb emits.

Returns

tuple[str, ...] The sorted, de-duplicated union of each verb's produces schemas.

Studio Evidence Bundles

evidence

Emit studio.*.v1 evidence bundles for CONTROL results (schema B).

This module is the crux of CONTROL's vertical: it maps CONTROL's existing, provenance-graded result surfaces onto the locked platform :class:scpn_studio_platform.evidence.EvidenceBundle. Three mappers exercise the contract's honesty invariant from three different sides, which is exactly why CONTROL is the contract's second-domain test:

  • An EFIT reconstruction is a real least-squares inverse but only closure-validated against synthetic diagnostics, so its claim is bounded-model and does not render as validated — the Hub shows the boundary verbatim (the honesty lever CONTROL contributed).
  • A runtime safety certificate is formally-proven: it carries a :class:scpn_studio_platform.evidence.FormalCertificate whose subject_digest binds the proof to the exact Petri-net topology it ran on, so the Hub voids the proof the moment the live controller's topology drifts.
  • A controller-latency result is a measured local benchmark, not a hardware-in-the-loop guarantee, so its claim is bounded-model too.

None of these outcomes is a CONTROL-private rule; each falls out of the shared :class:scpn_studio_platform.evidence.ClaimBoundary lattice and the orthogonal :class:scpn_studio_platform.evidence.EvidenceKind axis.

EfitReconstructionResult dataclass

EfitReconstructionResult(ip_reconstructed_a, chi_squared, n_iterations, nr, nz, input_digest, result_digest)

The path-free result of a real-time EFIT-lite reconstruction.

Mirrors the public shape of RealtimeEFIT.reconstruct reduced to the fields the evidence bundle needs. The reconstruction is closure-validated against synthetic diagnostics only, so its claim stays bounded.

Parameters

ip_reconstructed_a Plasma current recovered from the fitted flux, in amperes. chi_squared The measured weighted least-squares residual (not forced). n_iterations The number of outer Picard iterations actually run. nr, nz Radial and vertical grid sizes of the R-Z reconstruction grid. input_digest SHA-256 of the diagnostic/configuration inputs. result_digest SHA-256 of the reconstructed flux and profile coefficients.

Raises

ValueError If a count is non-positive or a digest is empty.

__post_init__

__post_init__()

Validate counts and digests.

SafetyCertificateResult dataclass

SafetyCertificateResult(theorem_id, checker, checker_version, proof_digest, petri_topology_sha256, live_topology_sha256, result_digest, non_vacuous=False)

The path-free result of a runtime safety certificate replay.

Mirrors scpn_control.scpn.runtime_safety_certificate: a machine-checked CTL/LTL proof over a compiled StochasticPetriNet bound to the exact Petri-net topology digest the proof ran on.

Parameters

theorem_id Identifier of the proven safety/liveness obligation. checker, checker_version The proof engine and its exact version (e.g. "z3" / "4.13.0"). proof_digest SHA-256 of the proof artifact itself. petri_topology_sha256 SHA-256 of the Petri-net topology the proof was established against — the certificate's subject. live_topology_sha256 SHA-256 of the live controller's topology at replay time. result_digest SHA-256 of the issued certificate record. non_vacuous Whether the obligation was checked to be non-vacuous.

Raises

ValueError If any digest or identity field is empty.

topology_matches property

topology_matches

Whether the live controller topology matches the proven one.

__post_init__

__post_init__()

Validate that the identifying fields are non-empty.

ParityRefutationResult dataclass

ParityRefutationResult(solver_method, grid, pearson_r, interior_l2_rel, gs_residual_plateau, target_rtol, result_digest, raw_reference)

The path-free result of a cross-backend solver-parity check.

Mirrors tests/test_rust_python_parity.py: the Python and Rust FusionKernel run the same equilibrium solver on the same grid and their final Psi fields are compared. For the SOR solver the hypothesis "the backends agree to target_rtol" is TESTED and REFUTED with raw counts (PARITY-1) — both backends reach the same equilibrium structure (high pearson_r, identical extrema) but SOR's update-norm stopping criterion plateaus at gs_residual_plateau far from the true GS solution, so the interior fields halt interior_l2_rel apart and exact-value parity is not reachable via SOR.

Parameters

solver_method The solver whose cross-backend parity was checked (e.g. "sor"). grid Opaque description of the test grid/case (e.g. the Solov'ev parameters). pearson_r Structural correlation of the two backends' final fields (~0.999 for SOR). interior_l2_rel Relative interior L2 gap at halt (the refutation magnitude; ~0.06 for SOR). gs_residual_plateau The Grad-Shafranov residual the SOR update-norm criterion plateaus at (~4). target_rtol The exact-value parity tolerance that is not reached (e.g. 1e-3). result_digest SHA-256 of the canonical result payload. raw_reference Pointer to the originating test / tracked finding (e.g. "PARITY-1").

ControllerLatencyResult dataclass

ControllerLatencyResult(controller, active_backend, reference_backend, p50_us, p95_us, p99_us, sample_count, input_digest, result_digest)

The path-free result of a control-cycle latency benchmark.

Mirrors the native-handoff / controller-latency reports: a measured local benchmark with explicit percentiles and a reference backend, but not a hardware-in-the-loop real-time guarantee.

Parameters

controller Name of the controller benchmarked. active_backend The backend timed (e.g. "rust"). reference_backend The reference backend the active one is compared against (e.g. "python"). p50_us, p95_us, p99_us Latency percentiles in microseconds. sample_count The number of timed iterations. input_digest, result_digest SHA-256 of the benchmark configuration and the recorded percentiles.

Raises

ValueError If a percentile is non-positive, the sample count is too small, or a digest is empty.

__post_init__

__post_init__()

Validate percentiles, sample count, and digests.

TraceabilityClaim dataclass

TraceabilityClaim(component, module_path, fidelity_status, public_claim_allowed, validity_domain, blocking_dependency=None)

A path-free physics-traceability registry claim.

Mirrors one entry of validation/physics_traceability.json reduced to the fields the bundle needs: the component, its module, the fidelity status, whether its public claim is admitted, and the qualitative validity-domain prose.

Parameters

component Human-readable name of the claim. module_path Source module the claim covers. fidelity_status One of reference_validated / bounded_model / validation_gap / external_dependency_blocked. public_claim_allowed Whether the public (facility) claim is admitted. validity_domain The qualitative scope prose (no fabricated numeric ranges). blocking_dependency The missing dependency, required when fidelity_status is external_dependency_blocked.

Raises

ValueError If a required string is empty, the status is unknown, or a blocked status carries no blocking dependency.

__post_init__

__post_init__()

Validate the fields and the status/blocking-dependency invariant.

ReplaySummary dataclass

ReplaySummary(scenario_digest, trace_digest, result_digest, max_abs_current_a, p95_latency_us, device_claim_allowed)

Path-free summary of a geometry-neutral replay.

Mirrors the fields of GeometryNeutralReplayEvidence the bundle needs.

Parameters

scenario_digest, trace_digest SHA-256 digests of the replayed scenario and trace. result_digest SHA-256 of the replay evidence payload. max_abs_current_a Peak absolute coil current over the replay, in amperes. p95_latency_us 95th-percentile control-cycle latency over the replay, in microseconds. device_claim_allowed Whether the device (facility) claim is admitted.

Raises

ValueError If a digest is empty or a magnitude is negative.

__post_init__

__post_init__()

Validate the digests and magnitudes.

MonitorSnapshot dataclass

MonitorSnapshot(tick, r_global, lambda_exp, guard_approved, latency_us)

Path-free phase-sync monitor snapshot.

Mirrors the dashboard snapshot returned by RealtimeMonitor.tick.

Parameters

tick Monotonic tick index of the snapshot. r_global Global Kuramoto coherence in [0, 1]. lambda_exp Estimated largest Lyapunov exponent. guard_approved Whether the Lyapunov guard approved the step. latency_us Tick wall time in microseconds.

Raises

ValueError If the tick or latency is negative, or coherence is outside [0, 1].

__post_init__

__post_init__()

Validate the snapshot ranges.

DisruptionPrediction dataclass

DisruptionPrediction(risk, observable_count, result_digest)

Path-free disruption-risk prediction.

Mirrors predict_disruption_risk output: a risk in [0, 1] from a hand-tuned fixed-weight baseline (U-015), not a model trained on a real disruption database — so its claim is a validation gap.

Parameters

risk The predicted disruption risk in [0, 1]. observable_count Number of toroidal-asymmetry observables the prediction used. result_digest SHA-256 of the prediction inputs/output.

Raises

ValueError If the risk is outside [0, 1], the count is negative, or the digest is empty.

__post_init__

__post_init__()

Validate the risk range, count, and digest.

EquilibriumAnalysis dataclass

EquilibriumAnalysis(r0, a, kappa, q95, beta_pol, li, result_digest)

Path-free macroscopic equilibrium-shape analysis.

Mirrors the ShapeParams derived from a reconstruction.

Parameters

r0, a, kappa Major radius, minor radius, elongation. q95, beta_pol, li Safety factor at 95% flux, poloidal beta, internal inductance. result_digest SHA-256 of the analysis output.

Raises

ValueError If a positive-definite geometry value is non-positive or the digest is empty.

__post_init__

__post_init__()

Validate geometry positivity and the digest.

ControllerRunResult dataclass

ControllerRunResult(controller, n_steps, mean_tracking_error, max_abs_action, max_abs_coil_current, result_digest)

Path-free summary of a closed-loop controller run against a plant.

Mirrors the run summary of a closed-loop shape/current controller (for example run_neural_mpc_simulation): the controller, the number of control steps, the realised mean tracking error, and the peak coil action/current the run drove. The run is a closed loop on a surrogate low-order plant, not a facility-certified live-hardware control outcome.

Parameters

controller Name of the controller run. n_steps Number of closed-loop control steps executed. mean_tracking_error Mean state-tracking error over the run, in metres (the controlled state is the magnetic-axis / X-point position). max_abs_action Peak absolute per-step coil action over the run, in kA-turn. max_abs_coil_current Peak absolute coil current over the run, in kA-turn. result_digest SHA-256 of the run summary.

Raises

ValueError If the controller name is empty, the step count is below one, a magnitude is negative, or the digest is empty.

__post_init__

__post_init__()

Validate the controller name, step count, magnitudes, and digest.

MitigationRun dataclass

MitigationRun(neon_quantity_mol, argon_quantity_mol, xenon_quantity_mol, z_eff, final_current_ma, sample_count, result_digest)

Path-free summary of a shattered-pellet-injection mitigation run.

Mirrors the deterministic summary of run_spi_mitigation: the injected noble-gas dose per species, the post-injection effective charge, the plasma current at the end of the current quench, and the simulated sample count.

Parameters

neon_quantity_mol, argon_quantity_mol, xenon_quantity_mol Injected noble-gas dose per species, in moles. z_eff Post-injection effective charge (>= 1). final_current_ma Plasma current at the end of the current quench, in MA. sample_count Number of simulated time samples. result_digest SHA-256 of the mitigation run summary.

Raises

ValueError If a dose is negative, z_eff is below one, the sample count is below one, or the digest is empty.

__post_init__

__post_init__()

Validate the doses, effective charge, sample count, and digest.

ScenarioSimulationRun dataclass

ScenarioSimulationRun(scenario_name, config_digest, n_steps, t_start_s, t_end_s, module_count, audit_passed)

Path-free summary of an integrated-scenario simulation rollout.

Mirrors a ScenarioCouplingMetadata together with its audit pass flag (from audit_scenario_coupling): the scenario, the digest of the exact config used, the step count and time window, the participating-module count, and whether the bounded replay-coupling audit passed.

Parameters

scenario_name Name of the scenario. config_digest SHA-256 of the exact ScenarioConfig used. n_steps Number of simulated time steps. t_start_s, t_end_s Scenario time window in seconds (t_end_s must exceed t_start_s). module_count Number of physics/control modules in the coupling exchange. audit_passed Whether the bounded replay-coupling audit passed.

Raises

ValueError If a required string is empty, the step count is below one, the time window is empty, or the module count is below one.

__post_init__

__post_init__()

Validate the identity, step count, time window, and module count.

canonical_digest

canonical_digest(payload)

Return the SHA-256 hex of a canonical-JSON payload.

Parameters

payload A JSON-serialisable mapping.

Returns

str The lowercase hex SHA-256 over the canonical (sorted-key, tight-separator, NaN-rejecting) JSON encoding — the same convention the rest of the ecosystem uses so a result's digest matches across the studio boundary.

efit_reconstruction_evidence

efit_reconstruction_evidence(result, *, operator, studio_version, started, ended, host=None)

Build the studio.efit-reconstruction.v1 bundle.

The inverse is real (measured chi-squared, recovered Ip) but only closure-validated against synthetic diagnostics, so the claim is bounded-model and the runtime admission is rejected for facility use — the bundle does not render as validated.

Parameters

result The path-free reconstruction result. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio that produced the result. started, ended ISO-8601 start/end timestamps (passed in; no hidden clock). host Optional host descriptor the reconstruction ran on.

Returns

EvidenceBundle A schema-B bundle whose renders_as_validated is False.

safety_certificate_evidence

safety_certificate_evidence(result, *, operator, studio_version, started, ended)

Build the studio.safety-certificate.v1 bundle (formally-proven).

The bundle carries a :class:FormalCertificate whose subject_digest is the proven Petri-net topology. The claim renders as validated only when the proof holds for the live topology (no drift); a drifted topology degrades the claim to validation-gap / rejected so the Hub never presents a voided proof as established.

Parameters

result The path-free certificate replay result. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A formally-proven schema-B bundle; admissible iff the proof covers the live subject.

parity_refutation_evidence

parity_refutation_evidence(result, *, operator, studio_version, started, ended)

Build the studio.parity-refutation.v1 bundle (falsified / refuted).

A cross-backend numerical-parity hypothesis that was TESTED and REFUTED with raw counts — distinct from an untested validation-gap. It is carried as evidence_kind=falsified + claim_status=refuted (admission rejected) so the ledger records a promoted negative finding: a refuted parity can never render as a validated parity claim, and the raw ParityCheck/Convergence counts travel with it so a consumer can see exactly how it failed.

Returns

EvidenceBundle A falsified schema-B bundle whose renders_as_validated is False.

controller_latency_evidence

controller_latency_evidence(result, *, operator, studio_version, started, ended, host=None)

Build the studio.controller-latency.v1 bundle (measured benchmark).

A measured local benchmark is real but bounded — it is not a hardware-in-the-loop real-time guarantee — so the claim is bounded-model / rejected and does not render as validated.

Parameters

result The path-free latency result. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps. host Optional host descriptor the benchmark ran on.

Returns

EvidenceBundle A measured schema-B bundle whose renders_as_validated is False.

physics_validation_evidence

physics_validation_evidence(claim, *, operator, studio_version, started, ended)

Build a studio.physics-validation.v1 bundle for a traceability claim.

Carries the claim's place on the seven-state lattice, its runtime admission (from public_claim_allowed), and its qualitative validity-domain prose as a :class:ValidityDomain note — the honest scope without fabricated numeric ranges. Only the single reference-validated, admitted claim renders as validated; the rest render their boundary verbatim.

Parameters

claim The path-free traceability claim. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A curated schema-B bundle whose admissibility reflects the claim status.

geometry_neutral_replay_evidence_bundle

geometry_neutral_replay_evidence_bundle(summary, *, operator, studio_version, started, ended)

Build the studio.geometry-neutral-replay.v1 bundle.

A replay is a recomputable measured run, but bounded synthetic regression evidence — its device claim is not admitted unless matched-reference evidence passes — so it does not render as validated.

Parameters

summary The path-free replay summary. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A measured schema-B bundle whose admissibility reflects the device claim.

phase_sync_monitor_evidence

phase_sync_monitor_evidence(snapshot, *, operator, studio_version, started, ended)

Build the studio.phase-sync-monitor.v1 bundle.

A live coherence/guard snapshot is measured telemetry, not a validated facility claim, so it is bounded-model and does not render as validated.

Parameters

snapshot The path-free monitor snapshot. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A measured schema-B bundle (bounded-model).

disruption_prediction_evidence

disruption_prediction_evidence(prediction, *, operator, studio_version, started, ended)

Build the studio.disruption-prediction.v1 bundle.

The predictor is a hand-tuned fixed-weight baseline with no ROC against a real disruption database, so the claim is a validation gap and is not admitted.

Parameters

prediction The path-free prediction. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A measured schema-B bundle on the validation-gap boundary.

equilibrium_analysis_evidence

equilibrium_analysis_evidence(analysis, *, operator, studio_version, started, ended)

Build the studio.equilibrium-analysis.v1 bundle.

Macroscopic shape parameters derived from an EFIT-lite reconstruction inherit its bounded status, so the analysis does not render as validated.

Parameters

analysis The path-free shape analysis. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A measured schema-B bundle (bounded-model).

controller_run_evidence

controller_run_evidence(result, *, operator, studio_version, started, ended, host=None)

Build the studio.controller-run.v1 bundle (measured closed-loop run).

regulate is the ecosystem's only realtime, live-hardware verb, but a closed-loop run against a surrogate low-order plant is measured-but-bounded evidence: it is not a facility-certified live-hardware control outcome (that is the certify verb's machine-checked safety certificate). So the claim is bounded-model / rejected and does not render as validated.

Parameters

result The path-free controller-run summary. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps. host Optional host descriptor the run executed on.

Returns

EvidenceBundle A measured schema-B bundle whose renders_as_validated is False.

disruption_mitigation_evidence

disruption_mitigation_evidence(run, *, operator, studio_version, started, ended)

Build the studio.disruption-mitigation.v1 bundle (measured SPI run).

The mitigation run is a low-order shattered-pellet-injection quench model (neutral-gas-shielding ablation, noble-gas radiative Z_eff); it is real and deterministic but not a facility-validated disruption-mitigation outcome, so the claim is bounded-model / rejected.

Parameters

run The path-free mitigation run summary. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A measured schema-B bundle (bounded-model).

scenario_simulation_evidence

scenario_simulation_evidence(run, *, operator, studio_version, started, ended)

Build the studio.scenario-simulation.v1 bundle (measured rollout).

A scenario rollout is a closed-loop run on a low-order plant model checked by a bounded replay-coupling audit (finite, monotonic, conservation-bounded). A passing audit proves replay consistency, not facility validation — so even an audited, passing rollout stays bounded-model / rejected and does not render as validated; external trajectory validation is required for a facility claim.

Parameters

run The path-free scenario rollout summary. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A measured schema-B bundle (bounded-model).

Studio Capability Manifest

manifest

The CONTROL studio's capability manifest (schema A) on the platform contract.

Authors CONTROL's :class:scpn_studio_platform.manifest.CapabilityManifest: the verbs it advertises, the evidence schemas they emit, the federated UI panel the Hub loads, and the content-addressed digest of that declared surface. The digest is computed with the platform's :func:scpn_studio_platform.manifest.content_digest over the canonical JSON of the declared verbs and evidence schemas, so it is reproducible across checkouts and independent of git state — the cross-repo capability_manifest drift gotcha does not apply.

STUDIO_VERSION module-attribute

STUDIO_VERSION = _resolve_studio_version()

The CONTROL studio version this manifest stamps (the installed package version).

PLATFORM_SDK_RANGE module-attribute

PLATFORM_SDK_RANGE = '>=0.1,<0.9'

The platform SDK SemVer range the studio builds on (matches the studio extra).

PROTOCOL_VERSION module-attribute

PROTOCOL_VERSION = '1'

The SYNAPSE wire protocol version the studio pins.

UI_REMOTE_ENTRY module-attribute

UI_REMOTE_ENTRY = 'https://www.anulum.org/studios/scpn-control/remoteEntry.js'

The deployed Module Federation remote-entry URL for the CONTROL panel.

UI_PANEL_EXPOSE module-attribute

UI_PANEL_EXPOSE = './Panel'

The Module Federation exposure path loaded by the SCPN Studio Hub.

UI_PANEL module-attribute

UI_PANEL = UiModule(remote_entry=UI_REMOTE_ENTRY, exposes=(UI_PANEL_EXPOSE,))

The federated UI panel the Hub loads for the CONTROL studio.

declared_surface

declared_surface()

Return the content-addressable declared surface of the CONTROL studio.

The surface is the canonical JSON of each advertised verb plus the evidence schema list, keyed by a stable logical path. Hashing file content (not git state) is what makes the digest reproducible across checkouts.

Returns

dict[str, bytes] Mapping of logical path to canonical-JSON bytes, suitable for :func:scpn_studio_platform.manifest.content_digest.

build_manifest

build_manifest(*, studio_version=STUDIO_VERSION)

Build the CONTROL studio's capability manifest.

Parameters

studio_version The studio version to stamp; defaults to :data:STUDIO_VERSION (the installed scpn-control version).

Returns

CapabilityManifest The schema-A manifest, with a content digest over :func:declared_surface.

Studio Live-Emitter Adapters

adapters

Bridge CONTROL's live emitter outputs onto the studio EvidenceBundle mappers.

The mappers in :mod:scpn_control.studio.evidence take path-free result shapes so they stay testable in isolation. These adapters connect the real CONTROL emitters to them: a RealtimeEFIT.reconstruct result, an issued runtime safety certificate, and a controller-latency measurement become schema-B EvidenceBundles with no re-implementation of the honesty rules.

Where an emitter does not stamp a value the bundle needs — the proof engine and its version are not recorded on a runtime safety certificate — the adapter takes it as an explicit argument from the caller that ran the proof, rather than inventing one.

efit_evidence_from_reconstruction

efit_evidence_from_reconstruction(result, *, measurements, operator, studio_version, started, ended, host=None)

Map a live RealtimeEFIT.reconstruct result onto an evidence bundle.

Parameters

result The reconstruction output (its shape.Ip_reconstructed, chi_squared, n_iterations and psi grid shape feed the bundle). measurements The diagnostic/configuration inputs the reconstruction ran on; hashed for the input provenance digest. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps. host Optional host descriptor.

Returns

EvidenceBundle A studio.efit-reconstruction.v1 bundle (bounded-model, not validated).

safety_certificate_evidence_from_certificate

safety_certificate_evidence_from_certificate(certificate, *, live_topology_sha256, checker, checker_version, operator, studio_version, started, ended)

Map an issued runtime safety certificate onto a formally-proven bundle.

Parameters

certificate A certificate from issue_runtime_safety_certificate — its binding.petri_topology_sha256 is the proven subject, its formal_certificate_sha256 the proof artifact, and checked_specs the proven obligations. live_topology_sha256 SHA-256 of the live controller's Petri-net topology (e.g. from compute_petri_topology_digest on the deployed net). A mismatch with the proven topology degrades the claim and voids the proof. checker, checker_version The proof engine and its exact version. A runtime safety certificate does not stamp these, so the caller that ran the proof supplies them rather than the adapter inventing a value. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.safety-certificate.v1 bundle (formally-proven); admissible only when the live topology still matches the proven one.

Raises

KeyError If the certificate is missing the binding, topology, proof, or specs fields.

controller_latency_evidence_from_measurement

controller_latency_evidence_from_measurement(measurement, *, controller, active_backend, reference_backend, operator, studio_version, started, ended, host=None)

Map a controller-latency measurement onto a measured benchmark bundle.

Parameters

measurement A latency record with p50_us, p95_us, p99_us and n (the sample count), as produced by the per-controller latency benchmark. controller Name of the controller benchmarked. active_backend The backend timed (e.g. "rust"). reference_backend The reference backend it is compared against. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps. host Optional host descriptor.

Returns

EvidenceBundle A studio.controller-latency.v1 bundle (measured, bounded-model).

Raises

KeyError If the measurement is missing a percentile or the sample count.

physics_validation_evidences_from_registry

physics_validation_evidences_from_registry(entries, *, operator, studio_version, started, ended)

Map physics_traceability registry entries onto physics-validation bundles.

Each entry of validation/physics_traceability.json becomes a studio.physics-validation.v1 bundle carrying its fidelity status on the claim lattice, its admission, and its qualitative validity_domain as a prose note. For an external_dependency_blocked entry the blocking dependency is taken from the first required_actions item (the lattice requires one).

Parameters

entries The registry entries (each a mapping with component, module_path, fidelity_status, public_claim_allowed, validity_domain and, for blocked entries, required_actions). operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

tuple[EvidenceBundle, ...] One bundle per entry, in input order.

Raises

KeyError If an entry is missing a required field.

replay_evidence_from_geometry_neutral

replay_evidence_from_geometry_neutral(evidence, *, operator, studio_version, started, ended)

Map a GeometryNeutralReplayEvidence onto a replay bundle.

Parameters

evidence The geometry-neutral replay evidence object. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.geometry-neutral-replay.v1 bundle.

phase_sync_monitor_evidence_from_snapshot

phase_sync_monitor_evidence_from_snapshot(snapshot, *, operator, studio_version, started, ended)

Map a RealtimeMonitor.tick snapshot onto a monitor bundle.

Parameters

snapshot The dashboard snapshot dict (tick, R_global, lambda_exp, guard_approved, latency_us). operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.phase-sync-monitor.v1 bundle.

Raises

KeyError If a required snapshot field is missing.

disruption_prediction_evidence_from_risk

disruption_prediction_evidence_from_risk(risk, *, observables, operator, studio_version, started, ended)

Map a predict_disruption_risk output onto a prediction bundle.

Parameters

risk The predicted disruption risk in [0, 1]. observables The toroidal-asymmetry observables the prediction used. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.disruption-prediction.v1 bundle (validation-gap, fixed-weight).

equilibrium_analysis_evidence_from_shape

equilibrium_analysis_evidence_from_shape(shape, *, operator, studio_version, started, ended)

Map a ShapeParams onto an equilibrium-analysis bundle.

Parameters

shape The macroscopic shape parameters derived from a reconstruction. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.equilibrium-analysis.v1 bundle (bounded-model).

controller_run_evidence_from_simulation

controller_run_evidence_from_simulation(summary, *, controller, operator, studio_version, started, ended, host=None)

Map a closed-loop controller-run summary onto a controller-run bundle.

Parameters

summary A run summary as produced by a closed-loop shape/current controller (for example run_neural_mpc_simulation): steps, mean_tracking_error, max_abs_action and max_abs_coil_current. controller Name of the controller run; the summary records the loop, not its name, so the caller supplies it rather than the adapter inventing one. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps. host Optional host descriptor.

Returns

EvidenceBundle A studio.controller-run.v1 bundle (measured, bounded-model).

Raises

KeyError If the summary is missing a required field.

disruption_mitigation_evidence_from_run

disruption_mitigation_evidence_from_run(summary, *, operator, studio_version, started, ended)

Map a run_spi_mitigation summary onto a disruption-mitigation bundle.

Parameters

summary The deterministic SPI summary (neon_quantity_mol, argon_quantity_mol, xenon_quantity_mol, z_eff, final_current_ma and samples). operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.disruption-mitigation.v1 bundle (measured, bounded-model).

Raises

KeyError If the summary is missing a required field.

scenario_simulation_evidence_from_audit

scenario_simulation_evidence_from_audit(audit, *, operator, studio_version, started, ended)

Map a ScenarioCouplingAudit onto a scenario-simulation bundle.

Parameters

audit A ScenarioCouplingAudit from audit_scenario_coupling — its metadata carries the scenario name, config digest, step count, time window and module count, and its passed flag the bounded-audit result. operator Opaque identity of the operator/tenant. studio_version Version of the CONTROL studio. started, ended ISO-8601 start/end timestamps.

Returns

EvidenceBundle A studio.scenario-simulation.v1 bundle (measured, bounded-model). A passing audit does not promote the claim past bounded-model.

Studio Panel Feed

The federated panel reads CONTROL's verbs and claims from the wire feed this module emits (studio.control-feed.v1), so the UI never holds a second, drifting copy of the contract. Claim summaries include the platform freshness axis: the safety certificate emits verified-at-source after the mapper re-checks proof coverage, while the other representative claims emit traceable-unchecked and render at their boundary unless fresh source verification is supplied. Regenerate the standalone artefact with python -m scpn_control.studio.feed > studio-web/public/studio-feed.json.

feed

Serialise the CONTROL studio surface into a wire feed the panel reads live.

The federated panel must not hard-code CONTROL's verbs and claims — a second copy drifts from the Python contract. This module emits a JSON-safe studio feed: the manifest's verbs reduced to the panel's rendering fields, plus a set of live :class:scpn_studio_platform.evidence.EvidenceBundle claims reduced to their schema, claim-boundary status, admission, modality, and freshness. The panel reads the same honesty grading the Python vertical emits, with no second source of truth.

The serialisers (:func:verb_summary, :func:claim_summary, :func:studio_feed) take whatever bundles the serving runtime has produced. :func:representative_feed builds one bundle per evidence schema from the real mappers, so the feed is complete and self-describing even before a live run has emitted anything; a serving runtime substitutes live bundles for the representative set.

The wire format is snake_case (matching the rest of the platform JSON); the panel's loader narrows it to its own camelCase domain types at the boundary.

verb_summary

verb_summary(verb)

Reduce a platform :class:Verb to the panel's verb-rendering fields.

Parameters

verb A CONTROL verb.

Returns

dict[str, object] The verb's name, safety_tier, side_effect and timing_class (all wire strings from :meth:Verb.to_dict), domain_distinctive (true when the verb is not in the core spine), and deadline_us only when the verb declares a realtime deadline.

claim_summary

claim_summary(bundle)

Reduce an :class:EvidenceBundle to the panel's claim-rendering fields.

Parameters

bundle A schema-B evidence bundle.

Returns

dict[str, str] The bundle's schema and its claim status / admission / kind / freshness as wire strings — exactly what the panel needs to apply the honesty rule (validated only when reference-validated, admitted, and not freshness-floored).

studio_feed

studio_feed(bundles, *, studio_version=STUDIO_VERSION, verbs=CONTROL_VERBS, content_digest=None)

Assemble the studio feed document from verbs and live claim bundles.

Parameters

bundles The evidence bundles the serving runtime has produced. studio_version The studio version to stamp; defaults to the installed package version. verbs The verbs to advertise; defaults to every CONTROL verb in manifest order. content_digest The manifest content digest to stamp; computed from the manifest when not supplied so the feed and manifest always agree.

Returns

dict[str, object] A JSON-safe feed: feed_schema, studio, studio_version, content_digest, verbs and claims.

representative_bundles

representative_bundles(*, operator=_REPRESENTATIVE_OPERATOR, studio_version=STUDIO_VERSION, started=_REPRESENTATIVE_STARTED, ended=_REPRESENTATIVE_ENDED)

Build one representative evidence bundle per CONTROL evidence schema.

Each bundle is produced by the real evidence mapper on a representative path-free input, so the feed shows the same honesty grading the vertical emits: a held safety certificate renders validated, the fixed-weight disruption predictor lands on validation-gap, and every other claim is bounded. The set is representative, not a live-hardware capture — a serving runtime substitutes the bundles it actually produced.

Parameters

operator Opaque identity to stamp on the representative bundles. studio_version The studio version to stamp. started, ended ISO-8601 window to stamp (representative; no hidden clock).

Returns

tuple[EvidenceBundle, ...] One bundle per evidence schema, in manifest order.

representative_feed

representative_feed(*, operator=_REPRESENTATIVE_OPERATOR, studio_version=STUDIO_VERSION, started=_REPRESENTATIVE_STARTED, ended=_REPRESENTATIVE_ENDED)

Build the complete representative studio feed.

Parameters

operator Opaque identity to stamp on the representative bundles. studio_version The studio version to stamp. started, ended ISO-8601 window to stamp (representative).

Returns

dict[str, object] The feed document with every verb and one claim per evidence schema, its content_digest taken from the manifest so feed and manifest agree.

render_feed_json

render_feed_json()

Render the representative feed as deterministic, indented JSON.

Returns

str The representative feed as sorted-key, two-space-indented JSON with a trailing newline — the on-disk artefact the standalone panel fetches.

Studio Sealed Safety Claim

The Hub's transparency log verifies artefacts with an RFC-8785 JCS canonicaliser that rejects non-integer JSON numbers, so the sealed safety-certificate claim is emitted float-free: integers stay within the exact-interoperability range and exact decimals travel as strings. The module is deliberately SDK-free so the artefact can be produced from a checkout without the optional studio extra.

sealed_claim

Float-free sealed-claim artefact for the SCPN Studio Hub transparency log.

The Hub seals evidence artefacts (studio.publication-seal.v1) into an append-only RFC-6962 Merkle transparency log and verifies them in-browser with an RFC-8785 JCS canonicaliser that rejects non-integer JSON numbers. This module emits CONTROL's safety-certificate claim in that constrained form: every numeric value is an integer within the exact-interoperability range or an exact decimal string — JSON floats are rejected fail-closed before a byte is written.

Unlike :mod:scpn_control.studio.evidence, this module deliberately has no scpn_studio_platform dependency: the artefact is plain JSON handed to the Hub keeper, so it must be producible from a checkout without the optional studio SDK installed.

assert_jcs_safe

assert_jcs_safe(value, *, path='$')

Reject any value the Hub's RFC-8785 JCS verifier cannot seal.

Parameters

value JSON-compatible tree to inspect (mappings, sequences, scalars). path JSONPath-style location prefix used in error messages.

Raises

ValueError If the tree contains a float (including bool-adjacent NumPy scalars coerced to float), an integer beyond ±(2**53 − 1), or a non-JSON type. Exact decimal values must be carried as strings.

build_safety_certificate_sealed_claim

build_safety_certificate_sealed_claim(certificate, *, live_topology_sha256, checker, checker_version, studio_version, claim_id, issued_utc)

Build the float-free sealed-claim payload from an issued certificate.

Parameters

certificate A certificate from :func:scpn_control.scpn.issue_runtime_safety_certificate — its binding.petri_topology_sha256 is the proven subject, formal_certificate_sha256 the proof artifact digest, checked_specs the proven obligations, and payload_sha256 the certificate digest this claim cites for provenance. live_topology_sha256 SHA-256 of the live controller's Petri-net topology. A mismatch with the proven topology degrades claim_status to validation-gap and admission to rejected — the claim never presents a voided proof as established. checker, checker_version Proof engine and its exact version; certificates do not stamp these, so the caller that ran the proof supplies them. studio_version Version of the CONTROL studio producing the claim. claim_id Stable capability/claim identifier under which the Hub records the sealed artefact. issued_utc ISO-8601 UTC timestamp of claim emission (caller-supplied so the payload stays deterministic for a given certificate).

Returns

dict[str, Any] JCS-safe payload (verified by :func:assert_jcs_safe before return).

Raises

KeyError If the certificate lacks binding, proof, digest, or spec fields. ValueError If any identity field is empty or the payload fails the JCS guard.

render_sealed_claim_json

render_sealed_claim_json(payload)

Serialise a sealed-claim payload to deterministic UTF-8 JSON.

Parameters

payload A JCS-safe payload (re-verified here fail-closed).

Returns

str Sorted-key, compact-separator JSON with a trailing newline — byte-stable for a given payload so the sha256 the keeper receives is reproducible.

write_sealed_claim

write_sealed_claim(payload, path)

Write the sealed-claim artefact and return its SHA-256 hex digest.

Parameters

payload A JCS-safe payload (verified during rendering). path Destination file path; parent directories are created.

Returns

str SHA-256 hex digest of the written bytes — quote it when pinging the Hub keeper alongside the artefact path.


CLI

scpn-control demo --scenario combined --steps 1000
scpn-control benchmark --n-bench 5000 --json-out
scpn-control validate --json-out
scpn-control validate-release-evidence artifacts/release_evidence_report.json --json-out
scpn-control info --json-out
scpn-control live --port 8765 --zeta 0.5 --layers 16
scpn-control hil-test --shots-dir path/to/shots
Command Description
demo Closed-loop control demonstration (PID, SNN, combined)
benchmark PID vs SNN timing benchmark with JSON output option
validate Import hygiene plus data-manifest, JAX GK parity, and physics-traceability gates
validate-release-evidence Admission check for JSON reports emitted by scpn-control validate --json-out, including data manifests, JAX GK parity, physics traceability, multi-shot campaign evidence, and native formal certificate evidence
info Version, Rust backend status, weight provenance, Python/NumPy versions
live Real-time WebSocket phase sync server
hil-test Hardware-in-the-loop test campaign against shot data

Rust Acceleration

When scpn-control-rs is built via maturin, all core solvers use Rust backends automatically:

from scpn_control import RUST_BACKEND
print(RUST_BACKEND)  # True if Rust available

# Transparent acceleration — same Python API, Rust execution
kernel = FusionKernel(R0=6.2, a=2.0, B0=5.3)

Build Rust bindings:

cd scpn-control-rs/crates/control-python
maturin develop --release

PyO3 Bindings

Python Class Rust Binding Crate
FusionKernel PyFusionKernel control-core
RealtimeMonitor PyRealtimeMonitor control-math
SnnPool PySnnPool control-control
MpcController PyMpcController control-control
Plasma2D PyPlasma2D control-core
TransportSolver PyTransportSolver control-core

Core — Native Rust Engine Wrapper

scpn_control.core.rust_engine is the Python control-plane wrapper for the optional PyO3 native execution bridge. It configures native campaign execution, formal-verification mode, runtime admission, transport backend selection, and emergency telemetry handoff while keeping timing-critical execution inside the compiled Rust data plane when the extension is available.

This wrapper is an execution boundary, not a physics solver. It does not turn a local workstation run into target-hardware PCS evidence unless the matching runtime-admission and benchmark-context reports pass.

rust_engine

Rust-accelerated control-plane wrapper.

This module exposes a single orchestrator class for launch-time control of the compiled Rust transport primitives.

Responsibility is split by design: - Python owns policy, campaign configuration, and lifecycle boundaries. - Rust owns the per-tick heavy control/transport work and back-pressured UDP path.

NeuroCyberneticEngine

NeuroCyberneticEngine(n_neurons=64, seed=7, state_init_r=6.2, state_init_z=0.0, plant_gain=0.0)

Coordinate Rust-native control execution from Python.

Python owns campaign configuration, safety policy, transport policy and telemetry. Execution primitives (SNN, PID, transport publisher) are executed through compiled Rust bindings.

transport_backend property

transport_backend

Name of the active multicast transport backend.

is_running property

is_running

Whether the control-engine loop is currently running.

is_native_backend property

is_native_backend

Whether the native Rust worker pool is active.

native_backend_available property

native_backend_available

Whether transport-native Rust bindings are importable.

configure_acados_targets

configure_acados_targets(targets)

Configure control targets and execution-safe scalar bounds.

Supported keys: - target_r / rho_tor_target / R_target - target_z / z_tor_target / Z_target - beta_n_limit - c_gB / c_gB_nominal - optional u_min/u_max safety clamps

configure_transport

configure_transport(*, endpoint='239.0.0.1', port=5555, ttl=1, max_queue=4, backend='std', heartbeat_port=0, heartbeat_timeout_ms=3)

Configure the UDP multicast transport for state publishing.

Parameters

endpoint Multicast group address. port Multicast port, 1–65535. ttl Multicast time-to-live, 1–255. max_queue Publisher backpressure queue depth, 1–4096. backend Transport backend name (e.g. "std"). heartbeat_port Heartbeat UDP port; 0 disables the heartbeat. heartbeat_timeout_ms Heartbeat timeout in milliseconds, 1–120000.

configure_kuramoto_weights

configure_kuramoto_weights(weights)

Load Kuramoto weights that remain under Python policy control.

Accepts either:

  • a mapping of weight names to float coefficients,
  • a filesystem path to a JSON object containing weight names.

configure_execution_affinity

configure_execution_affinity(*, core_snn=1, core_z3=2, core_net=3, core_hb=4)

Record preferred execution affinity for campaign telemetry.

The compiled transport bridge does not currently expose explicit core pinning. These fields are retained for reproducibility, campaign replay, and future native controller migration.

configure_native_formal_verification

configure_native_formal_verification(*, enabled=True, mode='async_drop', max_marking=100, max_depth=4, dispatch_interval_steps=30, channel_capacity=2)

Configure native formal checking for the fused hardware loop.

configure_itpa_gyro_bohm

configure_itpa_gyro_bohm(path=None)

Load and apply ITPA gyro-Bohm profile constraints.

Both legacy ({"c_gB": ...}) and upgraded schema (scaling_parameters.{c_gB_nominal,...}) are supported.

set_max_publish_failures

set_max_publish_failures(max_failures)

Set the tolerated consecutive publish failures before halting.

Parameters

max_failures Maximum consecutive publish failures, 0–1000000.

set_state_sampler

set_state_sampler(sampler)

Install the callable that samples the live (R, Z) plant state.

Parameters

sampler Zero-argument callable returning the measured (R, Z) position.

Raises

TypeError If sampler is not callable.

execute_campaign

execute_campaign(*, steps=None, runtime_s=None, tick_interval_s=0.001, max_publish_failures=None, initial_state=None, plant_gain=None, core_snn=1, core_z3=2, core_net=3, core_hb=4, execution_backend='auto', pacing_mode='sleep', runtime_admission_policy='warn')

Campaign-facing wrapper used by command-line and external orchestrators.

execute_hardware_loop

execute_hardware_loop(*, steps=None, runtime_s=None, tick_interval_s=0.001, initial_state=None, plant_gain=None, max_publish_failures=None, core_snn=1, core_z3=2, core_net=3, core_hb=4, execution_backend='auto', pacing_mode='sleep', runtime_admission_policy='warn')

Run the Rust-accelerated control loop.

Configuration is owned by Python; transport execution is delegated to compiled Rust primitives (RustSnnController, RustPIDController, RustUdpTransportBridge).

admit_runtime

admit_runtime(*, execution_backend, pacing_mode, tick_interval_s, native_backend_available, policy='warn')

Collect runtime admission evidence and optionally fail closed.

stop

stop()

Request immediate campaign stop.

run_hardware_campaign

run_hardware_campaign(**kwargs)

Compatibility alias used by external orchestration scripts.

execute_emergency_shutdown

execute_emergency_shutdown()

Best-effort emergency shutdown hook exposed to callers.

extract_slab_telemetry

extract_slab_telemetry()

Return a snapshot of engine, transport, and bridge telemetry.

Returns

dict[str, Any] Running state, transport backend, target position, bridge payload and heartbeat status, ITPA constraints, and any recorded runtime admission and native-pool telemetry.

API surface usage model

This page is the binding map for practical integration, not a promise of stable semantics for every release.

  • Use top-level exports for workflow composition.
  • Use submodules for module-specific interfaces and keep import paths explicit.
  • Use documented Rust bindings for hot-path execution only after matching environment checks pass.

When uncertain, start in Python for reproducibility and then switch to native paths only for timing and production-oriented experiments.

API usage expectation for enterprise workflows

This API surface is intentionally split by risk and execution boundary:

  • Safe exploratory usage: top-level Python entry points with explicit argument checks and deterministic defaults.
  • Research extension usage: module-level APIs where validation still controls the admissible claim level.
  • Deployment-oriented usage: PyO3-backed primitives where timing and transport behavior are benchmarked under explicit host context.

When preparing an integration PR, include both:

  • one reproducible usage example against the public API,
  • one admission-visible benchmark or validation record for the same path.

That pairing is the minimum contract for external-facing confidence.

Practical use and scope

Use this page as the public contract boundary for scpn_control exports and import-level behavior.

  • Validate import and symbol usage here before changing interface signatures in src/scpn_control.
  • Use the API map to decide whether integration changes are safe for third-party callers.
  • Pair API updates with corresponding validation and release notes for any public call-flow changes.