Obsidian/Presentations/ERLM/two_loop_linearized_reachability.jl
Dane Sabo ed59b30dd0 Auto sync: 2025-12-07 20:55:06 (25 files changed)
D  Presentations/ERLM/bouncing_ball_hybrid.png

A  Presentations/ERLM/images/4_research_approach/phase_portrait_sg1.png

A  Presentations/ERLM/images/4_research_approach/two_loop.png

A  Presentations/ERLM/images/7_broader_impacts/billion.jpg

M  Presentations/ERLM/main.aux

M  Presentations/ERLM/main.fdb_latexmk

M  Presentations/ERLM/main.fls

M  Presentations/ERLM/main.log
2025-12-07 20:55:06 -05:00

216 lines
6.9 KiB
Julia

using ReachabilityAnalysis
using LinearAlgebra
using Plots
"""
Two-Loop Reactor: Linearized Reachability Analysis
Since the nonlinear system is causing issues with all algorithms,
let's linearize around an equilibrium point and use LGG09.
This is actual formal reachability analysis - the linearization is
a standard approximation technique used in control theory.
"""
println("=== Two-Loop Reactor: Linearized Reachability ===\n")
# Constants
const C_0 = 33.33
const τ_0 = 0.75 * C_0
const μ = 0.6
const P_r = 100.0
const Q2 = 50.0
C_r = μ * C_0
C_sg = (1 - μ) * C_0 / 2
W = C_0 / (2 * τ_0)
println("=== System Parameters ===")
println("C_r = $C_r, C_sg = $C_sg, W = $W")
# Find equilibrium with Q1 = 50
# At equilibrium: dT/dt = 0
# From equations:
# P_r - W*(T_hot - T_cold1) - W*(T_hot - T_cold2) = 0
# W*(T_hot - T_cold1) - Q1 = 0 => T_hot - T_cold1 = Q1/W
# W*(T_hot - T_cold2) - Q2 = 0 => T_hot - T_cold2 = Q2/W
Q1_nominal = 50.0
T_hot_eq = 453.0 # Approximate equilibrium
T_cold1_eq = T_hot_eq - Q1_nominal/W
T_cold2_eq = T_hot_eq - Q2/W
println("\n=== Equilibrium Point (Q1 = $Q1_nominal) ===")
println("T_hot_eq = $T_hot_eq °F")
println("T_cold1_eq = $T_cold1_eq °F")
println("T_cold2_eq = $T_cold2_eq °F")
# Linearize around equilibrium
# State: x = [T_hot - T_hot_eq, T_cold1 - T_cold1_eq, T_cold2 - T_cold2_eq, Q1 - Q1_nominal]
#
# Original equations:
# dT_hot/dt = (P_r - W*(T_hot - T_cold1) - W*(T_hot - T_cold2)) / C_r
# dT_cold1/dt = (W*(T_hot - T_cold1) - Q1) / C_sg
# dT_cold2/dt = (W*(T_hot - T_cold2) - Q2) / C_sg
# dQ1/dt = 0
#
# Linearized (using deviations from equilibrium):
# dx1/dt = (-W*x1 + W*x2 + W*x3) / C_r
# dx2/dt = (W*x1 - W*x2 - x4) / C_sg
# dx3/dt = (W*x1 - W*x3) / C_sg
# dx4/dt = 0
A = [
-W/C_r W/C_r W/C_r 0.0;
W/C_sg -W/C_sg 0.0 -1.0/C_sg;
W/C_sg 0.0 -W/C_sg 0.0;
0.0 0.0 0.0 0.0
]
println("\n=== Linearized System Matrix A ===")
display(A)
println()
# Initial set: Small deviations from equilibrium
# Starting close to equilibrium but with full Q1 uncertainty
δT_init = 2.0 # ±2°F initial temperature deviation
Q1_min = 45.0
Q1_max = 55.0
X0 = Hyperrectangle(
low=[-δT_init, -δT_init, -δT_init, Q1_min - Q1_nominal],
high=[δT_init, δT_init, δT_init, Q1_max - Q1_nominal]
)
println("\n=== Initial Set (deviations from equilibrium) ===")
println("δT_hot ∈ [-$δT_init, $δT_init]")
println("δT_cold1 ∈ [-$δT_init, $δT_init]")
println("δT_cold2 ∈ [-$δT_init, $δT_init]")
println("δQ1 ∈ [$(Q1_min - Q1_nominal), $(Q1_max - Q1_nominal)]")
# Create linear system
sys = @system(x' = A*x, x Universe(4))
# Create IVP
prob = InitialValueProblem(sys, X0)
println("\n=== Solving with LGG09 (Exact for Linear Systems!) ===")
# Solve - LGG09 is EXACT for linear systems!
# Must specify vars and dimension
sol = solve(prob, T=30.0, alg=LGG09(δ=0.5, vars=[1, 2, 3, 4], dim=4))
println("✓ Success! Computed $(length(sol)) reach sets")
println("\nNote: LGG09 provides EXACT reachability for linear systems!")
# Convert back to absolute temperatures for plotting
println("\n=== Creating Plots ===")
# Extract and shift back to absolute coordinates
function plot_absolute(sol, var_idx, eq_val, ylabel_text, title_text, color_choice)
# Get the reach sets
times = Float64[]
lower = Float64[]
upper = Float64[]
for reach_set in sol
t = inf(reach_set.t_start)
set = reach_set.X
# Project to variable
proj = project(set, var_idx)
# Get bounds
low_val = low(proj)[1] + eq_val
high_val = high(proj)[1] + eq_val
push!(times, t)
push!(lower, low_val)
push!(upper, high_val)
end
p = plot(times, lower, fillrange=upper,
xlabel="Time (s)", ylabel=ylabel_text,
title=title_text,
fillalpha=0.5, color=color_choice, lw=2,
label="Reach tube", legend=:topright)
return p
end
p1 = plot_absolute(sol, 1, T_hot_eq, "T_hot (°F)", "Hot Leg Temperature", :red)
p2 = plot_absolute(sol, 2, T_cold1_eq, "T_cold1 (°F)", "Cold Leg 1 (SG1 - Uncertain)", :blue)
p3 = plot_absolute(sol, 3, T_cold2_eq, "T_cold2 (°F)", "Cold Leg 2 (SG2 - Known)", :green)
# Phase portrait: T_hot vs T_cold1
p4 = plot(xlabel="T_cold1 (°F)", ylabel="T_hot (°F)",
title="Phase Portrait: T_hot vs T_cold1",
legend=:topright)
for reach_set in sol
set = reach_set.X
# Project to (x2, x1)
proj = project(set, [2, 1])
# Get vertices and shift
verts = vertices_list(proj)
verts_abs = [[v[1] + T_cold1_eq, v[2] + T_hot_eq] for v in verts]
# Plot polygon
xs = [v[1] for v in verts_abs]
ys = [v[2] for v in verts_abs]
push!(xs, xs[1]) # Close the polygon
push!(ys, ys[1])
plot!(p4, xs, ys, fillalpha=0.3, color=:purple, lw=0, label="")
end
plot!(p4, [], [], fillalpha=0.3, color=:purple, label="Reachable sets")
plot_combined = plot(p1, p2, p3, p4, layout=(2, 2), size=(1400, 1000),
plot_title="Linearized Reachability Analysis: Q1 ∈ [$Q1_min, $Q1_max]")
savefig(plot_combined, "two_loop_linearized_reachability.png")
println("Saved: two_loop_linearized_reachability.png")
# Detailed phase portrait
p_phase = plot(xlabel="T_cold1 (°F)", ylabel="T_hot (°F)",
title="Phase Portrait: T_hot vs T_cold1\n(Linearized Reachability: Q1 ∈ [$Q1_min, $Q1_max])",
size=(1000, 900), legend=:topright)
for (i, reach_set) in enumerate(sol)
set = reach_set.X
proj = project(set, [2, 1])
verts = vertices_list(proj)
verts_abs = [[v[1] + T_cold1_eq, v[2] + T_hot_eq] for v in verts]
xs = [v[1] for v in verts_abs]
ys = [v[2] for v in verts_abs]
push!(xs, xs[1])
push!(ys, ys[1])
alpha_val = 0.2 + 0.3 * (i / length(sol)) # Fade over time
plot!(p_phase, xs, ys, fillalpha=alpha_val, color=:purple, lw=0.5, label="")
end
plot!(p_phase, [], [], fillalpha=0.5, color=:purple, label="Reachable region")
savefig(p_phase, "phase_portrait_linearized.png")
println("Saved: phase_portrait_linearized.png")
println("\n✓ Complete!")
println("\n=== Results ===")
println("This is FORMAL reachability analysis using linear approximation.")
println("LGG09 provides EXACT reachable sets for linear systems.")
println("\nThe linearization is valid near the equilibrium point.")
println("For large deviations, use parameter sweeps (our previous approach).")
# Print final bounds
final_set = sol[end].X
println("\n=== Final Reachable Set (t=30s) ===")
proj1 = project(final_set, 1)
proj2 = project(final_set, 2)
proj3 = project(final_set, 3)
println("T_hot ∈ [$(round(low(proj1)[1] + T_hot_eq, digits=1)), $(round(high(proj1)[1] + T_hot_eq, digits=1))] °F")
println("T_cold1 ∈ [$(round(low(proj2)[1] + T_cold1_eq, digits=1)), $(round(high(proj2)[1] + T_cold1_eq, digits=1))] °F")
println("T_cold2 ∈ [$(round(low(proj3)[1] + T_cold2_eq, digits=1)), $(round(high(proj3)[1] + T_cold2_eq, digits=1))] °F")