Doppler-Kuramoto¶
MIF-001 implements the local, upstream-pending Doppler-corrected Kuramoto carrier for counter-propagating FRC plasmoid merging. The Python reference, Rust kernel, PyO3 bridge, and Julia counterpart all use the same RK4 phase integration and linear axial-position update.
Validation status — read before citing
This is a control-lane kinematic model, not an experiment-validated coupling.
The Doppler-corrected coupling term is a defensible kinematic correction; it is
checked for numerical self-consistency across backends (RK4 ↔ RK45, bit-exact /
~1 ULP parity) but is not fitted or confirmed against measured FRC-merge phase
data. What is anchored to a published FRC-merge study (Belova, arXiv:2501.03425)
is the downstream merge-window monitor and the merge/no-merge classification —
see tools/belova_merge_parity.py and tests/physics_parity/test_belova_merge_parity.py,
where the real MergeWindowMonitor tracks the ballistic closure and the
reconnection acceleration is explicitly delegated to SCPN-FUSION-CORE. Do not cite
the phase-coupling dynamics as validated physics.
Carrier¶
For oscillator i, phase theta_i, chamber-axis position z_i, and
axial velocity v_i, the implemented derivative is:
dtheta_i/dt =
omega_i(t)
+ sum_{j != i} K_ij / (1 + |z_i - z_j| / L_z)
* sin(theta_j - theta_i - alpha)
+ gamma * sum_{j != i}
(v_i - v_j) / (0.5 * (|v_i| + |v_j|) + epsilon_v)
omega_i(t) = omega_i0 + omega_rate_i * t
The Doppler denominator is pair-normalised by the characteristic speed of the interacting pair, not by the observer channel alone. For unequal-speed two-body pairs this keeps the velocity correction equal and opposite, and it prevents a stationary channel from receiving an unbounded relative-velocity impulse.
The carrier advances positions with dz_i/dt = v_i. That axial update is
included only to evaluate the MIF-001 chamber-centre lock window; the
full moving-frame UPDE remains MIF-002.
omega_rate_rad_s2 is optional on every Python, Rust, PyO3, and Julia
surface. Omitting it uses a zero vector and preserves the constant-frequency
MIF-001 contract. When supplied, RK4 evaluates omega(t) at each stage time,
so a linearly decaying uncoupled oscillator matches the analytic
theta(t) = theta0 + omega_i0 t + 0.5 omega_rate_i t^2 solution within the
committed 1 ppm acceptance bound at dt = 1 us.
Python API¶
doppler_kuramoto
¶
Doppler-corrected axial Kuramoto carrier for MIF-001.
The local carrier is the MIF-specific extension of the PHASE-ORCH
Kuramoto/Swarmalator family. For phase :math:\theta_i, axial position
:math:z_i, and constant axial velocity :math:v_i, the implemented
pointwise derivative is
.. math::
\dot\theta_i = \omega_i(t)
+ \sum_{j \ne i}
\frac{K_{ij}}{1 + |z_i-z_j| / L_z}
\sin(\theta_j - \theta_i - \alpha)
+ \gamma \sum_{j \ne i}
\frac{v_i-v_j}{\tfrac12(|v_i|+|v_j|) + \epsilon_v}.
where :math:\omega_i(t)=\omega_{i,0}+\dot\omega_i t for affine
non-autonomous phase-law runs. The default
:math:\dot\omega_i=0 preserves the constant-frequency MIF-001 contract.
The position state is advanced by :math:\dot z_i=v_i. This is only the
linear axial kinematic envelope required to evaluate the MIF-001
Doppler-lock acceptance window; the richer moving-frame UPDE remains MIF-002.
DopplerKuramotoSpec(omega_rad_s, coupling_rad_s, phase_lag_rad=0.0, doppler_strength_rad_s=0.0, velocity_epsilon_m_s=1e-09, distance_scale_m=1.0, omega_rate_rad_s2=None)
dataclass
¶
Immutable Doppler-Kuramoto parameter set.
Attributes¶
omega_rad_s:
Natural angular frequencies in radians per second.
coupling_rad_s:
Square off-diagonal coupling matrix. Entry K[i, j] weights the
phase pull from oscillator j onto oscillator i before distance
decay.
omega_rate_rad_s2:
Optional affine natural-frequency rates in radians per second squared.
None is normalised to a zero vector.
phase_lag_rad:
Sakaguchi-style phase lag :math:\alpha in radians.
doppler_strength_rad_s:
Scale factor :math:\gamma applied to each off-diagonal,
pair-normalised relative-velocity Doppler correction.
velocity_epsilon_m_s:
Positive denominator guard for stationary or near-stationary channels.
distance_scale_m:
Positive axial length scale :math:L_z used to make distance decay
dimensionless.
DopplerKuramotoState(t_s, phases_rad, positions_m, velocities_m_s, order_parameter, phase_lock_error_rad)
dataclass
¶
Single state snapshot from the Doppler-Kuramoto carrier.
DopplerKuramotoReport(time_s, phases_rad, positions_m, order_parameter, phase_lock_error_rad)
dataclass
¶
Batch simulation trace for a Doppler-Kuramoto run.
DopplerKuramoto(spec, phases_rad, positions_m, velocities_m_s)
¶
Stateful RK4 integrator for the MIF-001 axial Doppler-Kuramoto carrier.
t_s
property
¶
Current simulation time in seconds.
phases_rad
property
¶
Read-only current phase vector in radians.
positions_m
property
¶
Read-only current axial positions in metres.
velocities_m_s
property
¶
Read-only constant axial velocities in metres per second.
state()
¶
Return a read-only snapshot of the current state.
derivatives(phases_rad=None, positions_m=None, t_s=None)
¶
Return dtheta/dt for the supplied or current phase/position state.
step(dt_s)
¶
Advance the coupled phase/linear-position state by dt_s seconds.
copy()
¶
Return an independent copy of the current integrator state.
doppler_derivatives(spec, phases_rad, positions_m, velocities_m_s, t_s=0.0)
¶
Return the MIF-001 pointwise phase derivative vector.
evaluate_doppler_kuramoto(spec, phases_rad, positions_m, velocities_m_s, dt_s, steps)
¶
Run steps RK4 updates and return the full phase/position trace.
order_parameter(phases_rad)
¶
Return the Kuramoto order parameter |mean(exp(i theta))|.
phase_lock_error(phases_rad)
¶
Return the maximum circular pairwise phase separation in radians.
Dispatch¶
Use scpn_mif_core.kinematic.dispatched_doppler_kuramoto(...) when a
caller wants the fastest available measured backend. The dispatch table
prefers Rust, with Python, Mojo, and Julia retained as parity and
reference surfaces:
The pure Python DopplerKuramoto class remains importable directly for
deterministic debugging and tests. The derivative kernel also ships a Mojo
surface (mojo/doppler_kuramoto.mojo, compiled with mojo build -Xlinker
-lm) — a subprocess CLI with tolerance-aware parity (~1 ULP; bit-exact on
transcendental-free inputs), parity-tested and benchmarked in the
doppler_kuramoto.derivative_3 group. Its per-call process spawn places it
behind the in-process backends but ahead of the Julia CLI; like Julia it is
a measured/parity surface, not the runtime hot path.
Acceptance¶
The committed acceptance scenario uses two counter-propagating channels:
v_z = +300000 m s^-1and-300000 m s^-1;- initial positions
[-0.03, 0.03] m; - chamber-centre spatial window
|z| <= 0.002 m; - required phase window
|Delta theta| < 0.01 rad.
With doppler_strength_rad_s = 2.0e6, the model reaches the phase window
inside the spatial window. With the Doppler term removed, the same scenario
misses the phase window. Python, Rust, and Julia tests also cover the
pair-antisymmetry invariant for unequal-speed two-body Doppler corrections.
Benchmarks¶
Measured on the local i5-11600K rig using 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 host load present; the numbers are for
dispatch ordering, not production performance claims. The Julia entries are
measured through the package CLI harness, so the timings include Julia
process startup.
| Group | Backend | Mean | Result |
|---|---|---|---|
derivatives_3 |
Rust | 483 ns | fastest |
derivatives_3 |
Python | 21.88 us | 45.3x slower than Rust |
trace_120 |
Rust | 78.16 us | fastest |
trace_120 |
Python | 15.59 ms | 199.4x slower than Rust |
trace_120 |
Julia CLI | 1.49 s | CLI startup comparison |
affine_trace_1000 |
Rust | 374.86 us | fastest |
affine_trace_1000 |
Python | 88.32 ms | 235.6x slower than Rust |
affine_trace_1000 |
Julia CLI | 1.72 s | CLI startup comparison |
Raw summary: bench/results/doppler_kuramoto.json.
Ownership¶
SYNC-STATE: upstream-pending applies to all MIF-001 implementation
surfaces. SCPN-MIF-CORE owns the FRC-specific local carrier until
SCPN-PHASE-ORCHESTRATOR receives the reusable scpn.upde.doppler
surface targeted for the 0.7.0 lane.