Skip to content

Moving-Frame UPDE

MIF-002 implements the chamber-fixed moving-frame UPDE layer. It reuses the MIF-001 pair-normalised Doppler-Kuramoto phase derivative and advances the combined [theta, z] state with a fixed-step Dormand-Prince RK45 update so absolute axial positions can be evaluated against the chamber reference point z = 0.

Carrier

For each oscillator:

dtheta_i/dt = DopplerKuramoto(theta, z, v)_i
dz_i/dt     = v_i

The moving-frame surface adds:

  • omega_rate_rad_s2: optional MIF-001 affine natural-frequency rate vector, propagated through each Dormand-Prince stage time;
  • reference_point_m: chamber-fixed axial reference, usually 0;
  • time_to_reference_s(): per-channel non-negative crossing estimate;
  • collision_imminent(eps_m): simultaneous reference-window predicate;
  • reference_error_m: max absolute distance from the reference;
  • separation_m: max-min axial spread across moving channels;
  • local_error_estimate: RK45 fifth/fourth order embedded difference, using circular deltas for phase components and linear deltas for axial position components.

Python API

moving_frame_upde

Moving-frame UPDE carrier with chamber-fixed absolute positions.

MIF-002 owns the chamber-frame trajectory layer that MIF-001 deliberately keeps minimal. The phase derivative is delegated to the MIF-001 Doppler-Kuramoto carrier while this module advances the combined [theta, z] state with a fixed-step Dormand-Prince RK45 update and computes reference-window observables. The embedded RK error is evaluated with circular phase deltas for theta components and linear deltas for chamber-frame z components.

MovingFrameUPDESpec(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, reference_point_m=0.0, omega_rate_rad_s2=None) dataclass

Immutable moving-frame UPDE parameter set.

n_oscillators property

Number of moving oscillators.

phase_spec property

Underlying Doppler-Kuramoto phase-law spec.

__post_init__()

Build and freeze the Doppler phase spec used by the moving-frame solver.

MovingFrameUPDEState(t_s, phases_rad, positions_m, velocities_m_s, reference_point_m, separation_m, reference_error_m, order_parameter, phase_lock_error_rad, local_error_estimate) dataclass

Single moving-frame UPDE state snapshot.

MovingFrameUPDEReport(time_s, phases_rad, positions_m, separation_m, reference_error_m, order_parameter, phase_lock_error_rad, local_error_estimate) dataclass

Batch simulation trace for a moving-frame UPDE run.

MovingFrameUPDE(spec, phases_rad, positions_m, velocities_m_s)

Stateful fixed-step Dormand-Prince RK45 integrator for MIF-002.

state()

Return a read-only snapshot of the current state.

derivatives(phases_rad=None, positions_m=None, t_s=None)

Return the combined [dtheta/dt, dz/dt] derivative vector.

step(dt_s)

Advance the combined phase/position state by dt_s seconds.

time_to_reference_s()

Return non-negative time-to-reference estimates for each oscillator.

collision_imminent(eps_m=0.002)

Return whether all channels are inside eps_m of the reference point.

copy()

Return an independent copy of the current integrator state.

moving_frame_derivatives(spec, phases_rad, positions_m, velocities_m_s, t_s=0.0)

Return the combined [dtheta/dt, dz/dt] derivative vector.

evaluate_moving_frame_upde(spec, phases_rad, positions_m, velocities_m_s, dt_s, steps)

Run steps RK45 updates and return the full moving-frame trace.

Dispatch

Use scpn_mif_core.kinematic.dispatched_moving_frame_upde(...) for the fastest available measured backend:

"kinematic.moving_frame_upde" = ["rust", "python", "julia"]

The pure Python MovingFrameUPDE class remains available for deterministic debugging and tests.

Acceptance

The committed acceptance path uses the two-body chamber-centre scenario:

  • initial positions [-0.03, 0.03] m;
  • velocities [+300000, -300000] m s^-1;
  • reference point 0 m;
  • step size 1 ns.

After 100 steps both channels are inside the ±2 mm reference window and the max-min separation is within 4 mm. The first-step RK45 result is also checked against an independent Dormand-Prince tableau implementation in the Python tests, including the branch-cut case where a phase crosses +pi and the local error must remain a circular embedded delta instead of a spurious 2*pi jump.

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_2 Rust 591 ns fastest
derivatives_2 Python 19.56 us 33.1x slower than Rust
trace_120 Rust 143.41 us fastest
trace_120 Python 30.78 ms 214.6x slower than Rust
trace_120 Julia CLI 2.74 s CLI startup comparison
affine_trace_1000 Rust 865.91 us fastest
affine_trace_1000 Python 199.23 ms 230.1x slower than Rust
affine_trace_1000 Julia CLI 3.01 s CLI startup comparison

Raw summary: bench/results/moving_frame_upde.json.

Ownership

SYNC-STATE: upstream-pending applies to all MIF-002 implementation surfaces. SCPN-MIF-CORE owns the FRC-specific local carrier until SCPN-PHASE-ORCHESTRATOR receives the reusable scpn.upde.moving_frame surface targeted for the 0.7.0 lane.