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

197 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
```matlab
main % runs the default closed-loop scenario
```
From a terminal (figures appear as their own windows):
```bash
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:**
```matlab
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:**
```matlab
[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.