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
For N_eff effective turns, the back-EMF is
For a positive recovery load R_load and coupling efficiency eta, the
reported load power is
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¶
Acceptance criterion¶
The MIF-009 acceptance gate is the closed-form carrier table below. It validates the exact Faraday-law calculation that MIF owns:
The gate deliberately does not validate self-consistent plasma compression, mirror-field evolution, or exact FRC merge timing. Those are FUSION-owned physics and remain blocked until a pinned FUSION fixture exists.
| Case | Non-zero term(s) | Acceptance |
|---|---|---|
| Static radius and field | none | zero flux-rate, zero EMF, zero recovered power |
| Constant radius field ramp | R_s² dB/dt |
public scalar API equals closed form at machine precision |
| Constant field radius motion | 2 R_s dR_s/dt B |
public scalar API equals closed form at machine precision |
| Mixed product rule | both terms | public scalar API equals product-rule closed form at machine precision |
| Disconnected recovery | carrier non-zero, eta = 0 |
EMF remains finite and recovered power is exactly zero without squaring overflow |
The waveform gate checks the same carrier over an explicit time grid, including
independent central-difference and Simpson-quadrature checks in
tests/physics_parity/test_faraday_compression_parity.py. Python/Rust parity is
checked when the compiled extension is present, and Mojo waveform parity is
checked when the Mojo toolchain is available. No runtime behaviour changed in
this acceptance update, so no benchmark regeneration is required.
| 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 |
| Named closed-form carrier acceptance table | 5 scalar cases pass at machine precision |
| 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, Rust 1.85.0, and
Julia 1.12.6. This was a non-isolated workstation comparison with the CPU
governor set to powersave and heavy host load; treat it as local
regression evidence, not a production latency claim. Medians are quoted
because means are outlier-inflated under contention.
| Operation | Python | Rust | Julia | Dispatch |
|---|---|---|---|---|
Scalar faraday_back_emf |
975 ns | 313 ns | not used for scalar dispatch | rust, then python |
| 4 096-sample waveform | 242 us | 93.7 us | 13.6 s through CLI startup | rust, then python, then mojo, then julia |
The waveform ranking flipped on 2026-07-04 when the zero-copy array bridge
landed: the PyO3 boundary now consumes the five input channels as NumPy views
and returns the two waveform outputs as NumPy arrays, with no Python-list
materialisation in either direction. The earlier list-copying boundary had
left Python fastest on this group;
bench/results/faraday_recovery_waveform.json records both the new
measurement and that provenance.
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.jsonbench/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 andupstream-pendinguntil 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.