Dane Sabo bc3a6028a9 docs: flag soundness, alpha-drift, saturation-hybrid in file headers
Three caveats surfaced during walkthrough lived only in the
conversation transcript before this commit.  Now they live where
future agents and future-me will actually see them:

- reach_operation.m and reachability/README.md state prominently that
  the current reach tube is an over-approximation of the LINEAR
  model, not a sound tube for the nonlinear plant.  Thesis-blocking
  for a real safety claim.  Upgrade paths documented.

- ctrl_heatup.m header and plant-model/CLAUDE.md note that the
  feedback-linearization u_ff assumes exact alpha_f, alpha_c.  Real
  plants drift (burnup ~20%, boron ~10x, xenon).  Robust treatment =
  parametric reach with alpha as an interval.

- ctrl_heatup.m header and plant-model/CLAUDE.md note that sat() is
  formally a 3-mode piecewise-affine sub-system.  Operation-mode LQR
  is dormant (trivially); heatup will need either a dormancy proof
  or explicit hybrid modeling.

README.md top-level now has a run-commands table for the reach
artifacts and a pointer to the soundness status.

Hacker-Split: raise caveats from transcript to artifact so the work
is actually reviewable by people who weren't in the room.

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

8.8 KiB
Raw Blame History

CLAUDE.md

Guidance for Claude Code (and any AI agent) working in this subdirectory.

See also: the parent repo's CLAUDE.md (living-documentation rule — update this file when new knowledge is created) and claude_memory/ for session notes that haven't yet earned a place here.

What this is

A 10-state point kinetic equation (PKE) model of a PWR coupled with lumped-parameter thermal-hydraulics, plus mode-specific continuous controllers. Implements the plant and the tactical layer of the HAHACS methodology — see ../thesis/3-research-approach/approach.tex sections on transitory, stabilizing, and expulsory modes.

The model is being developed for hybrid-systems reachability analysis. The end goal is barrier-certificate and reachability methods (in ../reachability/) to prove each continuous mode satisfies its obligations under bounded Q_sg(t) variations.

Naming convention (thesis-aligned)

Continuous state and control-theory notation matches the thesis:

Symbol Meaning
t time [s]
x continuous state vector, [n; C1..C6; T_f; T_c; T_cold]
plant parameter struct from pke_params() (was p — renamed to avoid collision with p_i predicates)
u control input: external rod reactivity [dk/k]
Q_sg bounded disturbance: SG heat removal [W], as a function handle Q_sg(t)
ref reference/setpoint struct (e.g. ref.T_avg)

T_avg = T_c (algebraic alias); T_hot = 2*T_c - T_cold (linear coolant profile assumption).

Running

From inside MATLAB:

main     % runs the default closed-loop scenario

From a terminal (figures appear as their own windows):

matlab -nodesktop -nosplash -r "main"     # drops at >> prompt; `exit` to quit
matlab -r "main" &                        # full IDE, backgrounded
matlab -batch "main"                      # headless sanity check (no figures shown)

main.m configures the scenario (plant, Q_sg, ref, tspan), picks a controller, and calls pke_solver. Tested on MATLAB R2025b; works on any MATLAB with ode15s (R2020b or newer).

Architecture

plant-model/
  pke_params.m               plant parameters and derived steady state
  pke_initial_conditions.m   analytic equilibrium x0 from plant
  pke_th_rhs.m               dynamics:  dx/dt = f(t, x, plant, Q_sg, u)
  pke_linearize.m            numerical (A, B, B_w) at any x_star
  pke_solver.m               closed-loop driver (optional x0 arg)
  plot_pke_results.m         4-panel results plot
  load_profile.m             canned Q_sg demand shapes
  main.m                     entry point — single-mode scenarios
  main_mode_sweep.m          runs all DRC modes back-to-back
  test_linearize.m           sanity-check Jacobians vs nonlinear sim
  controllers/
    ctrl_null.m              u = 0 (baseline, no feedback)
    ctrl_shutdown.m          hot standby: u = -5*beta (constant)
    ctrl_heatup.m            ramp T_avg: feedback lin. + P on ramp, saturated u
    ctrl_operation.m         stabilizing: P on T_avg
    ctrl_operation_lqr.m     full-state LQR around x_op (persistent-cached K)
    ctrl_scram.m             emergency: u = -8*beta (constant)

Controller contract:

u = ctrl_<mode>(t, x, plant, ref)

Pure function. Returns scalar u (external rod reactivity in dk/k). All four signature args are required even if unused — lets the solver swap controllers by function handle without caring which one it is.

Solver contract:

[t, X, U] = pke_solver(plant, Q_sg, ctrl_fn, ref, tspan)

Builds the closed-loop RHS internally, calls ode15s, then reconstructs the control trajectory U by re-evaluating ctrl_fn at each returned time step. For event-driven or hysteretic controllers, this reconstruction may miss interior solver evaluations — revisit if / when that matters.

