Morning-review items 2 and 3.
Point 2 (scram X_entry expansion):
- reach_loca_operation.jl: LQR reach under Q_sg widened to
[0, 1.5*P_0] (steam-line break envelope) for 3 s horizon.
Longer horizons cause numerical blowup in the box-hull
reach propagator due to slow precursor modes amplifying
under large disturbance — documented in the script.
- reach_scram_pj_fat.jl: computes bounding-box union of
hot-standby + heatup-tight + operation + LOCA reach
envelopes, clamps obvious numerical outliers on precursors
and temperatures, builds a fat X_entry(scram), runs scram
PJ reach. Result pending (TMJets compiling).
Point 3 (heatup steam-dump Q_sg):
- configs/heatup/with_steam_dump.toml: Q_sg ∈ [0, 0.05·P_0]
as bounded parameter.
- reach_heatup_pj_sd.jl: 12-state RHS with x[10]=Q_sg (dx=0,
augmented bounded param) and x[11]=t. Running in
background.
Tight-entry heatup via the new TOML-config reach_heatup_pj.jl
reproduces the previous all-6-halfspaces-discharged result
(300s horizon, T_c envelope [281.05, 291.0]). Refactor
preserves semantics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
91 lines
3.5 KiB
Julia
91 lines
3.5 KiB
Julia
#!/usr/bin/env julia
|
|
#
|
|
# reach_loca_operation.jl — operation mode under a secondary-side LOCA.
|
|
#
|
|
# Simulates the fast transient just BEFORE scram fires: Q_sg jumps from
|
|
# nominal toward its worst-case runaway value (steam-line break = Q_sg
|
|
# spikes to ~1.5·P_0 as the primary cools rapidly). LQR can't handle
|
|
# this — scram is expected to fire. We care about WHERE the plant state
|
|
# ends up at the moment scram triggers, because that state becomes part
|
|
# of X_entry(scram).
|
|
#
|
|
# Uses the full-state linearized closed-loop under LQR, reach propagated
|
|
# with the existing hand-rolled reach_linear machinery (fast, sound for
|
|
# the linear model over short horizons).
|
|
|
|
using Pkg
|
|
Pkg.activate(joinpath(@__DIR__, "..", ".."))
|
|
|
|
using Printf
|
|
using LinearAlgebra
|
|
using MatrixEquations
|
|
using MAT
|
|
|
|
include(joinpath(@__DIR__, "..", "..", "src", "pke_params.jl"))
|
|
include(joinpath(@__DIR__, "..", "..", "src", "pke_th_rhs.jl"))
|
|
include(joinpath(@__DIR__, "..", "..", "src", "pke_linearize.jl"))
|
|
include(joinpath(@__DIR__, "..", "..", "src", "reach_linear.jl"))
|
|
|
|
plant = pke_params()
|
|
x_op = pke_initial_conditions(plant)
|
|
|
|
A, B, B_w, _, _, _ = pke_linearize(plant)
|
|
Q_lqr = Diagonal([1.0, 1e-3, 1e-3, 1e-3, 1e-3, 1e-3, 1e-3, 1e-2, 1e2, 1.0])
|
|
R_lqr = 1e6 * ones(1, 1)
|
|
X_ric, _, _ = arec(A, reshape(B, :, 1), R_lqr, Matrix(Q_lqr))
|
|
K = (R_lqr \ reshape(B, 1, :)) * X_ric
|
|
A_cl = A - reshape(B, :, 1) * K
|
|
|
|
# LOCA entry box — small perturbation around operating point (plant was
|
|
# nominally stable before the break).
|
|
delta_entry = [0.01 * x_op[1];
|
|
0.001 .* abs.(x_op[2:7]);
|
|
0.1; 0.1; 0.1]
|
|
|
|
# LOCA disturbance envelope: Q_sg spikes from P_0 up to 1.5·P_0 (break
|
|
# releases 50% more heat into secondary) or can collapse to 0 (break on
|
|
# a specific line isolates a loop). Either way widens the Q_sg range.
|
|
Q_nom = plant.P0
|
|
Q_min = 0.0
|
|
Q_max = 1.5 * plant.P0
|
|
w_lo = Q_min - Q_nom # -P_0
|
|
w_hi = Q_max - Q_nom # +0.5 P_0
|
|
|
|
# Short transient: scram must fire within a few seconds of detection
|
|
# of the LOCA event. 3 s is realistic (RPS trip + rod free-fall).
|
|
# Over longer horizons, the box-hull reach diverges numerically
|
|
# because precursor modes have slow return times (~56 s for group 1)
|
|
# and the worst-case |A_step|^k propagation grows super-linearly
|
|
# without cross-term cancellation — a known limitation of box-hull
|
|
# reach vs true zonotope propagation. The 3 s result captures the
|
|
# physically relevant transient before the DRC intervenes.
|
|
tspan = (0.0, 3.0)
|
|
dt = 0.05
|
|
|
|
T, R_lo, R_hi, Cnom = reach_linear(A_cl, B_w, zeros(10), delta_entry,
|
|
w_lo, w_hi, tspan, dt)
|
|
X_lo = R_lo .+ x_op
|
|
X_hi = R_hi .+ x_op
|
|
|
|
state_names = ["n","C1","C2","C3","C4","C5","C6","T_f","T_c","T_cold"]
|
|
println("\n=== LOCA reach — final-state envelope at t=$(tspan[2]) s ===")
|
|
println(" Disturbance: Q_sg ∈ [$(Q_min/1e6), $(Q_max/1e6)] MW (LOCA break)")
|
|
@printf " %-7s %-14s %-14s\n" "state" "lo" "hi"
|
|
for i in 1:10
|
|
@printf " %-7s %-14.4e %-14.4e\n" state_names[i] X_lo[i, end] X_hi[i, end]
|
|
end
|
|
|
|
# Save final-state envelope (per-state lo/hi at t=tspan[2]) for consumption
|
|
# by reach_scram_pj_fat_entry.jl.
|
|
mat_out = joinpath(@__DIR__, "..", "..", "..", "results", "reach_loca_operation.mat")
|
|
matwrite(mat_out, Dict(
|
|
"X_final_lo" => X_lo[:, end],
|
|
"X_final_hi" => X_hi[:, end],
|
|
"X_envelope_lo" => [minimum(X_lo[i, :]) for i in 1:10],
|
|
"X_envelope_hi" => [maximum(X_hi[i, :]) for i in 1:10],
|
|
"T" => collect(T),
|
|
"w_lo" => w_lo, "w_hi" => w_hi,
|
|
"state_names" => state_names,
|
|
))
|
|
println("\nSaved LOCA envelope to $mat_out")
|