#!/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")