UPDE Doppler Engine
June 5, 2026 ยท View on GitHub
DopplerEngine is the PHA-C.2 kinematic phase-dynamics surface for moving
oscillator systems where relative velocity shifts the effective natural
frequency before phase coupling is applied. It is intended for counter-moving
plasmoids, moving sensor swarms, acoustic/clock synchronisation under motion,
and any domain where phase lock depends on velocity-corrected detuning rather
than static omega alone.
Mathematical contract
For each outer integration step the engine resolves omega(t) and scalarises
velocity into one value per oscillator. The effective frequency is
omega_eff_i(t) = omega_i(t) + D_i(t)
D_i(t) = s * sum_j |K_ij| * (v_i - v_j) / (|v_i| + epsilon) / sum_j |K_ij|
where s is doppler_strength, epsilon is doppler_epsilon, and rows with
no active coupling receive zero Doppler correction. The row normalisation keeps
doppler_strength independent of graph degree while the active K_nm topology
still determines which relative velocities are physically coupled.
Scalar velocities use the signed values directly. Vector velocities are reduced
to scalar speeds by Euclidean norm unless velocity_axis is supplied, in which
case velocities are projected onto the normalised axis so counter-propagating
motion keeps its sign.
Public API
import numpy as np
from scpn_phase_orchestrator.upde import DopplerEngine
knm = np.array([[0.0, 5.0], [5.0, 0.0]])
velocities = np.array([300.0, -300.0])
omega = np.array([-2.0, 2.0])
engine = DopplerEngine(
2,
omega=omega,
k_nm=knm,
alpha=0.0,
dt=1.0e-3,
velocities=velocities,
solver="euler",
)
phases = engine.run(n_steps=2_000)
print(phases, engine.doppler_term)
velocities may be a fixed array or a callable velocities(t) -> array.
omega may use the same fixed/callable forms supported by UPDEEngine.
Backend contract
Doppler integration is implemented as a schedule-backed UPDE run:
- Resolve
omega_schedule[step, i]. - Resolve
velocity_schedule[step, i]. - Compute graph-weighted
D_i. - Integrate one UPDE outer step with
omega_eff = omega + D.
The source contract exists for Python, Rust/PyO3, Go, Julia, and Mojo. The benchmark gate records unavailable optional runtimes explicitly rather than silently skipping parity evidence.
PYTHONPATH=src python benchmarks/upde_doppler_benchmark.py --parity-gate
Committed local benchmark artefacts are regression/parity evidence only unless rerun with the repository benchmark-isolation protocol.
Failure boundaries
DopplerEngine fails closed on:
- non-finite or non-real phases, omega schedules, velocities,
K_nm, oralpha; - boolean and object-dtype numeric aliases;
- non-zero self-coupling diagonal in
K_nm; - non-positive
doppler_epsilon; - malformed scalar or vector velocity shapes;
- backend outputs outside
[0, 2*pi).
Operational use case
- Doppler correction is used when velocity mismatch can destabilise the intended phase order before a policy loop can react.
- The row-normalised coupling form keeps response characteristics comparable across changing communication density and moving graph topologies.
- In review mode, compare
doppler_termtrends with regime transitions so you can separate physical transport effects from control-induced desynchronization.
Operational context
The Doppler surface makes motion effects explicit instead of forcing operators to absorb them as unexplained model error. That reduces false positives in review lanes that otherwise appear as sudden synchronization loss.
For fleets, moving swarms, or rotating hardware, this surface enables consistent comparisons by normalising relative-velocity effects by coupling structure.
Because output includes the per-step doppler_term, teams can attribute recovery
latency either to transport effects or to control actuation.
::: scpn_phase_orchestrator.upde.doppler.DopplerEngine
::: scpn_phase_orchestrator.upde.doppler.doppler_term
::: scpn_phase_orchestrator.upde.doppler.doppler_run
API documentation
::: scpn_phase_orchestrator.upde.doppler