Skip to content

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.

n_oscillators property

Number of coupled oscillators in the carrier.

__post_init__()

Validate oscillator arrays and freeze the Doppler-Kuramoto specification.

omega_at(t_s=0.0)

Return natural angular frequencies at simulation time t_s.

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:

"kinematic.doppler_kuramoto" = ["rust", "python", "mojo", "julia"]

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^-1 and -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.