Skip to content

Faraday recovery — scpn_mif_core.physics.faraday_recovery

Module ID: MIF-009. Sync state: upstream-pending for the eventual SCPN-FUSION-CORE FUS-C.7 recovery model; self-consistent pulsed-compression coupling remains owned by FUS-C.6. Reference carrier: classical Faraday induction, applied to the external flux through the FRC separatrix cross-section.

MIF-009 computes the induced back-EMF in a recovery winding when the separatrix radius R_s(t) and external axial field B_ext(t) change in time. It intentionally stops at the exact Faraday-law carrier and ohmic recovery channel. The matching Lean proof surface records the signed EMF identity, non-negative recovered power for a physical load, and non-negative trapezoid-integrated waveform energy. It does not evolve the plasma compression trajectory; that coupling belongs in SCPN-FUSION-CORE.

Carrier equations

The external magnetic flux linked by one effective turn is

\[ \Phi(t) = B_\mathrm{ext}(t)\,\pi R_s(t)^2. \]

For N_eff effective turns, the back-EMF is

\[ \mathcal{E}(t) = -N_\mathrm{eff}\frac{d\Phi}{dt} = -N_\mathrm{eff}\pi\left(R_s^2\dot B_\mathrm{ext} + 2R_s\dot R_s B_\mathrm{ext}\right). \]

For a positive recovery load R_load and coupling efficiency eta, the reported load power is

\[ P_\mathrm{rec}(t) = \eta\,\mathcal{E}(t)^2 / R_\mathrm{load}. \]

The degenerate eta = 0 case is handled before squaring the EMF. A physically disconnected recovery channel therefore reports exactly zero recovered power and energy for any finite EMF, including finite EMF values whose square would overflow.

Waveform energy is trapezoid-integrated over the explicit time grid. All executable Python, Rust/PyO3, and Julia surfaces fail closed if finite inputs would overflow any derived observable: flux, flux rate, back-EMF, recovered power, or recovered energy must remain finite.

Parameter dictionary

Symbol Field Units Range Notes
N_eff turns dimensionless > 0 Effective turn count; fractional values represent calibrated coupling.
R_load load_resistance_ohm ohm > 0 Ohmic recovery load.
eta coupling_efficiency dimensionless [0, 1] Power-transfer efficiency.
R_s radius_m m >= 0 FRC separatrix radius.
dR_s/dt radial_velocity_m_s m/s finite Positive for expansion, negative for compression.
B_ext magnetic_field_T T finite External axial magnetic field.
dB_ext/dt magnetic_field_rate_T_s T/s finite External field ramp rate.

Public Python API

Faraday-law direct-recovery carrier for MIF-009.

The model computes the back-EMF induced in an effective recovery winding by the time derivative of the external magnetic flux through the FRC separatrix cross-section:

.. math::

\Phi(t) = B_\mathrm{ext}(t) \, \pi R_s(t)^2,
\qquad
\mathcal{E}(t) = -N_\mathrm{eff} \, \frac{d\Phi}{dt}.

Applying the product rule gives the exact pointwise carrier used by the Python, Rust, and Julia implementations:

.. math::

\frac{d\Phi}{dt}
= \pi \left(R_s^2 \, \dot B_\mathrm{ext}
  + 2 R_s \, \dot R_s \, B_\mathrm{ext}\right).

Status

SYNC-STATE: upstream-pending — this is the local MIF-CORE exact Faraday-law carrier. Self-consistent pulsed-compression coupling remains owned by SCPN-FUSION-CORE (FUS-C.6); the eventual FUSION-hosted recovery model is tracked as FUS-C.7.

FaradayRecoverySpec(turns, load_resistance_ohm, coupling_efficiency=1.0) dataclass

Immutable recovery-coil and load specification.

Attributes

turns : float Positive effective turn count of the recovery coil. Fractional values are accepted to represent winding/coupling calibration. load_resistance_ohm : float Positive ohmic load presented to the induced EMF. coupling_efficiency : float Dimensionless power-transfer efficiency in [0, 1].

__post_init__()

Validate coil turns, load resistance, and coupling efficiency.

FaradayRecoveryState(radius_m, radial_velocity_m_s, magnetic_field_T, magnetic_field_rate_T_s, flux_Wb, flux_rate_Wb_s, back_emf_V, recovered_power_W) dataclass

Pointwise Faraday recovery observables.

FaradayRecoveryReport(time_s, flux_Wb, flux_rate_Wb_s, back_emf_V, recovered_power_W, recovered_energy_J, peak_abs_back_emf_V, peak_recovered_power_W) dataclass

Waveform-level Faraday recovery observables.

magnetic_flux(radius_m, magnetic_field_T)

Return external magnetic flux B_ext * pi * R_s**2 in webers.

flux_rate(radius_m, radial_velocity_m_s, magnetic_field_T, magnetic_field_rate_T_s)

