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>
197 lines
8.8 KiB
Markdown
197 lines
8.8 KiB
Markdown
# 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 (~10–100 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.
|