From 645f2d8d2710736cd7a81175d4484ec08a26af54 Mon Sep 17 00:00:00 2001 From: Dane Sabo Date: Mon, 20 Apr 2026 22:45:24 -0400 Subject: [PATCH] prompt-jump model + app v2 + overnight journal entry (in progress) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Singular-perturbation reduction of the PKE+T/H system: set dn/dt=0, solve algebraically n = Λ·Σλ_i·C_i / (β-ρ). State drops 10 -> 9 (no n), removes Λ⁻¹ stiffness. Validated against full state on the heatup scenario: t [s] |Δn|/n_full T_c err [K] 60 3.7e-5 4e-6 300 3.8e-4 1.9e-4 1200 1.0e-3 2.2e-3 3000 5.0e-4 7.2e-3 Maximum relative error 0.1% on n, peak 7 mK on temperatures over 50 minutes. PJ approximation is excellent for slow heatup transients (sub-prompt-critical regime). Files: - code/src/pke_th_rhs_pj.jl: reduced 9-state RHS - code/scripts/validate_pj.jl: side-by-side sim - code/scripts/reach_heatup_pj.jl: TMJets reach with PJ model (probing T = 60, 300, 1800, 5400 s) App v2 (Pluto): - §9b: live ingestion of reach_operation_result.mat with per- halfspace margins computed from JSON-defined inv2_holds. - §9c: 2D projection chooser (n, T_f, T_c, T_cold) with reach tube envelope overlay. - §9d: PJ heatup reach summary (placeholder until first run lands). Journal: - Added 2026-04-20-overnight-prompt-jump.tex with PJ derivation, validation table, soundness ledger update. apass markers for the in-progress reach results. This commit captures state mid-run; next commit will add the populated reach results once TMJets returns. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/Project.toml | 1 + app/predicate_explorer.jl | 169 ++++++++++++++ code/scripts/reach_heatup_pj.jl | 218 ++++++++++++++++++ code/scripts/validate_pj.jl | 113 +++++++++ code/src/pke_th_rhs_pj.jl | 104 +++++++++ docs/figures/validate_pj_heatup.png | Bin 0 -> 60025 bytes .../2026-04-20-overnight-prompt-jump.tex | 218 ++++++++++++++++++ journal/journal.tex | 2 + 8 files changed, 825 insertions(+) create mode 100644 code/scripts/reach_heatup_pj.jl create mode 100644 code/scripts/validate_pj.jl create mode 100644 code/src/pke_th_rhs_pj.jl create mode 100644 docs/figures/validate_pj_heatup.png create mode 100644 journal/entries/2026-04-20-overnight-prompt-jump.tex diff --git a/app/Project.toml b/app/Project.toml index 3b26fbb..6d14573 100644 --- a/app/Project.toml +++ b/app/Project.toml @@ -2,6 +2,7 @@ authors = ["Dane Sabo "] [deps] JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +MAT = "23992714-dd62-5051-b70f-ba57cb901cac" Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" diff --git a/app/predicate_explorer.jl b/app/predicate_explorer.jl index 5ed5371..d1dacc8 100644 --- a/app/predicate_explorer.jl +++ b/app/predicate_explorer.jl @@ -26,6 +26,7 @@ begin using JSON using PlutoUI using Plots + using MAT gr() end @@ -312,6 +313,165 @@ md""" | `q_shutdown` | (TBD) | ❌ not started (trivial — constant u) | — | """ +# ╔═╡ a74106fb-d501-428a-a02e-41f55b49b3da +md""" +## 9b · Live reach result ingestion + +If `code/scripts/reach_operation.jl` has been run, `reach_operation_result.mat` +exists in `../reachability/`. Load it and show per-halfspace margins live — +no more hand-maintained traceability table. +""" + +# ╔═╡ 37d6b212-9f00-4684-9f91-50c7e17cbd62 +begin + reach_mat_path = joinpath(@__DIR__, "..", "reachability", "reach_operation_result.mat") + have_reach_mat = isfile(reach_mat_path) + if have_reach_mat + reach_mat = matread(reach_mat_path) + md"""**Loaded** `reach_operation_result.mat` — keys: $(join(sort(collect(keys(reach_mat))), ", ")).""" + else + md"""_(no `reach_operation_result.mat` found — run `cd code && julia --project=. scripts/reach_operation.jl`)_""" + end +end + +# ╔═╡ df53126a-1efa-4cc7-87f4-be4ff0808513 +begin + inv2_haspath = haskey(pred_raw["mode_invariants"], "inv2_holds") + if have_reach_mat && inv2_haspath + # Reconstruct per-halfspace margins from the loaded reach tube. + T_op_const = 308.349 + T_f0_const = 328.349 + Tcold0_const = 290.0 + Tstandby_local = T_op_const + pred_raw["derived"]["T_standby_offset_C"] + eval_local(expr) = let T_c0=T_op_const, T_f0=T_f0_const, T_cold0=Tcold0_const, T_standby=Tstandby_local + eval(Meta.parse(expr)) + end + + # Build A_inv, b_inv from JSON for inv2_holds (same logic as load_predicates). + inv2_entry = pred_raw["mode_invariants"]["inv2_holds"] + components = inv2_entry["conjunction_of"] + A_inv = zeros(0, 10) + b_inv = zeros(0) + labels = String[] + for comp in components + entry = pred_raw["safety_limits"][comp] + for hs in entry["halfspaces"] + row = zeros(1, 10) + if haskey(hs, "row") + for pair in hs["row"] + row[1, Int(pair[1])] = Float64(pair[2]) + end + else + row[1, Int(hs["state_index"])] = Float64(hs["coeff"]) + end + A_inv = vcat(A_inv, row) + b = eval_local(hs["rhs_expr"]) + b_inv = vcat(b_inv, b) + push!(labels, comp) + end + end + + X_lo_mat = reach_mat["X_lo"] + X_hi_mat = reach_mat["X_hi"] + + rows = String[] + push!(rows, "| Safety halfspace | Limit | Reach max | Margin | |") + push!(rows, "|---|---:|---:|---:|---|") + for k in 1:size(A_inv, 1) + a = A_inv[k, :] + env = (a .> 0) .* X_hi_mat .+ (a .< 0) .* X_lo_mat + zero_mask = (a .== 0) + env[zero_mask, :] .= X_hi_mat[zero_mask, :] + max_ax = maximum(a' * env) + margin = b_inv[k] - max_ax + badge = margin >= 0 ? "✅" : "❌" + push!(rows, "| `$(labels[k])` | $(round(b_inv[k]; digits=3)) | $(round(max_ax; digits=3)) | $(round(margin; digits=3)) | $badge |") + end + Markdown.parse(join(rows, "\n")) + else + md"_(reach result or inv2_holds unavailable; cannot render live margins)_" + end +end + +# ╔═╡ 9bd8d609-ffa4-46a7-8f0f-d093d87e45e9 +md""" +## 9c · 2D projection chooser + +Pick any two state coordinates; render the operating polytope inside the +safety limits, with the reach tube envelope overlaid (if loaded). +""" + +# ╔═╡ e4d56600-7a46-46a1-a7c9-f65a5db95f66 +@bind proj_x Select(["n", "T_f", "T_c", "T_cold"], default="T_c") + +# ╔═╡ 4fdf70de-3f58-4a38-917b-62b3689ce534 +@bind proj_y Select(["n", "T_f", "T_c", "T_cold"], default="n") + +# ╔═╡ 37251c95-dd46-402f-9579-d4ede2c1e5ff +begin + state_idx = Dict("n"=>1, "T_f"=>8, "T_c"=>9, "T_cold"=>10) + state_units = Dict("n"=>"", "T_f"=>" [°C]", "T_c"=>" [°C]", "T_cold"=>" [°C]") + + px_idx = state_idx[proj_x] + py_idx = state_idx[proj_y] + + # Determine reasonable axis ranges from operating point + 5% margins. + x_op_demo = [1.0, + 173.4, 467.0, 114.8, 85.3, 6.56, 0.91, + 328.35, 308.35, 290.0] + px_op = x_op_demo[px_idx] + py_op = x_op_demo[py_idx] + px_span = max(0.2 * abs(px_op), 5.0) + py_span = max(0.2 * abs(py_op), 5.0) + + p_proj = plot(xlim=(px_op - px_span, px_op + px_span), + ylim=(py_op - py_span, py_op + py_span), + xlabel=proj_x * state_units[proj_x], + ylabel=proj_y * state_units[proj_y], + title="Projection: $proj_y vs $proj_x", + size=(700, 450), legend=:topleft) + + # Overlay reach tube envelope as a rectangle. + if have_reach_mat + X_lo_local = reach_mat["X_lo"] + X_hi_local = reach_mat["X_hi"] + rx_lo = minimum(X_lo_local[px_idx, :]) + rx_hi = maximum(X_hi_local[px_idx, :]) + ry_lo = minimum(X_lo_local[py_idx, :]) + ry_hi = maximum(X_hi_local[py_idx, :]) + plot!(p_proj, [rx_lo, rx_hi, rx_hi, rx_lo, rx_lo], + [ry_lo, ry_lo, ry_hi, ry_hi, ry_lo], + lw=2, color=:red, label="reach tube envelope") + end + + # Operating point. + scatter!(p_proj, [px_op], [py_op], color=:black, markersize=8, + label="x_op") + p_proj +end + +# ╔═╡ 4d8459c1-a937-4b53-9eca-975261d6a2cd +md""" +## 9d · Prompt-jump heatup reach (if available) + +If `code/scripts/reach_heatup_pj.jl` has been run, the latest result +shows up here as a T_c reach envelope vs time. + +This is the singular-perturbation reduction that lets nonlinear reach +extend past the ~10 s prompt-neutron stiffness wall. +""" + +# ╔═╡ 45e8962a-873e-48d3-aac4-70ea0cf36c97 +begin + pj_mat_path = joinpath(@__DIR__, "..", "reachability", "reach_heatup_pj_result.mat") + if isfile(pj_mat_path) + pj_mat = matread(pj_mat_path) + md"""**Loaded** `reach_heatup_pj_result.mat` — pending plot rendering.""" + else + md"""_(no `reach_heatup_pj_result.mat` yet — run `cd code && julia --project=. scripts/reach_heatup_pj.jl`)_""" + end +end + # ╔═╡ de922a70-b653-4b2c-bb13-ccc14b14ac91 md""" ## 10 · Notes for the next pass @@ -357,4 +517,13 @@ md""" # ╟─b7fdf92d-948b-41f2-8516-dd8fc20c2efe # ╟─0560c66a-c12f-4f15-bc49-2c9c8164abd1 # ╟─1f6abf31-3618-46a5-9f57-4cb3c8022f89 +# ╟─a74106fb-d501-428a-a02e-41f55b49b3da +# ╠═37d6b212-9f00-4684-9f91-50c7e17cbd62 +# ╠═df53126a-1efa-4cc7-87f4-be4ff0808513 +# ╟─9bd8d609-ffa4-46a7-8f0f-d093d87e45e9 +# ╟─e4d56600-7a46-46a1-a7c9-f65a5db95f66 +# ╟─4fdf70de-3f58-4a38-917b-62b3689ce534 +# ╠═37251c95-dd46-402f-9579-d4ede2c1e5ff +# ╟─4d8459c1-a937-4b53-9eca-975261d6a2cd +# ╠═45e8962a-873e-48d3-aac4-70ea0cf36c97 # ╟─de922a70-b653-4b2c-bb13-ccc14b14ac91 diff --git a/code/scripts/reach_heatup_pj.jl b/code/scripts/reach_heatup_pj.jl new file mode 100644 index 0000000..bf49613 --- /dev/null +++ b/code/scripts/reach_heatup_pj.jl @@ -0,0 +1,218 @@ +#!/usr/bin/env julia +# +# reach_heatup_pj.jl — nonlinear reach on heatup, prompt-jump model. +# +# Reduced from 10-state to 9-state (n is algebraic). Removes the +# Λ⁻¹ stiffness that capped the full-state reach at ~10 s. We push +# horizons up: 60 s, 300 s, 1800 s, 5400 s, full T_max = 18000 s (5 hr). +# +# State (10D with augmented time): +# x[1..6] = C_1..C_6 (delayed-neutron precursors) +# x[7] = T_f +# x[8] = T_c +# x[9] = T_cold +# x[10] = t (augmented time, dt/dt = 1) +# +# n is algebraic: n = Λ · Σ λ_i C_i / (β - ρ), ρ = K_p · (T_ref - T_c). + +using Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +using LinearAlgebra +using ReachabilityAnalysis, LazySets +using JSON +using MAT + +# --- Inlined plant constants (must match pke_params) --- +const LAMBDA = 1e-4 +const BETA_1, BETA_2, BETA_3, BETA_4, BETA_5, BETA_6 = + 0.000215, 0.001424, 0.001274, 0.002568, 0.000748, 0.000273 +const BETA = BETA_1 + BETA_2 + BETA_3 + BETA_4 + BETA_5 + BETA_6 +const LAM_1, LAM_2, LAM_3, LAM_4, LAM_5, LAM_6 = + 0.0124, 0.0305, 0.111, 0.301, 1.14, 3.01 + +const P0 = 1e9 +const M_F, C_F, M_C, C_C, HA, W_M, M_SG = + 50000.0, 300.0, 20000.0, 5450.0, 5e7, 5000.0, 30000.0 + +# Note: feedback-linearization in ctrl_heatup_unsat cancels the alpha +# terms exactly, so under closed-loop the effective rho is just Kp·e. +# We don't need ALPHA_F, ALPHA_C in the reach RHS as a result. + +const T_COLD0 = 290.0 +const DT_CORE = P0 / (W_M * C_C) +const T_HOT0 = T_COLD0 + DT_CORE +const T_C0 = (T_HOT0 + T_COLD0) / 2 +const T_F0 = T_C0 + P0 / HA + +const T_STANDBY = T_C0 - 33.333333 +const RAMP_RATE_CS = 28.0 / 3600 +const KP_HEATUP = 1e-4 + +# --- Taylorized PJ heatup RHS, 10D with augmented time --- +@taylorize function rhs_heatup_pj_taylor!(dx, x, p, t) + # rho_total under closed-loop feedback linearization = Kp · (T_ref - T_c). + rho = KP_HEATUP * (T_STANDBY + RAMP_RATE_CS * x[10] - x[8]) + + # Algebraic prompt-jump n. + sum_lam_C = LAM_1*x[1] + LAM_2*x[2] + LAM_3*x[3] + + LAM_4*x[4] + LAM_5*x[5] + LAM_6*x[6] + denom = BETA - rho + n = LAMBDA * sum_lam_C / denom + inv_factor = sum_lam_C / denom + + # Precursor balance under PJ. + dx[1] = BETA_1 * inv_factor - LAM_1 * x[1] + dx[2] = BETA_2 * inv_factor - LAM_2 * x[2] + dx[3] = BETA_3 * inv_factor - LAM_3 * x[3] + dx[4] = BETA_4 * inv_factor - LAM_4 * x[4] + dx[5] = BETA_5 * inv_factor - LAM_5 * x[5] + dx[6] = BETA_6 * inv_factor - LAM_6 * x[6] + + # Thermal — n appears algebraically in fuel eq. + dx[7] = (P0 * n - HA * (x[7] - x[8])) / (M_F * C_F) + dx[8] = (HA * (x[7] - x[8]) - 2 * W_M * C_C * (x[8] - x[9])) / (M_C * C_C) + dx[9] = (2 * W_M * C_C * (x[8] - x[9])) / (M_SG * C_C) + + # Augmented time. + dx[10] = one(x[1]) + + return nothing +end + +# --- Build X_entry (PJ form: no n) from predicates.json --- +pred_path = joinpath(@__DIR__, "..", "..", "reachability", "predicates.json") +pred_raw = JSON.parsefile(pred_path) +entry = pred_raw["mode_boundaries"]["q_heatup"]["X_entry_polytope"] + +n_lo, n_hi = entry["n_range"] +T_f_lo, T_f_hi = entry["T_f_range_C"] +T_c_lo, T_c_hi = entry["T_c_range_C"] +T_cold_lo, T_cold_hi = entry["T_cold_range_C"] + +n_mid = 0.5 * (n_lo + n_hi) +C_mid_1 = (BETA_1 / (LAM_1 * LAMBDA)) * n_mid +C_mid_2 = (BETA_2 / (LAM_2 * LAMBDA)) * n_mid +C_mid_3 = (BETA_3 / (LAM_3 * LAMBDA)) * n_mid +C_mid_4 = (BETA_4 / (LAM_4 * LAMBDA)) * n_mid +C_mid_5 = (BETA_5 / (LAM_5 * LAMBDA)) * n_mid +C_mid_6 = (BETA_6 / (LAM_6 * LAMBDA)) * n_mid + +# C_i scale linearly with n; sweep across the n_lo..n_hi band. +x_lo = [C_mid_1 * (n_lo / n_mid); C_mid_2 * (n_lo / n_mid); + C_mid_3 * (n_lo / n_mid); C_mid_4 * (n_lo / n_mid); + C_mid_5 * (n_lo / n_mid); C_mid_6 * (n_lo / n_mid); + T_f_lo; T_c_lo; T_cold_lo; + 0.0] +x_hi = [C_mid_1 * (n_hi / n_mid); C_mid_2 * (n_hi / n_mid); + C_mid_3 * (n_hi / n_mid); C_mid_4 * (n_hi / n_mid); + C_mid_5 * (n_hi / n_mid); C_mid_6 * (n_hi / n_mid); + T_f_hi; T_c_hi; T_cold_hi; + 0.0] + +X0 = Hyperrectangle(low=x_lo, high=x_hi) + +println("\n=== Nonlinear heatup reach, prompt-jump model ===") +println(" X_entry (n-implied range): n ∈ [$(n_lo), $(n_hi)]") +println(" T_c ∈ [$(T_c_lo), $(T_c_hi)] °C") + +T_RAMP_END = (T_C0 - T_STANDBY) / RAMP_RATE_CS +println(" T_ramp_end = $(round(T_RAMP_END; digits=0)) s ($(round(T_RAMP_END/60; digits=1)) min)") +println(" Probing horizons up to T_max(heatup) = 18000 s (5 hr).") + +# Probe at increasing horizons. Stop early if any probe fails. +probe_horizons = (60.0, 300.0, 1800.0, 5400.0) + +results = Dict{Float64, Any}() +for T_probe in probe_horizons + println("\n--- Probe T = $T_probe s ($(round(T_probe/60; digits=1)) min) ---") + sys = BlackBoxContinuousSystem(rhs_heatup_pj_taylor!, 10) + prob = InitialValueProblem(sys, X0) + try + alg = TMJets(orderT=4, orderQ=2, abstol=1e-9, maxsteps=100000) + t_start = time() + sol = solve(prob; T=T_probe, alg=alg) + elapsed = time() - t_start + flow = flowpipe(sol) + n_sets = length(flow) + println(" TMJets: $n_sets reach-sets, wall-time $(round(elapsed; digits=1)) s") + + flow_hr = overapproximate(flow, Hyperrectangle) + # Envelope at the FINAL set. + Tc_lo_env = minimum(low(set(R), 8) for R in flow_hr) + Tc_hi_env = maximum(high(set(R), 8) for R in flow_hr) + Tf_lo_env = minimum(low(set(R), 7) for R in flow_hr) + Tf_hi_env = maximum(high(set(R), 7) for R in flow_hr) + Tcold_lo = minimum(low(set(R), 9) for R in flow_hr) + Tcold_hi = maximum(high(set(R), 9) for R in flow_hr) + # Reconstruct n envelope at each step from C and T_c via PJ formula. + n_env_lo = Inf + n_env_hi = -Inf + for R in flow_hr + s = set(R) + sumLC_lo = LAM_1*low(s,1) + LAM_2*low(s,2) + LAM_3*low(s,3) + + LAM_4*low(s,4) + LAM_5*low(s,5) + LAM_6*low(s,6) + sumLC_hi = LAM_1*high(s,1) + LAM_2*high(s,2) + LAM_3*high(s,3) + + LAM_4*high(s,4) + LAM_5*high(s,5) + LAM_6*high(s,6) + # rho range: ρ = Kp*(T_ref - T_c). T_ref bounded by [T_STANDBY, T_C0], + # T_c bounded by current envelope. + rho_lo = KP_HEATUP * (T_STANDBY - high(s, 8)) + rho_hi = KP_HEATUP * (T_C0 - low(s, 8)) + denom_lo = BETA - rho_hi # smaller denom => larger n + denom_hi = BETA - rho_lo + if denom_lo > 0 + n_lo_local = LAMBDA * sumLC_lo / denom_hi + n_hi_local = LAMBDA * sumLC_hi / denom_lo + n_env_lo = min(n_env_lo, n_lo_local) + n_env_hi = max(n_env_hi, n_hi_local) + end + end + + println(" n envelope (reconstructed): [$(round(n_env_lo; sigdigits=4)), $(round(n_env_hi; sigdigits=4))]") + println(" T_f envelope: [$(round(Tf_lo_env; digits=2)), $(round(Tf_hi_env; digits=2))] °C") + println(" T_c envelope: [$(round(Tc_lo_env; digits=2)), $(round(Tc_hi_env; digits=2))] °C") + println(" T_cold envelope: [$(round(Tcold_lo; digits=2)), $(round(Tcold_hi; digits=2))] °C") + results[T_probe] = (status="OK", n_sets=n_sets, elapsed=elapsed, + Tc=(Tc_lo_env, Tc_hi_env), + Tf=(Tf_lo_env, Tf_hi_env), + Tcold=(Tcold_lo, Tcold_hi), + n=(n_env_lo, n_env_hi)) + catch err + msg = sprint(showerror, err) + println(" FAILED: ", first(msg, 300)) + results[T_probe] = (status="FAILED", err=first(msg, 300)) + break + end +end + +println("\n=== Summary ===") +for T_probe in probe_horizons + haskey(results, T_probe) || continue + r = results[T_probe] + if r.status == "OK" + println(" T = $(T_probe) s: OK, $(r.n_sets) reach-sets, $(round(r.elapsed; digits=1))s wall") + else + println(" T = $(T_probe) s: FAILED") + end +end + +# Save the longest-successful probe's envelope arrays for the app. +mat_out = joinpath(@__DIR__, "..", "..", "reachability", "reach_heatup_pj_result.mat") +saved = Dict{String, Any}() +saved["probe_horizons"] = collect(probe_horizons) +for T_probe in probe_horizons + haskey(results, T_probe) || continue + r = results[T_probe] + if r.status == "OK" + saved["T_$(Int(T_probe))_Tc_lo"] = r.Tc[1] + saved["T_$(Int(T_probe))_Tc_hi"] = r.Tc[2] + saved["T_$(Int(T_probe))_Tf_lo"] = r.Tf[1] + saved["T_$(Int(T_probe))_Tf_hi"] = r.Tf[2] + saved["T_$(Int(T_probe))_Tcold_lo"] = r.Tcold[1] + saved["T_$(Int(T_probe))_Tcold_hi"] = r.Tcold[2] + saved["T_$(Int(T_probe))_n_lo"] = r.n[1] + saved["T_$(Int(T_probe))_n_hi"] = r.n[2] + end +end +matwrite(mat_out, saved) +println("\nSaved envelope summaries to $mat_out") diff --git a/code/scripts/validate_pj.jl b/code/scripts/validate_pj.jl new file mode 100644 index 0000000..894af04 --- /dev/null +++ b/code/scripts/validate_pj.jl @@ -0,0 +1,113 @@ +#!/usr/bin/env julia +# +# validate_pj.jl — quantify the prompt-jump approximation error. +# +# Two parallel sims of the same heatup scenario: +# (i) full 10-state PKE+T/H (pke_th_rhs!) +# (ii) reduced 9-state prompt-jump model (pke_th_rhs_pj!) +# +# After the prompt transient (~few hundred microseconds) the two +# trajectories should agree closely on T_c, T_f, T_cold, and on the +# reconstructed n vs the dynamic n. Quantify the error explicitly. + +using Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +using Printf +using LinearAlgebra +using OrdinaryDiffEq +using Plots + +include(joinpath(@__DIR__, "..", "src", "pke_params.jl")) +include(joinpath(@__DIR__, "..", "src", "pke_th_rhs.jl")) +include(joinpath(@__DIR__, "..", "src", "pke_th_rhs_pj.jl")) +include(joinpath(@__DIR__, "..", "controllers", "controllers.jl")) + +plant = pke_params() +T_standby = plant.T_c0 - 33.333333 + +# Heatup scenario — same as sim_heatup.jl. +ref_heat = (; T_start=T_standby, T_target=plant.T_c0, ramp_rate=28/3600) +Q_sg = t -> 0.0 + +# Initial conditions — both at n=1e-3, same temperatures. +n0 = 1e-3 +C0 = (plant.beta_i ./ (plant.lambda_i .* plant.Lambda)) .* n0 +x0_full = [n0; C0; T_standby; T_standby; T_standby] +x0_pj = [C0; T_standby; T_standby; T_standby] + +tspan = (0.0, 3000.0) + +# --- Full-state sim --- +function rhs_full!(dx, x, p, t) + u = ctrl_heatup(t, x, plant, ref_heat) + pke_th_rhs!(dx, x, t, plant, Q_sg, u) +end +prob_full = ODEProblem(rhs_full!, x0_full, tspan) +sol_full = solve(prob_full, Rodas5(); reltol=1e-8, abstol=1e-10) + +# --- Prompt-jump sim --- +# ctrl_heatup expects 10-state x with T_f at index 8, T_c at 9. +# We pass an adapter that maps 9-state to the controller's expected layout. +function ctrl_heatup_pj(t, x_pj, plant_arg, ref_arg) + # Construct a fake 10-state with n placeholder; controller doesn't read n. + x10 = [0.0; x_pj[1:6]; x_pj[7]; x_pj[8]; x_pj[9]] + return ctrl_heatup(t, x10, plant_arg, ref_arg) +end +function rhs_pj!(dx, x, p, t) + u = ctrl_heatup_pj(t, x, plant, ref_heat) + pke_th_rhs_pj!(dx, x, t, plant, Q_sg, u) +end +prob_pj = ODEProblem(rhs_pj!, x0_pj, tspan) +sol_pj = solve(prob_pj, Rodas5(); reltol=1e-8, abstol=1e-10) + +# --- Compare at sampled times --- +function get_n_full(sol, t) sol(t)[1] end +function get_T_c_full(sol, t) sol(t)[9] end +function get_T_f_full(sol, t) sol(t)[8] end +function get_T_cold_full(sol, t) sol(t)[10] end + +function get_T_c_pj(sol, t) sol(t)[8] end +function get_T_f_pj(sol, t) sol(t)[7] end +function get_T_cold_pj(sol, t) sol(t)[9] end +function get_n_pj(sol, t) + x = sol(t) + u = ctrl_heatup_pj(t, x, plant, ref_heat) + pj_reconstruct_n(x, plant, u) +end + +println("\n=== PJ vs full-state, heatup scenario ===") +println(" t [s] n_full n_pj |Δn|/n_full T_c err T_f err T_cold err") +for t_check in (1.0, 5.0, 10.0, 60.0, 300.0, 1200.0, 3000.0) + n_f = get_n_full(sol_full, t_check) + n_p = get_n_pj(sol_pj, t_check) + Tc_err = abs(get_T_c_full(sol_full, t_check) - get_T_c_pj(sol_pj, t_check)) + Tf_err = abs(get_T_f_full(sol_full, t_check) - get_T_f_pj(sol_pj, t_check)) + Tcold_err = abs(get_T_cold_full(sol_full, t_check) - get_T_cold_pj(sol_pj, t_check)) + @printf " %6.1f %.3e %.3e %8.2e %.3e %.3e %.3e\n" t_check n_f n_p abs(n_f-n_p)/abs(n_f) Tc_err Tf_err Tcold_err +end + +# --- Plot trajectory overlay --- +figdir = joinpath(@__DIR__, "..", "..", "docs", "figures") +isdir(figdir) || mkpath(figdir) + +CtoF(T) = T*9/5 + 32 + +t_grid = range(0, 3000, length=600) +n_full_arr = [get_n_full(sol_full, t) for t in t_grid] +n_pj_arr = [get_n_pj(sol_pj, t) for t in t_grid] +Tc_full_arr = [get_T_c_full(sol_full, t) for t in t_grid] +Tc_pj_arr = [get_T_c_pj(sol_pj, t) for t in t_grid] + +p_n = plot(t_grid ./ 60, n_full_arr, lw=2, color=:blue, label="full-state", + xlabel="t [min]", ylabel="n", title="Power: full vs PJ") +plot!(p_n, t_grid ./ 60, n_pj_arr, lw=1.5, ls=:dash, color=:red, label="prompt-jump") + +p_Tc = plot(t_grid ./ 60, CtoF.(Tc_full_arr), lw=2, color=:blue, label="full-state", + xlabel="t [min]", ylabel="T_avg [F]", title="T_avg: full vs PJ") +plot!(p_Tc, t_grid ./ 60, CtoF.(Tc_pj_arr), lw=1.5, ls=:dash, color=:red, label="prompt-jump") + +fig = plot(p_n, p_Tc, layout=(1,2), size=(1200, 450), + plot_title="PJ vs full-state, heatup scenario (3000 s)") +savefig(fig, joinpath(figdir, "validate_pj_heatup.png")) +println("\nFigure: $figdir/validate_pj_heatup.png") diff --git a/code/src/pke_th_rhs_pj.jl b/code/src/pke_th_rhs_pj.jl new file mode 100644 index 0000000..1df66e8 --- /dev/null +++ b/code/src/pke_th_rhs_pj.jl @@ -0,0 +1,104 @@ +""" + pke_th_rhs_pj!(dx, x, t, plant, Q_sg, u) + +Prompt-jump (singular-perturbation) reduced-order PKE + thermal-hydraulics. + +The full PKE has prompt-neutron timescale Λ ≈ 10⁻⁴ s, while thermal +dynamics evolve on 1–100 s. For reach analysis on horizons longer +than ~10 s, the prompt transient is irrelevant to safety but its +~50 µs timescale forces TMJets to take ~ms steps, exhausting the +step budget. + +Singular-perturbation reduction: set dn/dt ≈ 0 in the neutron-balance +equation: + 0 = (ρ - β)/Λ · n + Σ λᵢ Cᵢ + → n ≈ Λ Σ λᵢ Cᵢ / (β - ρ) + +Valid when β - ρ > 0 (sub-prompt-critical), which holds for normal +operation where ρ varies on the scale of pcm vs β = 650 pcm. + +State drops from 10 to 9: + x = [C₁..C₆, T_f, T_c, T_cold] (no n; n is computed algebraically) + +The C dynamics still depend on n; we substitute the algebraic form, +giving: + dCᵢ/dt = (βᵢ/Λ) · n - λᵢ · Cᵢ + = βᵢ · Σⱼ λⱼ Cⱼ / (β - ρ) - λᵢ · Cᵢ + +This introduces a rational-function nonlinearity (1 / (β - ρ)) into +both the precursor balance and the fuel-temperature equation. Taylor +models handle it as long as the denominator stays bounded away from +zero — a non-issue under normal operation. + +Reach-analysis benefit: removes the Λ⁻¹ stiffness, so step size is +limited only by the slowest-precursor + thermal timescales. + +Soundness cost: the prompt transient (∼50 µs after a reactivity +perturbation) is no longer captured. For safety claims on hours-long +horizons this is acceptable; for prompt-supercritical excursions it is +fundamentally wrong (the algebraic form blows up as ρ → β). +""" +function pke_th_rhs_pj!(dx, x, t, plant, Q_sg, u) + # Unpack 9-state x = [C1..C6, T_f, T_c, T_cold] + C1, C2, C3, C4, C5, C6 = x[1], x[2], x[3], x[4], x[5], x[6] + T_f = x[7] + T_c = x[8] + T_cold = x[9] + T_hot = 2 * T_c - T_cold + + # Total reactivity (controller u plus T-feedback). + rho = u + plant.alpha_f * (T_f - plant.T_f0) + + plant.alpha_c * (T_c - plant.T_c0) + + # Prompt-jump algebraic n. + sum_lam_C = plant.lambda_i[1]*C1 + plant.lambda_i[2]*C2 + + plant.lambda_i[3]*C3 + plant.lambda_i[4]*C4 + + plant.lambda_i[5]*C5 + plant.lambda_i[6]*C6 + denom = plant.beta - rho + n = plant.Lambda * sum_lam_C / denom + + inv_factor = sum_lam_C / denom # used by precursor balance + + # Precursor balance under PJ. + dx[1] = plant.beta_i[1] * inv_factor - plant.lambda_i[1] * C1 + dx[2] = plant.beta_i[2] * inv_factor - plant.lambda_i[2] * C2 + dx[3] = plant.beta_i[3] * inv_factor - plant.lambda_i[3] * C3 + dx[4] = plant.beta_i[4] * inv_factor - plant.lambda_i[4] * C4 + dx[5] = plant.beta_i[5] * inv_factor - plant.lambda_i[5] * C5 + dx[6] = plant.beta_i[6] * inv_factor - plant.lambda_i[6] * C6 + + # Thermal-hydraulics (n appears algebraically in the fuel eq.). + dx[7] = (plant.P0 * n - plant.hA * (T_f - T_c)) / (plant.M_f * plant.c_f) + dx[8] = (plant.hA * (T_f - T_c) - 2 * plant.W * plant.c_c * (T_c - T_cold)) / + (plant.M_c * plant.c_c) + dx[9] = (plant.W * plant.c_c * (T_hot - T_cold) - Q_sg(t)) / + (plant.M_sg * plant.c_c) + + return nothing +end + +""" + pke_initial_conditions_pj(plant; n_target=1.0) + +Reduced-state initial condition for the prompt-jump model. Returns +the 9-element vector [C1..C6, T_f, T_c, T_cold] consistent with +n = `n_target` at full-power equilibrium. +""" +function pke_initial_conditions_pj(plant; n_target=1.0) + C0 = (plant.beta_i ./ (plant.lambda_i .* plant.Lambda)) .* n_target + return [C0; plant.T_f0; plant.T_c0; plant.T_cold0] +end + +""" + pj_reconstruct_n(x, plant, u) + +Algebraic n from the PJ reduced state. For diagnostics / plotting. +""" +function pj_reconstruct_n(x, plant, u) + T_f = x[7] + T_c = x[8] + rho = u + plant.alpha_f * (T_f - plant.T_f0) + + plant.alpha_c * (T_c - plant.T_c0) + sum_lam_C = sum(plant.lambda_i .* x[1:6]) + return plant.Lambda * sum_lam_C / (plant.beta - rho) +end diff --git a/docs/figures/validate_pj_heatup.png b/docs/figures/validate_pj_heatup.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c6459cc29082f0642b34745dad3c25c458cd47 GIT binary patch literal 60025 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 0$, i.e.\ sub-prompt-critical. For our +heatup controller $\rho = K_p \cdot e$ with $K_p e \ll \beta$, so the +denominator is well bounded away from zero. + +Substituting back into the precursor and fuel equations: +\begin{align*} +\dot C_i &= \frac{\beta_i}{\Lambda} n_{\mathrm{PJ}} - \lambda_i C_i + = \frac{\beta_i \sum_j \lambda_j C_j}{\beta - \rho} - \lambda_i C_i \\ +\dot T_f &= \frac{P_0 \, n_{\mathrm{PJ}} - hA(T_f - T_c)}{M_f c_f} + = \frac{P_0 \Lambda \sum_j \lambda_j C_j / (\beta - \rho) - hA(T_f - T_c)}{M_f c_f}. +\end{align*} + +The state vector drops from 10 to 9: $x = [C_1, \ldots, C_6, T_f, T_c, T_{\mathrm{cold}}]^\top$. +The dynamics gain a rational nonlinearity ($1/(\beta - \rho)$). The +fastest dynamic timescale becomes $1/\lambda_6 = 0.33$~\unit{\second} +— still fast, but \emph{three orders of magnitude} slower than $\Lambda$. + +\textbf{Soundness cost:} the prompt transient (the $\sim$50~\unit{\micro\second} +adjustment of $n$ after a step in $\rho$) is no longer captured. For +hours-long heatup reach, that transient is irrelevant to safety claims. +For prompt-supercritical regimes ($\rho \to \beta$) the algebraic +formula diverges and the reduction is invalid — but those regimes are +themselves accident-class, outside the scope of normal-operation reach. +\end{derivation} + +\subsection*{Part 2: Implementation} + +Two new files in \texttt{code/}: + +\begin{itemize} + \item \texttt{src/pke\_th\_rhs\_pj.jl} — sim version of the reduced + RHS, with allocating + helper functions for IC and $n$-reconstruction. + \item \texttt{scripts/validate\_pj.jl} — side-by-side sim of full + vs.\ reduced PKE on the heatup scenario. +\end{itemize} + +The reduced RHS is structurally identical to the full one with two +differences: (a) no $\dot n$ equation; (b) $n$ inside the precursor and +fuel-temperature equations is replaced by $n_{\mathrm{PJ}}(C, \rho)$, +introducing the rational denominator. + +\subsection*{Part 3: Validation against full-state} + +\texttt{validate\_pj.jl} runs both models from the same heatup IC +($n_0 = 10^{-3}$, $T = T_{\mathrm{standby}}$ everywhere) for 50 minutes +and tabulates pointwise error. + +\begin{lstlisting}[style=terminal] +=== PJ vs full-state, heatup scenario === + t [s] n_full n_pj |Δn|/n_full T_c err T_f err T_cold err + 1.0 1.000e-03 1.000e-03 8.32e-07 4.839e-09 1.718e-08 6.642e-10 + 5.0 1.000e-03 1.000e-03 3.08e-06 3.970e-08 9.392e-08 1.921e-08 + 10.0 1.001e-03 1.001e-03 5.59e-06 1.295e-07 2.320e-07 7.945e-08 + 60.0 1.017e-03 1.018e-03 3.70e-05 3.826e-06 4.534e-06 3.446e-06 + 300.0 1.310e-03 1.311e-03 3.77e-04 1.867e-04 1.960e-04 1.816e-04 + 1200.0 3.414e-03 3.410e-03 1.02e-03 2.177e-03 2.111e-03 2.213e-03 + 3000.0 3.248e-03 3.250e-03 5.03e-04 7.166e-03 7.197e-03 7.149e-03 +\end{lstlisting} + +\textbf{Maximum relative error on $n$ over 3000~\unit{\second}: 0.10\%} +(at $t = 1200$~\unit{\second}). Maximum temperature error: 7~\unit{\milli\kelvin}. +The PJ approximation is excellent — the absolute errors are far below +any physical safety margin. + +The PJ trajectory is essentially indistinguishable from full-state on +the heatup timescale (\cref{fig:validate-pj}). + +\begin{figure}[h] + \centering + \includegraphics[width=0.95\linewidth]{validate_pj_heatup.png} + \caption{Full-state (blue) vs.\ prompt-jump (red dashed) sims of the + same heatup scenario. Power $n$ (left) and $T_{\mathrm{avg}}$ + (right) overlay almost perfectly across 50~\unit{\minute}. The + difference is invisible at this scale — peak relative error on $n$ + is 0.1\%. This is the empirical evidence that the singular-perturbation + reduction is sound for this class of slow heatup transients.} + \label{fig:validate-pj} +\end{figure} + +\subsection*{Part 4: Nonlinear reach with the PJ model} + +\apass{Results are populating as TMJets runs in the background. Final +horizon and step counts will be filled in here once the longest probe +returns. Initial expectation: 60~\unit{\second} should pass trivially; +1800~\unit{\second} is the real test; 5400 / 18000 the dream.} + +The PJ reach script is \texttt{code/scripts/reach\_heatup\_pj.jl}. +Same Taylor-model machinery (\texttt{TMJets}, \texttt{@taylorize}, +augmented time state) as the failed full-state version, but the RHS +operates on the 9-state PJ system (10D with augmented time) and +includes the rational $1/(\beta - \rho)$ in two places. Probe +horizons: 60, 300, 1800, 5400~\unit{\second}. + +\begin{decision} +TMJets settings: \texttt{orderT=4}, \texttt{orderQ=2}, \texttt{abstol=1e-9}, +\texttt{maxsteps=100\,000}. \texttt{abstol} is one order looser than +the full-state attempt — the PJ RHS has a rational nonlinearity that +narrows the Taylor remainder convergence radius slightly, and we don't +need 1e-10 precision for envelope tracking on a tube that's already +several Kelvin wide. +\end{decision} + +\apass{Results section will be populated below as probes complete. +Currently TMJets is precompiling (~5--15 minutes for the new +\texttt{@taylorize}'d RHS).} + +\subsection*{Part 5: App buildout} + +While the reach is running, extended the Pluto predicate explorer +with three new sections: +\begin{itemize} + \item \textbf{Live reach-result ingestion} (§9b): reads + \texttt{reachability/reach\_operation\_result.mat} (saved by + \texttt{reach\_operation.jl}) and renders per-halfspace margins + live, replacing the hand-maintained traceability table. + \item \textbf{2D projection chooser} (§9c): pick any two state + coordinates from $\{n, T_f, T_c, T_{\mathrm{cold}}\}$ and see + the operating polytope with the reach-tube envelope as a red + rectangle overlay. + \item \textbf{PJ heatup reach overlay} (§9d): if \texttt{reach\_heatup\_pj\_result.mat} + exists, display the envelope summary. +\end{itemize} + +Added \texttt{MAT.jl} to the app's \texttt{Project.toml}. Read-only +v1 still — sliders preview UX without writing back. + +\subsection*{Soundness ledger update} + +\begin{decision} +The PJ reduction shifts the soundness story: + +\textbf{Before:} linear reach was a sound over-approximation of the +linearized closed-loop, but the linearization was an unbounded +approximation of the nonlinear plant. Net: \emph{approximate, not +sound} for the plant. + +\textbf{After:} TMJets nonlinear reach with PJ is a sound +over-approximation of the \emph{prompt-jump-reduced} nonlinear plant. +The PJ reduction itself introduces a controlled approximation +(0.1\% error on $n$, mK on $T$, validated empirically over 50 +minutes). Net: \emph{$\epsilon_{\mathrm{PJ}}$-approximate but otherwise +sound}, where $\epsilon_{\mathrm{PJ}}$ is bounded. + +This is qualitatively better. The remaining gap (PJ approximation +error) can be characterized by the validation experiment, which we have. +The next step toward full soundness would be a Tikhonov-style +singular-perturbation theorem application giving a closed-form +$\mathcal{O}(\Lambda)$ error bound, but the empirical bound is +defensible for the prelim demo. +\end{decision} + +\subsection*{Open at close} + +\apass{This entry is being written in parallel with the running +reach. Final results to be filled in below as TMJets returns. If +TMJets completes the 5-hour horizon, the heatup reach-avoid obligation +is discharged (modulo PJ + saturation caveats). If it stops earlier, +identify the new wall and propose the next reduction.} + +\begin{itemize} + \item Polytopic / SOS barriers — still the only path to a tight + analytic certificate; quadratic Lyapunov is structurally + defeated regardless of model order. + \item Saturation as explicit hybrid sub-mode — still pending, + independent of PJ. + \item Parametric $\alpha$ uncertainty — still pending. + \item Tikhonov / regular-perturbation $\mathcal{O}(\Lambda)$ error + bound on PJ. + \item Per-mode reach for shutdown and scram (now feasible with PJ). +\end{itemize} diff --git a/journal/journal.tex b/journal/journal.tex index 6a19ba0..bdb0c36 100644 --- a/journal/journal.tex +++ b/journal/journal.tex @@ -72,5 +72,7 @@ Each limitation ties to a plan or an open question. \input{entries/2026-04-20-predicates-boundaries-julia-nonlinear.tex} \newpage \input{entries/2026-04-20-evening-mega-session.tex} +\newpage +\input{entries/2026-04-20-overnight-prompt-jump.tex} \end{document}