Key design decisions

  • Q_sg is the disturbance, never an actuator. Cold leg temperature is a dynamic state resulting from SG heat removal — the reachability formulation needs Q_sg ∈ [Q_min, Q_max] as a bounded disturbance, not a control input.
  • u is explicit. pke_th_rhs takes u directly instead of reading p.rho_ext(t) from the params struct. This decouples plant from controller and matches the thesis's f(x, u) formulation.
  • Controller gains live in the controller file. Rough-out phase — we'll lift to a config struct once we're tuning more than one mode. The gain in ctrl_operation (Kp = 1e-4 /K) is chosen to roughly double the effective moderator coefficient, not for precision tracking.
  • ode15s is required because the system is stiff: Lambda (~10⁻⁴ s) vs thermal time constants (~10100 s).

Conventions

  • One function per file, each file has a single responsibility.
  • Internal model uses SI (W, kg, °C); all printed/plotted temperatures in °F.
  • Reference setpoints in SI (so ref.T_avg is in °C).
  • Controllers are pure functions — no persistent state. If a mode ever needs integral action, augment x rather than hiding state in globals.

Model validity range

The feedback coefficients alpha_f, alpha_c are linear slopes taken about the full-power operating point (T_f0, T_c0). They become unphysical when extrapolated far from that reference — e.g. at true cold shutdown (~50 C everywhere), the model predicts ~+5 $ intrinsic reactivity that real reactors do not exhibit because temperature coefficients saturate and flip sign in the cold-unborated state.

Trust range: roughly ±50 C around the operating point. "Shutdown" in this demo means hot standby (~290 C, n≈0), not cold shutdown. Extending to cold-shutdown semantics requires piecewise-linear or table-lookup temperature coefficients and is out of scope for the running example.

For reachability work

  • pke_th_rhs(t, x, plant, Q_sg, u) is the ẋ = f(x, u) with Q_sg as the disturbance w. For a given mode, substitute u = ctrl_<mode>(t, x, plant, ref) to get the closed-loop ẋ = f_cl(x, w).
  • Use pke_linearize(plant, x_star, u_star, Q_star) for (A, B, B_w) at any operating point; returns central-finite-difference Jacobians. The result at the full-power point is saved to ../reachability/linearization_at_op.mat by test_linearize.m.
  • Safe regions come from the FRET spec's guards (see ../fret-pipeline/specs/synthesis_config_v3.json) — these define X_entry, X_exit, X_safe per the thesis approach. Numerical predicate thresholds still need to be pinned (see ../claude_memory/ notes for the discussion).
  • Do NOT try to verify the whole hybrid system at once. Pick one mode, compute its reach set, discharge the per-mode obligation. The compositionality argument in the thesis is what makes this tractable.
  • First reach set is in ../reachability/reach_operation.m (operation mode under LQR); a Lyapunov-ellipsoid barrier-cert attempt is in ../reachability/barrier_lyapunov.m.

For control design

  • The LQR gain in ctrl_operation_lqr.m is cached in a persistent variable. If you change Q_lqr or R_lqr or mutate plant, restart MATLAB to clear the cache.
  • ctrl_heatup.m uses feedback linearization + saturated P. No integrator on purpose — adding PI would double the plant's intrinsic thermal-mass integration and make anti-windup a hybrid mode, both bad for reach.
  • Starting heatup from a truly zero-power IC produces a large lag + power-spike overshoot in the simulation. Physical resolution: the reactor should be taken critical in shutdown mode (at ~0.1% power) before DRC transitions to heatup. main_mode_sweep.m uses this IC.

Robustness caveats (idealized in current artifacts)

  • α_f, α_c are treated as known exactly. In reality α_f drifts ~20% over burnup; α_c spans ~10x across soluble-boron dilution over a cycle; xenon adds 2-3 $ reactivity on its own timescale. The feedback-linearization in ctrl_heatup.m assumes the controller's α matches the plant's; if not, the clean rho_total = Kp*e property degrades to Kp*e + delta*alpha*dT, and the P term must absorb the residual. Stabilization still holds but reach analysis should eventually treat α as a bounded parametric uncertainty.
  • Saturation is a hybrid sub-mode. The sat(u, u_min, u_max) in ctrl_heatup.m is formally piecewise affine. Current reach treats it as dormant (true for operation/LQR, near-true for the demo heatup trajectory). A rigorous heatup reach has to model the saturation regions explicitly.
  • Linear-model reach is not sound for the nonlinear plant. The reach artifacts in ../reachability/ use the linearization; the result is a sound over-approximation of the LINEAR model's reach, not of the plant's. To upgrade: nonlinear reach directly, or linear reach + Taylor-remainder inflation. See ../reachability/README.md § Soundness status.