Return d(B_ext * pi * R_s**2) / dt in webers per second.

faraday_back_emf(radius_m, radial_velocity_m_s, magnetic_field_T, magnetic_field_rate_T_s, turns)

Return induced back-EMF -turns * dPhi/dt in volts.

recovered_power(spec, back_emf_V)

Return instantaneous load power from a Thevenin EMF source.

evaluate_faraday_state(spec, radius_m, radial_velocity_m_s, magnetic_field_T, magnetic_field_rate_T_s)

Evaluate pointwise flux, EMF, and recovered power.

evaluate_faraday_recovery(spec, time_s, radius_m, radial_velocity_m_s, magnetic_field_T, magnetic_field_rate_T_s)

Evaluate a full Faraday recovery waveform and integrate recovered energy.

The velocity and field-rate arrays are explicit inputs so the waveform path uses the same exact product-rule carrier as the scalar path; no hidden finite-difference derivative is introduced here.

Worked example

from scpn_mif_core.physics import FaradayRecoverySpec, evaluate_faraday_state

spec = FaradayRecoverySpec(turns=64.0, load_resistance_ohm=4.0, coupling_efficiency=0.9)
state = evaluate_faraday_state(
    spec,
    radius_m=0.2,
    radial_velocity_m_s=-320.0,
    magnetic_field_T=5.0,
    magnetic_field_rate_T_s=250_000.0,
)
print(f"back-EMF = {state.back_emf_V:.3f} V")

Validation summary

Check Result
Magnetic flux equals B*pi*R**2 1 unit test passes
Constant-field expanding-radius limit closed-form match at machine epsilon
Constant-radius field-ramp limit closed-form match at machine epsilon
Static radius and static field zero EMF
Product-rule decomposition closed-form match at machine epsilon
Pointwise state typing and power bookkeeping 1 unit test passes
Waveform energy integration constant-power case exact to machine epsilon
Spec and scalar rejection paths turns, load, efficiency, radius, non-finite EMF, and overflowed derived observables
Zero-coupling recovery path exact zero scalar power, waveform power, energy, and peak power without EMF squaring overflow
Waveform rejection paths non-monotonic time, one sample, shape mismatch, non-1D, empty, non-finite input, negative radius, and overflowed derived observables
Hypothesis property EMF is linear in effective turns over 80 randomised examples
Python ↔ Rust parity scalar, waveform, dispatch, and finite-observable rejection parity after make bridge
Julia package parity scalar limit cases, waveform energy, and finite-observable rejection pass in Pkg.test()
Lean proof surface EMF identity, recovered-power sign, and waveform-energy sign build with lake build

Benchmarks

Run locally with:

make bridge
pytest bench/kernels/bench_faraday_recovery.py --benchmark-only
python tools/update_dispatch.py

Measured on the local i5-11600K rig with Python 3.12.3, Rust 1.85.0, and Julia 1.12.6. This was a non-isolated workstation comparison with the CPU governor set to powersave and nontrivial host load; treat it as local regression evidence, not a production latency claim.

Operation Python Rust Julia Dispatch
Scalar faraday_back_emf 745 ns 84.5 ns not used for scalar dispatch rust, then python
4 096-sample waveform 117 us 247 us 1.82 s through CLI startup python, then rust, then julia

The waveform Rust result is slower here because the current PyO3 bridge moves Python lists across the FFI boundary. The Rust core remains the production kernel; Python is the fastest measured facade for NumPy-resident waveform batches until a zero-copy array bridge lands.

The recovery waveform also ships a Mojo surface (mojo/faraday_recovery.mojo, compiled with mojo build -Xlinker -lm): a subprocess CLI whose flux array is bit-exact and whose flux-rate/EMF/power and integrated energy are tolerance-aware (~1 ULP — Mojo fuses the product-rule multiply-add and numpy sums the energy pairwise). Its per-call process spawn places it behind the in-process backends but ahead of the Julia CLI, so the dispatch list is ["python", "rust", "mojo", "julia"]; like Julia it is a measured/parity surface, parity-tested in tests/unit/physics/test_faraday_recovery_mojo_parity.py, not the runtime hot path.

Benchmark summaries are committed at:

  • bench/results/faraday_back_emf.json
  • bench/results/faraday_recovery_waveform.json

Cross-repository touch points

  • SCPN-FUSION-CORE: owns self-consistent pulsed compression (FUS-C.6) and the eventual fully coupled recovery model (FUS-C.7). MIF-009 remains local and upstream-pending until that surface exists.
  • MIF trigger fabric: uses MIF-009 as the energy-balance carrier for the sub-50 ns recovery trigger acceptance criteria.
  • SCPN-CONTROL: may consume the reported recovered-power waveform as a pulsed-shot lifecycle guard after MIF-004 is implemented.