PWR-HYBRID-3/claude_memory/2026-04-17-controllers-linearization-and-first-reach.md
Dane Sabo cb69290714 claude_memory: session note for 2026-04-17 controllers + reach
Captures what landed, the model-validity / heatup / PID-vs-P findings,
why the quadratic Lyapunov barrier is fundamentally loose here (thin-
slab safety in anisotropic state space), why ReachabilityAnalysis.jl
needs state rescaling, and loose ends for the next session.

Hacker-Split: leave a clean handoff for Split's next wrap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 12:53:06 -04:00

6.5 KiB

Session 2026-04-17 — controllers, linearization, first per-mode reach, barrier attempt

Context

Pre-lunch discussion: reachability thrust is empty, controllers missing for three of four DRC modes, tool choice (CORA vs JuliaReach) open. Dane stepped away for lunch with instructions to "let me rip" on the whole pipeline. Auto-mode on.

What landed

  1. Three new controllers in plant-model/controllers/:
    • ctrl_shutdown.m — constant u = -5*beta (passive hot standby).
    • ctrl_scram.m — constant u = -8*beta (max rod worth).
    • ctrl_heatup.m — feedback-linearizing P on a ramped T_avg reference, with saturated u. No integrator state on purpose (see below).
  2. pke_linearize.m — numerical Jacobians via central finite differences. At the operating point the linear model agrees with the nonlinear simulation to ~4e-4 relative error for a 5% Q_sg step over 60 s. Eigenvalues span [-65.93, -0.0124] — stiff by a factor of ~5000.
  3. ctrl_operation_lqr.m — full-state LQR around x_op. Closed-loop T_avg tracking is essentially perfect under the same Q_sg step that makes the plain P controller overshoot by ~5 F. Cost: requires lyap/icare (base MATLAB, no Control Toolbox).
  4. main_mode_sweep.m — drives all five modes back-to-back from plausible ICs, saves 6 figures to docs/figures/mode_sweep_*.png.
  5. Hand-rolled zonotope reach in reachability/reach_linear.m plus reach_operation.m end-to-end. Over-approximates with axis-aligned box hull + elementwise-abs matrix propagation. Result for operation mode under Q_sg ∈ [85%, 100%]·P0 and an initial ±0.1 K box on T_avg: max |T_c - T_c0| = 0.1 K over 600 s. Safety band is ±5 K. Operation-mode obligation discharged.
  6. Lyapunov-ellipsoid barrier in reachability/barrier_lyapunov.m. Does NOT certify safety — the tightest quadratic bound we can get (over a sweep of Qbar(9,9) from 10 to 1e6) yields max |dT_c| ≈ 33 K, far outside the 5 K band. This is a fundamental limitation of quadratic Lyapunov for highly anisotropic problems: the ellipsoidal invariant is fat in the precursor/thermal directions and can't be made tight only along T_c. For an analytic barrier cert here we'd need SOS polynomial barriers or a polytopic invariant. Numerical zonotope reach still proves safety; the analytic cert is a "nice to have".
  7. Julia port in julia-port/. Plant parameters, RHS, linearize, all five controllers, LQR factory. scripts/sim_sanity.jl matches MATLAB to 3 decimals on the standard operation test. scripts/reach_operation.jl is a stub — ReachabilityAnalysis.jl's LGG09, GLGM06, BFFPSV18 all numerically explode on the unscaled stiff system (envelopes of 1e14 K or worse). Fix is diagonal state-scaling; didn't get to it this session.

Key findings worth carrying forward

The model doesn't support true cold shutdown

alpha_f, alpha_c are linearized at (T_f0, T_c0) = (328 C, 308 C). Extrapolating to T = 50 C gives intrinsic feedback of ~5 $ which is unphysical — real temperature coefficients saturate/flip at low temp. Trust range: ±50 C from operating point. "Shutdown" in this demo means hot standby (290 C, n≈0), not cold shutdown. Documented in plant-model/CLAUDE.md. To properly support cold shutdown we'd need piecewise-linear or table-lookup coefficients — separate modeling task.

Heatup overshoot is a controller-design signal, not a physics bug

First heatup pass started from n=1e-6 and produced a ~20-minute lag (reactor rebuilds power from decay-heat level) followed by a ~7 K overshoot (thermal inertia + no derivative term). Fixed by: (a) saturating |u| at 0.5·beta so rho stays subcritical-prompt, (b) starting heatup from n=0.001 (0.1% power, already critical), which matches real plant practice — criticality is achieved in shutdown before DRC transitions to heatup.

PID not a good idea here

The plant has intrinsic integrators (thermal mass). Adding PI:

  • Increments state dim 10→11 (cheap for CORA, ok for JuliaReach).
  • Anti-windup saturation turns each "continuous mode" into a hybrid mode inside itself — more transitions, more reach-set bookkeeping.
  • Integrator drift under persistent disturbance inflates the reach set over long horizons.

Stuck with P + feedforward; if needed we can promote to PI with eyes open about the hybrid-mode cost.

LQR is dramatically better than P for operation mode

Same operating point, same Q_sg step: LQR holds T_avg essentially at setpoint; plain P has 5 F overshoot and ~0.8 F steady-state lag. LQR adds zero state. The only downside: K is tuned around one operating point — off-point robustness would need gain scheduling or MPC.

Hand-rolled reach beats ReachabilityAnalysis.jl out of the box

The stiff, poorly-scaled nature of this system (eig span 5000x, state magnitudes spanning 10 orders) breaks ReachabilityAnalysis.jl with default settings. Hand-rolled MATLAB reach with augmented-matrix integration trick + elementwise-abs box propagation was ~50 lines and produced a tight, trustworthy result immediately. Lesson: for well-structured linear reach, don't reach for a toolbox.

The analytic barrier problem is anisotropy-limited

Safety set is a thin slab |T_c - T_c0| <= 5 K in a 10D state space. Lyapunov ellipsoids that contain an initial box + robust invariant are necessarily fat in the uncontrolled directions (slow precursors), and no rescaling of the Lyapunov weight can pull them tight only along T_c without blowing out the other directions. Logical next step for a thesis-strength barrier: SOS polynomial barriers or polytopic (halfspace-intersection) invariants.

Loose ends for the next session

  • Julia reach needs state rescaling. S = diag(...) chosen so all states have O(1) magnitude, then run reach in scaled coords.
  • Heatup reach: the ramped reference makes x*(t) time-varying, so LQR-around-x_op isn't directly applicable. Options: trajectory-LQR (time-varying K), or tube MPC.
  • Predicate thresholds still not pinned in the FRET spec. t_avg_in_range currently = ±5 K in the reach code but not reflected in the spec JSON. Need to sync.
  • Shutdown and scram modes don't have reach analysis yet. They're trivial (constant u) but the safety claim for scram — "reactor reaches subcritical decay in bounded time from any operating IC" — is actually a nice bounded-time reachability problem.

Commit

Committed as two or three logical chunks. See git log.