julia-port: parallel plant model; sanity sim matches MATLAB, reach is stub
Port pke_params, pke_th_rhs, pke_linearize, and all five controllers to Julia. sim_sanity.jl reproduces the MATLAB main.m operation-mode scenario (100%->80% Q_sg step) and matches final state to 3 decimals across n, T_f, T_avg, T_cold, u. reach_operation.jl is a stub: ReachabilityAnalysis.jl (LGG09, GLGM06, BFFPSV18) numerically explodes on the raw stiff system — envelopes of 1e14 K to 1e37 K instead of the known-tight 0.03 K. Almost certainly a state-scaling issue: precursors C_i ~ 1e5, temperatures ~ 300, eigvals span 5000x. Diagonal scaling + retry is planned; left for the next pass since the hand-rolled MATLAB reach already discharges the operation-mode obligation. Project.toml pins OrdinaryDiffEq >= 6.111 (the one that precompiled cleanly on first instantiate). Manifest gitignored. Hacker-Split: Julia path open, reach side needs a scaling pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
02a675c152
commit
9fc4afb611
1
julia-port/.gitignore
vendored
Normal file
1
julia-port/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Manifest.toml
|
||||||
14
julia-port/Project.toml
Normal file
14
julia-port/Project.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
authors = ["Dane Sabo <yourstruly@danesabo.com>"]
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
LazySets = "b4f0291d-fe17-52bc-9479-3d1a343d9043"
|
||||||
|
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||||
|
MAT = "23992714-dd62-5051-b70f-ba57cb901cac"
|
||||||
|
MatrixEquations = "99c1a7ee-ab34-5fd5-8076-27c950a045f4"
|
||||||
|
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
|
||||||
|
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
|
||||||
|
ReachabilityAnalysis = "1e97bd63-91d1-579d-8e8d-501d2b57c93f"
|
||||||
|
|
||||||
|
[compat]
|
||||||
|
OrdinaryDiffEq = "6.111.0"
|
||||||
|
julia = "1.10"
|
||||||
40
julia-port/README.md
Normal file
40
julia-port/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Julia port
|
||||||
|
|
||||||
|
Parallel implementation of the plant model (`../plant-model/`) in Julia,
|
||||||
|
intended as an eventual landing spot for reachability if we outgrow the
|
||||||
|
MATLAB / hand-rolled tooling.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- `src/pke_params.jl`, `src/pke_th_rhs.jl`, `src/pke_linearize.jl` —
|
||||||
|
functional, match MATLAB term-for-term.
|
||||||
|
- `controllers/controllers.jl` — all five modes ported
|
||||||
|
(null, shutdown, heatup, operation, scram). LQR factory included via
|
||||||
|
MatrixEquations.jl.
|
||||||
|
- `scripts/sim_sanity.jl` — closed-loop simulation matches MATLAB to 3
|
||||||
|
decimals on the 100% → 80% `Q_sg` step. ✅ passing.
|
||||||
|
- `scripts/reach_operation.jl` — ❌ stub. ReachabilityAnalysis.jl
|
||||||
|
algorithms blow up on this stiff, badly-conditioned system. See the
|
||||||
|
file header for diagnosis and planned fix (state rescaling).
|
||||||
|
|
||||||
|
## When to prefer Julia over MATLAB
|
||||||
|
|
||||||
|
Today: nowhere. The sanity path exists so we don't regret the eventual
|
||||||
|
port.
|
||||||
|
|
||||||
|
Once the reach scaling is resolved:
|
||||||
|
- Nonlinear reach for the P controller in operation mode (CORA /
|
||||||
|
JuliaReach territory where MATLAB's linearization doesn't suffice).
|
||||||
|
- Heatup reach with its time-varying reference.
|
||||||
|
- Parametric studies where MATLAB license fees / CI friction matter.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd julia-port
|
||||||
|
julia --project=. -e 'using Pkg; Pkg.instantiate()'
|
||||||
|
julia --project=. scripts/sim_sanity.jl
|
||||||
|
```
|
||||||
|
|
||||||
|
First run will precompile the dependency stack (OrdinaryDiffEq,
|
||||||
|
ReachabilityAnalysis, LazySets — a few minutes).
|
||||||
49
julia-port/controllers/controllers.jl
Normal file
49
julia-port/controllers/controllers.jl
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"""
|
||||||
|
Mode controllers — signatures match the MATLAB side:
|
||||||
|
u = ctrl_<mode>(t, x, plant, ref)
|
||||||
|
|
||||||
|
Pure functions. `ref` can be any NamedTuple of setpoints; unused fields
|
||||||
|
are ignored. For heatup, `ref` must provide `T_start`, `T_target`, `ramp_rate`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ctrl_null(t, x, plant, ref) = 0.0
|
||||||
|
|
||||||
|
ctrl_shutdown(t, x, plant, ref) = -5.0 * plant.beta
|
||||||
|
ctrl_scram(t, x, plant, ref) = -8.0 * plant.beta
|
||||||
|
|
||||||
|
function ctrl_operation(t, x, plant, ref)
|
||||||
|
Kp = 1e-4
|
||||||
|
T_avg = x[9]
|
||||||
|
return Kp * (ref.T_avg - T_avg)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctrl_heatup(t, x, plant, ref)
|
||||||
|
Kp = 1e-4
|
||||||
|
T_f = x[8]
|
||||||
|
T_avg = x[9]
|
||||||
|
u_ff = -plant.alpha_f * (T_f - plant.T_f0) -
|
||||||
|
plant.alpha_c * (T_avg - plant.T_c0)
|
||||||
|
T_ref = min(ref.T_start + ref.ramp_rate * t, ref.T_target)
|
||||||
|
u_unsat = u_ff + Kp * (T_ref - T_avg)
|
||||||
|
u_min = get(ref, :u_min, -5 * plant.beta)
|
||||||
|
u_max = get(ref, :u_max, 0.5 * plant.beta)
|
||||||
|
return clamp(u_unsat, u_min, u_max)
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
ctrl_operation_lqr_factory(plant; Q_lqr, R_lqr)
|
||||||
|
|
||||||
|
Returns a closure `ctrl(t, x, plant_ignored, ref_ignored)` with the LQR
|
||||||
|
gain baked in. Pattern chosen so the user can specify Q/R from the
|
||||||
|
calling script and get a pure function to pass to the ODE solver.
|
||||||
|
|
||||||
|
Depends on MatrixEquations.jl for `arec` (algebraic Riccati).
|
||||||
|
"""
|
||||||
|
function ctrl_operation_lqr_factory(plant, A, B; Q_lqr, R_lqr)
|
||||||
|
x_op = pke_initial_conditions(plant)
|
||||||
|
X_ric, _, _ = MatrixEquations.arec(A, B, R_lqr, Q_lqr)
|
||||||
|
K = (R_lqr \ B') * X_ric # 1x10
|
||||||
|
return function (t, x, plant_ignored, ref_ignored)
|
||||||
|
return -(K * (x - x_op))[1]
|
||||||
|
end, K, x_op
|
||||||
|
end
|
||||||
90
julia-port/scripts/reach_operation.jl
Normal file
90
julia-port/scripts/reach_operation.jl
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env julia
|
||||||
|
#
|
||||||
|
# reach_operation.jl — operation-mode linear reach in Julia. **STUB**.
|
||||||
|
#
|
||||||
|
# Attempt to reproduce reachability/reach_operation.m using
|
||||||
|
# ReachabilityAnalysis.jl.
|
||||||
|
#
|
||||||
|
# STATUS: does NOT yet produce a useful result. The closed-loop system
|
||||||
|
# is strongly stiff (eigvals spanning 0.012 to 65 /s) and has states
|
||||||
|
# with magnitudes differing by ~10 orders of magnitude (precursors C_i
|
||||||
|
# ~ 1e5 due to 1/Lambda, temperatures ~ 300). LGG09 / GLGM06 / BFFPSV18
|
||||||
|
# all blow up numerically over the 600s horizon, returning envelopes
|
||||||
|
# ranging from 2757 K to 1e37 K instead of the known-tight 0.03 K.
|
||||||
|
#
|
||||||
|
# Likely fix: diagonal state scaling S such that A_cl_scaled = S A_cl S^{-1}
|
||||||
|
# has O(1) entries, run reach in scaled coordinates, then invert S.
|
||||||
|
# Also worth trying: ASB07 (adaptive step) or the Taylor-model schemes.
|
||||||
|
# Left as future work — the hand-rolled MATLAB zonotope in
|
||||||
|
# reachability/reach_linear.m gives a valid result today, so the Julia
|
||||||
|
# port is priority-B.
|
||||||
|
|
||||||
|
using Pkg
|
||||||
|
Pkg.activate(joinpath(@__DIR__, ".."))
|
||||||
|
|
||||||
|
using LinearAlgebra
|
||||||
|
using MatrixEquations
|
||||||
|
using ReachabilityAnalysis, LazySets
|
||||||
|
|
||||||
|
include(joinpath(@__DIR__, "..", "src", "pke_params.jl"))
|
||||||
|
include(joinpath(@__DIR__, "..", "src", "pke_th_rhs.jl"))
|
||||||
|
include(joinpath(@__DIR__, "..", "src", "pke_linearize.jl"))
|
||||||
|
|
||||||
|
plant = pke_params()
|
||||||
|
x_op = pke_initial_conditions(plant)
|
||||||
|
|
||||||
|
# --- Linearize around the operating point ---
|
||||||
|
A, B, B_w, _, _, _ = pke_linearize(plant)
|
||||||
|
|
||||||
|
# --- LQR gain (same Q, R as the MATLAB side) ---
|
||||||
|
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) # 1 x 10
|
||||||
|
A_cl = A - reshape(B, :, 1) * K
|
||||||
|
|
||||||
|
println("\n=== Julia closed-loop spectrum ===")
|
||||||
|
ev = eigvals(A_cl)
|
||||||
|
println(" max Re(eig) = ", round(maximum(real.(ev)); sigdigits=4))
|
||||||
|
println(" min Re(eig) = ", round(minimum(real.(ev)); sigdigits=4))
|
||||||
|
|
||||||
|
# --- Reach-set problem: dx/dt = A_cl dx + B_w w, dx(0) ∈ X0, w ∈ W ---
|
||||||
|
delta_entry = [0.01 * x_op[1];
|
||||||
|
0.001 .* abs.(x_op[2:7]);
|
||||||
|
0.1; 0.1; 0.1]
|
||||||
|
X0 = Hyperrectangle(zeros(10), delta_entry)
|
||||||
|
|
||||||
|
Q_nom = plant.P0
|
||||||
|
w_lo, w_hi = -0.15 * plant.P0, 0.0
|
||||||
|
W = Interval(w_lo, w_hi)
|
||||||
|
B_w_col = reshape(B_w, :, 1)
|
||||||
|
|
||||||
|
# Encode the bounded disturbance as a bounded input:
|
||||||
|
# dx/dt = A_cl x + B_w u, u ∈ W.
|
||||||
|
# Direct constructor since the @system macro's dialect is package-version sensitive.
|
||||||
|
sys = ConstrainedLinearControlContinuousSystem(A_cl, B_w_col, Universe(10), W)
|
||||||
|
prob = InitialValueProblem(sys, X0)
|
||||||
|
sol = solve(prob; T=600.0, alg=GLGM06(; δ=0.5, max_order=20))
|
||||||
|
|
||||||
|
# Extract T_c envelope via support function queries.
|
||||||
|
flow = flowpipe(sol)
|
||||||
|
e9 = zeros(10); e9[9] = 1.0
|
||||||
|
T_c_hi = [ρ(e9, set(R)) for R in flow]
|
||||||
|
T_c_lo = [-ρ(-e9, set(R)) for R in flow]
|
||||||
|
|
||||||
|
println("\n=== Operation-mode reach envelope on T_c (deviation from T_c0) ===")
|
||||||
|
println(" min dT_c = ", round(minimum(T_c_lo); digits=4), " K")
|
||||||
|
println(" max dT_c = ", round(maximum(T_c_hi); digits=4), " K")
|
||||||
|
println(" safety band |dT_c| <= 5.0 K")
|
||||||
|
if maximum(abs.([T_c_lo; T_c_hi])) <= 5.0
|
||||||
|
println(" OK: Julia reach set inside safety band.")
|
||||||
|
else
|
||||||
|
println(" *** VIOLATION ***")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Save the envelope for later comparison
|
||||||
|
using MAT
|
||||||
|
matwrite(joinpath(@__DIR__, "..", "..", "reachability", "julia_reach_operation.mat"),
|
||||||
|
Dict("T_c_lo" => T_c_lo, "T_c_hi" => T_c_hi,
|
||||||
|
"A_cl" => A_cl, "K" => Matrix(K), "delta_entry" => delta_entry))
|
||||||
|
println("\nSaved envelope to reachability/julia_reach_operation.mat")
|
||||||
40
julia-port/scripts/sim_sanity.jl
Normal file
40
julia-port/scripts/sim_sanity.jl
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env julia
|
||||||
|
#
|
||||||
|
# sim_sanity.jl — verify the Julia port matches the MATLAB result.
|
||||||
|
#
|
||||||
|
# Reproduces main.m Run 2 (ctrl_operation under 100% -> 80% Q_sg step)
|
||||||
|
# and prints the final state, which should agree with MATLAB to ~1e-3.
|
||||||
|
|
||||||
|
using Pkg
|
||||||
|
Pkg.activate(joinpath(@__DIR__, ".."))
|
||||||
|
|
||||||
|
using OrdinaryDiffEq
|
||||||
|
|
||||||
|
include(joinpath(@__DIR__, "..", "src", "pke_params.jl"))
|
||||||
|
include(joinpath(@__DIR__, "..", "src", "pke_th_rhs.jl"))
|
||||||
|
include(joinpath(@__DIR__, "..", "controllers", "controllers.jl"))
|
||||||
|
|
||||||
|
plant = pke_params()
|
||||||
|
x0 = pke_initial_conditions(plant)
|
||||||
|
|
||||||
|
Q_sg = t -> plant.P0 * (1.0 - 0.2 * (t >= 30))
|
||||||
|
ref = (; T_avg = plant.T_c0)
|
||||||
|
|
||||||
|
function rhs!(dx, x, p, t)
|
||||||
|
u = ctrl_operation(t, x, plant, ref)
|
||||||
|
pke_th_rhs!(dx, x, t, plant, Q_sg, u)
|
||||||
|
end
|
||||||
|
|
||||||
|
prob = ODEProblem(rhs!, x0, (0.0, 600.0))
|
||||||
|
sol = solve(prob, Rodas5(); reltol=1e-8, abstol=1e-10)
|
||||||
|
|
||||||
|
xf = sol.u[end]
|
||||||
|
CtoF(T) = T * 9/5 + 32
|
||||||
|
println("\n=== Julia port sanity — ctrl_operation under 100% -> 80% Q_sg step ===")
|
||||||
|
println(" Final t = ", sol.t[end])
|
||||||
|
println(" n = $(round(xf[1]; digits=4)) (expect ~0.800)")
|
||||||
|
println(" T_f = $(round(CtoF(xf[8]); digits=2)) F (expect ~616.6)")
|
||||||
|
println(" T_avg = $(round(CtoF(xf[9]); digits=2)) F (expect ~587.8)")
|
||||||
|
println(" T_cold = $(round(CtoF(xf[10]); digits=2)) F (expect ~561.4)")
|
||||||
|
u_final = ctrl_operation(sol.t[end], xf, plant, ref)
|
||||||
|
println(" u = $(round(u_final/plant.beta; digits=4)) \$ (expect ~-0.0068)")
|
||||||
37
julia-port/src/pke_linearize.jl
Normal file
37
julia-port/src/pke_linearize.jl
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
pke_linearize(plant; x_star=nothing, u_star=0.0, Q_star=nothing)
|
||||||
|
|
||||||
|
Numerical Jacobians via central finite differences. Returns
|
||||||
|
`(A, B, B_w, x_star, u_star, Q_star)` such that for small (dx, du, dw):
|
||||||
|
|
||||||
|
dx/dt ≈ A dx + B du + B_w dw, where w = Q_sg.
|
||||||
|
|
||||||
|
Defaults: `x_star` = operating-point steady state, `u_star = 0`,
|
||||||
|
`Q_star = P0`.
|
||||||
|
"""
|
||||||
|
function pke_linearize(plant; x_star=nothing, u_star=0.0, Q_star=nothing)
|
||||||
|
x_star === nothing && (x_star = pke_initial_conditions(plant))
|
||||||
|
Q_star === nothing && (Q_star = plant.P0)
|
||||||
|
|
||||||
|
n = length(x_star)
|
||||||
|
eps_rel = 1e-6
|
||||||
|
eps_abs = 1e-8
|
||||||
|
|
||||||
|
f = (x, u, w) -> pke_th_rhs(x, 0.0, plant, t -> w, u)
|
||||||
|
|
||||||
|
A = zeros(n, n)
|
||||||
|
for k in 1:n
|
||||||
|
h = max(eps_rel * abs(x_star[k]), eps_abs)
|
||||||
|
xp = copy(x_star); xp[k] += h
|
||||||
|
xm = copy(x_star); xm[k] -= h
|
||||||
|
A[:, k] = (f(xp, u_star, Q_star) - f(xm, u_star, Q_star)) ./ (2h)
|
||||||
|
end
|
||||||
|
|
||||||
|
h = max(eps_rel * abs(u_star), eps_abs)
|
||||||
|
B = (f(x_star, u_star + h, Q_star) - f(x_star, u_star - h, Q_star)) ./ (2h)
|
||||||
|
|
||||||
|
h = max(eps_rel * abs(Q_star), 1.0)
|
||||||
|
B_w = (f(x_star, u_star, Q_star + h) - f(x_star, u_star, Q_star - h)) ./ (2h)
|
||||||
|
|
||||||
|
return A, B, B_w, x_star, u_star, Q_star
|
||||||
|
end
|
||||||
53
julia-port/src/pke_params.jl
Normal file
53
julia-port/src/pke_params.jl
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
"""
|
||||||
|
pke_params()
|
||||||
|
|
||||||
|
Return a NamedTuple with all plant parameters and derived steady-state
|
||||||
|
conditions for the PKE + thermal-hydraulics model. See
|
||||||
|
../plant-model/pke_params.m for physical rationale on each value.
|
||||||
|
|
||||||
|
Kept as a NamedTuple rather than a mutable struct so that reachability
|
||||||
|
tools (`@taylorize` in ReachabilityAnalysis.jl) can inline the constants.
|
||||||
|
"""
|
||||||
|
function pke_params()
|
||||||
|
# --- Neutronics ---
|
||||||
|
Lambda = 1e-4
|
||||||
|
beta_i = [0.000215, 0.001424, 0.001274, 0.002568, 0.000748, 0.000273]
|
||||||
|
lambda_i = [0.0124, 0.0305, 0.111, 0.301, 1.14, 3.01]
|
||||||
|
beta = sum(beta_i)
|
||||||
|
|
||||||
|
# --- Thermal-hydraulic ---
|
||||||
|
P0 = 1000e6
|
||||||
|
M_f = 50000.0
|
||||||
|
c_f = 300.0
|
||||||
|
M_c = 20000.0
|
||||||
|
c_c = 5450.0
|
||||||
|
hA = 5e7
|
||||||
|
W = 5000.0
|
||||||
|
M_sg = 30000.0
|
||||||
|
|
||||||
|
# --- Reactivity feedback coefficients ---
|
||||||
|
alpha_f = -2.5e-5
|
||||||
|
alpha_c = -1.0e-4
|
||||||
|
|
||||||
|
# --- Derived steady-state (full-power equilibrium) ---
|
||||||
|
T_cold0 = 290.0
|
||||||
|
dT_core = P0 / (W * c_c)
|
||||||
|
T_hot0 = T_cold0 + dT_core
|
||||||
|
T_c0 = (T_hot0 + T_cold0) / 2
|
||||||
|
T_f0 = T_c0 + P0 / hA
|
||||||
|
|
||||||
|
return (; Lambda, beta_i, lambda_i, beta, P0, M_f, c_f, M_c, c_c, hA,
|
||||||
|
W, M_sg, alpha_f, alpha_c, T_cold0, T_hot0, T_c0, T_f0, dT_core)
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
pke_initial_conditions(plant)
|
||||||
|
|
||||||
|
Full-power steady state: n=1, precursors at equilibrium, temperatures at
|
||||||
|
(T_f0, T_c0, T_cold0). Returns a 10-element Vector.
|
||||||
|
"""
|
||||||
|
function pke_initial_conditions(plant)
|
||||||
|
n0 = 1.0
|
||||||
|
C0 = (plant.beta_i ./ (plant.lambda_i .* plant.Lambda)) .* n0
|
||||||
|
return [n0; C0; plant.T_f0; plant.T_c0; plant.T_cold0]
|
||||||
|
end
|
||||||
59
julia-port/src/pke_th_rhs.jl
Normal file
59
julia-port/src/pke_th_rhs.jl
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
pke_th_rhs!(dx, x, t, plant, Q_sg, u)
|
||||||
|
|
||||||
|
In-place ODE RHS for the coupled PKE + thermal-hydraulics model.
|
||||||
|
Matches ../plant-model/pke_th_rhs.m term-for-term.
|
||||||
|
|
||||||
|
State x = [n, C1..C6, T_f, T_c, T_cold].
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
- `dx` : 10-element mutable output
|
||||||
|
- `x` : 10-element state
|
||||||
|
- `t` : time [s]
|
||||||
|
- `plant`: parameter NamedTuple from `pke_params`
|
||||||
|
- `Q_sg`: callable `Q_sg(t)` returning SG heat removal [W]
|
||||||
|
- `u` : scalar external reactivity [dk/k]
|
||||||
|
"""
|
||||||
|
function pke_th_rhs!(dx, x, t, plant, Q_sg, u)
|
||||||
|
n = x[1]
|
||||||
|
T_f = x[8]
|
||||||
|
T_c = x[9]
|
||||||
|
T_cold = x[10]
|
||||||
|
T_hot = 2 * T_c - T_cold
|
||||||
|
|
||||||
|
Qsg_t = Q_sg(t)
|
||||||
|
|
||||||
|
rho = u +
|
||||||
|
plant.alpha_f * (T_f - plant.T_f0) +
|
||||||
|
plant.alpha_c * (T_c - plant.T_c0)
|
||||||
|
|
||||||
|
# Neutronics
|
||||||
|
dx[1] = (rho - plant.beta) / plant.Lambda * n +
|
||||||
|
plant.lambda_i[1]*x[2] + plant.lambda_i[2]*x[3] +
|
||||||
|
plant.lambda_i[3]*x[4] + plant.lambda_i[4]*x[5] +
|
||||||
|
plant.lambda_i[5]*x[6] + plant.lambda_i[6]*x[7]
|
||||||
|
|
||||||
|
# Precursors
|
||||||
|
for i in 1:6
|
||||||
|
dx[1+i] = (plant.beta_i[i] / plant.Lambda) * n - plant.lambda_i[i] * x[1+i]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Thermal-hydraulics
|
||||||
|
dx[8] = (plant.P0 * n - plant.hA * (T_f - T_c)) / (plant.M_f * plant.c_f)
|
||||||
|
dx[9] = (plant.hA * (T_f - T_c) - 2 * plant.W * plant.c_c * (T_c - T_cold)) /
|
||||||
|
(plant.M_c * plant.c_c)
|
||||||
|
dx[10] = (plant.W * plant.c_c * (T_hot - T_cold) - Qsg_t) /
|
||||||
|
(plant.M_sg * plant.c_c)
|
||||||
|
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
Convenience: allocating version (returns dx as a new vector). Useful in
|
||||||
|
scripts and tests; for hot-loop reachability use the in-place version.
|
||||||
|
"""
|
||||||
|
function pke_th_rhs(x, t, plant, Q_sg, u)
|
||||||
|
dx = similar(x)
|
||||||
|
pke_th_rhs!(dx, x, t, plant, Q_sg, u)
|
||||||
|
return dx
|
||||||
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user