SCPN-MIF-CORE merge-trigger quickstart¶

This notebook runs one public scpn_mif_core merge-trigger decision. It uses the same typed API as scripts and tests: a kinematic moving-frame model, a merge-window monitor, a kinematic safety certificate, and a capacitor-bank feasibility check.

The result is a software decision artifact, not a hardware firing command.

In [1]:
try:
    import scpn_mif_core  # noqa: F401
except ModuleNotFoundError:
    %pip install scpn-mif-core[demo]
In [2]:
import matplotlib.pyplot as plt
import numpy as np

from scpn_mif_core import (
    CapacitorBankSpec,
    KinematicSafetySpec,
    MergeWindowSpec,
    MovingFrameUPDESpec,
    PulseSpec,
    evaluate_merge_trigger,
)
from scpn_mif_core.merge_trigger import MergeTriggerScenario
In [3]:
scenario = MergeTriggerScenario(
    moving_frame=MovingFrameUPDESpec(
        omega_rad_s=np.asarray([1.0, 1.0]),
        coupling_rad_s=np.asarray([[0.0, 50.0], [50.0, 0.0]]),
        doppler_strength_rad_s=0.0,
        distance_scale_m=1.0,
    ),
    initial_phases_rad=np.asarray([0.0, 0.004]),
    initial_positions_m=np.asarray([-5.0e-4, 5.0e-4]),
    velocities_m_s=np.asarray([0.0, 0.0]),
    dt_s=1.0e-3,
    steps=20,
    merge_window=MergeWindowSpec(
        phase_tolerance_rad=0.01,
        spatial_tolerance_m=0.002,
        consecutive_samples=3,
    ),
    safety=KinematicSafetySpec(),
    bank=CapacitorBankSpec(
        capacitance_F=1.0e-3,
        inductance_H=1.0e-6,
        series_resistance_ohm=1.0e-3,
        voltage_max_V=2.0e4,
        recharge_power_kW=10.0,
    ),
    bank_initial_voltage_V=2.0e4,
    compression_pulse=PulseSpec(
        peak_current_A=1.0e5,
        duration_s=1.0e-5,
        waveform="half_sine",
    ),
)

report = evaluate_merge_trigger(scenario)
report.outcome.value, report.reason
Out[3]:
('fire', 'locked and safe at t = 0.002 s with a feasible compression pulse')
In [4]:
summary = {
    "outcome": report.outcome.value,
    "reason": report.reason,
    "lock_achieved": report.lock_achieved,
    "first_lock_time_s": report.first_lock_time_s,
    "safety_passed": report.safety_passed,
    "bank_feasible": report.bank_feasible,
    "min_separation_m": report.min_separation_m,
}
summary
Out[4]:
{'outcome': 'fire',
 'reason': 'locked and safe at t = 0.002 s with a feasible compression pulse',
 'lock_achieved': True,
 'first_lock_time_s': 0.002,
 'safety_passed': True,
 'bank_feasible': True,
 'min_separation_m': 0.001}
In [5]:
samples = report.merge_trace.samples
times = np.array([s.t_s for s in samples])
phase_err = np.array([s.phase_lock_error_rad for s in samples])
separation = np.array([s.separation_m for s in samples])

fig, ax = plt.subplots(2, 1, figsize=(8, 5), sharex=True)
ax[0].plot(times, phase_err, label="phase error")
ax[0].axhline(scenario.merge_window.phase_tolerance_rad, color="tab:red", linestyle="--", label="phase tolerance")
ax[0].set_ylabel("rad")
ax[0].legend(loc="best")

ax[1].plot(times, separation, label="separation")
ax[1].axhline(scenario.merge_window.spatial_tolerance_m, color="tab:red", linestyle="--", label="spatial tolerance")
ax[1].set_xlabel("time [s]")
ax[1].set_ylabel("m")
ax[1].legend(loc="best")
fig.tight_layout()
No description has been provided for this image