Hello everyone,
I am a new user of Julia ModelingToolkit and ControlSystem. I am currently working on a project with the topic Nonlinear Dynamic Simulation of a MicroGas Turbine Integrated with Concentrated Solar Thermal.
As a first step in building this system, I need to validate my MicroGas Turbine (MGT) model by comparing it with the T100 MGT model. I am performing an Acausal Modeling simulation in Julia ModelingToolkit. However, I am facing challenges in implementing the controller for the MGT simulation, which follows the following scheme:
The modular code I have written so far is as follows, and it only lacks the controller block:
using ControlSystems,PlutoUI
using ModelingToolkit, DifferentialEquations, CoolProp, Plots
using ModelingToolkitStandardLibrary
using ModelingToolkitStandardLibrary.Blocks
@independent_variables t
D = Differential(t)
const fluid = "Air"
PropsSI(out::AbstractString, name1::AbstractString, value1::Real, name2::AbstractString, value2::Real, fluid::AbstractString) = CoolProp.PropsSI(out, name1, value1, name2, value2, fluid)
@register_symbolic PropsSI(out::AbstractString, name1::AbstractString, value1::Real, name2::AbstractString, value2::Real, fluid::AbstractString)
PropsSI2(out::AbstractString, fluid::AbstractString) = CoolProp.PropsSI(out, fluid)
@register_symbolic PropsSI2(out::AbstractString, fluid::AbstractString)
function h(p_in, T_in)
h = PropsSI("H", "P", p_in, "T", T_in, fluid)
return PropsSI("H", "P", p_in, "T", T_in, fluid)
end
function hf(p_in, T_in)
hf = PropsSI("H", "P", p_in, "T", T_in, fluid)
return PropsSI("H", "P", p_in, "T", T_in, fluid)
end
function ρ(p_in, T_in)
ρ = PropsSI("D", "P", p_in, "T", T_in, fluid)
return PropsSI("D", "P", p_in, "T", T_in, fluid)
end
function u(p_in, T_in)
u = PropsSI("D", "P", p_in, "T", T_in, fluid)
return PropsSI("D", "P", p_in, "T", T_in, fluid)
end
function cv(T)
cv = PropsSI("Cvmass", "T", T, "P", 101325, fluid)
return PropsSI("Cvmass", "T", T, "P", 101325, fluid)
end
function cp(T)
cp = PropsSI("Cpmass", "T", T, "P", 101325, fluid)
return PropsSI("Cpmass", "T", T, "P", 101325, fluid)
end
function R_air()
R_air() = PropsSI2("GAS_CONSTANT", fluid)
return PropsSI2("GAS_CONSTANT", fluid)
end
@register_symbolic h(p_in, T_in)
@register_symbolic hf(p_in, T_in)
@register_symbolic ρ(p_in, T_in)
@register_symbolic u(p_in, T_in)
@register_symbolic cv(T)
@register_symbolic cp(T)
@register_symbolic R_air()
@connector function Port(;name)
vars = @variables begin
p(t), [input = true]
T(t), [input = true]
P(t)
mdot(t), [connect = Flow]
end
ODESystem(Equation[], t, vars, [];name=name)
end
@connector function Shaft(;name)
vars = @variables begin
ω(t)
end
ODESystem(Equation[], t, vars, [];name=name)
end
function Source(;name,source_temperature, source_pressure,source_mdot, source_power)
@named port = Port()
para = @parameters begin
end
vars = @variables begin
end
eqs = [
port.mdot ~ source_mdot
port.P ~ source_power
port.p ~ source_pressure
port.T ~ source_temperature
]
compose(ODESystem(eqs, t, vars, para;name),port)
end
function Compressor(;name, ṁ_comp = 0.5, η_c_is = 0.85, π_c = 3.0)
@named inport = Port()
@named outport = Port()
para = @parameters begin
m_cas_comp = 105, [description = "Mass Casing Compressor (kg)"]
c_casing = 500, [description = "Specific Heat Casing Compressor (J/kg·K)"]
κa = cp(inport.T)/cv(inport.T)
γ_a = (κa-1) / κa
end
vars = @variables begin
T2(t), [description = "Temperature outlet compressor (K)"]
end
eqs = [ D(T2) ~ (1 / (m_cas_comp * c_casing)) * (ṁ_comp *(h(outport.p,outport.T) - h(outport.p, T2)))
outport.p ~ π_c * inport.p
outport.T ~ inport.T * (1 + (1 / η_c_is) * (π_c^(γ_a) - 1))
outport.mdot ~ inport.mdot
outport.P ~ inport.P - (ṁ_comp * (h(outport.p, outport.T) - h(inport.p, inport.T)))]
compose(ODESystem(eqs, t, vars, para;name), inport, outport)
end
function Recuperator(; name, A_total = 10000.0, N = 3)
@named inport_hot = Port()
@named inport_cold = Port()
@named outport_hot = Port()
@named outport_cold = Port()
# Parameter sistem didefinisikan sebagai NamedTuple (nilai numerik)
para = (
cp_h = 500.0,
cp_c = 500.0,
m_dot_h = 0.508694,
m_dot_c = 0.5,
h_h = 100.0,
h_c = 100.0,
m_w = 185.0,
c_w = 500.0,
σ_h = 0.98,
σ_c = 0.95,
A = A_total / N
)
@independent_variables t
@variables T_w(t)[1:3]
@variables T_h_out(t)[1:3]
@variables T_c_out(t)[1:3]
# Inisialisasi vektor persamaan dengan tipe Equation
eqs = Equation[]
for i in 1:3
# Definisikan suhu masuk fluida
T_h_in_i = (i == 1) ? inport_hot.T : T_h_out[i-1]
T_c_in_i = (i == 3) ? inport_cold.T : T_c_out[i+1]
# Persamaan untuk fluida panas
push!(eqs, T_h_out[i] ~ ((para.m_dot_h * para.cp_h * T_h_in_i - 0.5 * para.h_h * para.A) * T_h_in_i + para.h_h * para.A * T_w[i]) /
((para.m_dot_h * para.cp_h * T_h_in_i) + (0.5 * para.h_h * para.A)))
# Persamaan untuk fluida dingin
push!(eqs, T_c_out[i] ~ ((para.m_dot_c * para.cp_c * T_c_in_i - 0.5 * para.h_c * para.A) * T_c_in_i + para.h_c * para.A * T_w[i]) /
((para.m_dot_c * para.cp_c * T_c_in_i) + (0.5 * para.h_c * para.A)))
# Persamaan diferensial untuk perubahan suhu dinding
Q_h = para.m_dot_h * (h(inport_hot.p, T_h_in_i) - h(inport_hot.p, T_h_out[i]))
Q_c = para.m_dot_c * (h(inport_cold.p, T_c_out[i]) - h(inport_cold.p, T_c_in_i))
push!(eqs, Differential(t)(T_w[i]) ~ (Q_h - Q_c) / ((para.m_w / N) * para.c_w))
end
# Kondisi batas port
push!(eqs, outport_hot.T ~ T_h_out[3])
push!(eqs, outport_cold.T ~ T_c_out[1])
push!(eqs, outport_hot.p ~ inport_hot.p * para.σ_h)
push!(eqs, outport_cold.p ~ inport_cold.p * para.σ_c)
push!(eqs, outport_hot.mdot ~ -para.m_dot_h)
push!(eqs, outport_cold.mdot ~ -para.m_dot_c)
push!(eqs, outport_hot.P ~ inport_hot.P)
push!(eqs, outport_cold.P ~ inport_cold.P)
# Gabungkan semua variabel keadaan ke dalam satu vektor
flat_vars = vcat(T_w..., T_h_out..., T_c_out...)
# Buat sistem ODE
sys = ODESystem(eqs, t, flat_vars, []; name=name)
# Komposisi sistem dengan semua port dalam satu vektor
ports = [inport_hot, inport_cold, outport_hot, outport_cold]
comp_sys = compose(sys, ports)
return comp_sys
end
function CombustionChamber(;name)
@named inport = Port()
@named outport = Port()
@named ṁ_fuel = RealInput()
pars = @parameters begin
η_comb = 0.97, [description = "Combustor efficiency"]
σ_comb = 0.945, [description = "Artificial Pressure Losses"]
V_comb = 5.5422e-3, [description = "Combustor Volume (m^3)"]
LHV = 40.564e6, [description = "Lower Heating Value (J/kg)"]
m_cas_comb = 10.0, [description = "Combustor casing mass (kg)"]
c_casing = 500, [description = "Specific heat capacity of casing (J/kg·K)"]
Tf = 303.15, [description = "Fuel Temperature (K)"]
pf = 600000, [description = "Fuel Pressure (Pa) (6 bar)"]
ρf = 0.8217, [description = "Fuel Density (kg/m^3)"]
ṁ_comp = 0.5
ṁ_turb = 0.50627
xṁ_fuel(t) = 0.00627
end
vars = @variables begin
ρ3(t), [description = "Density in combustor"]
T3(t), [description = "Internal energy in combustor"]
TIT(t)
end
# Define equations (steady-state: time derivatives are zero)
eqs = [ D(ρ3) ~ (ṁ_comp + xṁ_fuel - ṁ_turb) / V_comb,
D(T3) ~ (1 / (ρ3 * V_comb*cv(outport.T))) * (ṁ_comp * h(inport.p, inport.T) + xṁ_fuel * hf(pf, Tf) + (η_comb * xṁ_fuel * LHV) - ṁ_turb * h(outport.p, outport.T) - (cv(T3)*T3)*(ṁ_comp + xṁ_fuel - ṁ_turb)),
D(TIT) ~ (ṁ_turb * (h(outport.p, outport.T) - h(outport.p, TIT))) / (m_cas_comb * c_casing),
outport.p ~ ρ3 * R_air() * outport.T,
outport.mdot ~ inport.mdot,
outport.P ~ inport.P,
outport.T ~ T3,
xṁ_fuel ~ ṁ_fuel.u]
compose(ODESystem(eqs, t, name=name), inport, outport)
end
function Turbine(;name, ṁ_t = 0.508694, η_turb_is = 0.95, π_t = 3.4)
@named inport = Port()
@named outport = Port()
@named inshaft = Shaft()
@named outshaft = Shaft()
@named T4 = RealOutput()
para = @parameters begin
m_cas_turb = 122.2, [description = "Turbine casing mass (kg)"]
c_casing = 500, [description = "Specific Heat Casing Turbine (J/kg·K)"]
κg = cp(inport.T)/cv(inport.T)
γ_g = (κg - 1) / κg
end
vars = @variables begin
xT4(t), [description = "Turbine Outlet Temperature (K)"]
end
eqs = [ D(xT4) ~ (1 / (m_cas_turb * c_casing)) * (ṁ_t * (h(outport.p, outport.T) - h(outport.p, xT4)))
outport.p ~ inport.p / π_t
outport.T ~ inport.T * (1 - η_turb_is * (1 - π_t^(-γ_g)))
outport.mdot ~ inport.mdot
outport.P ~ inport.P + (ṁ_t * (h(inport.p, inport.T) - h(outport.p, outport.T)))
outshaft.ω ~ inshaft.ω
xT4 ~ T4.u]
compose(ODESystem(eqs, t, vars, para;name), inport, outport, inshaft, outshaft)
end
function RotationalSystem(;name)
@named inport = Port()
@named outport = Port()
@named outshaft = Shaft()
@named Pload = RealInput()
para = @parameters begin
I = 8.3e-3, [description = "Inertia (kg.m^2)"]
xPload(t) = 70000
P_lb = 21.33e-3 * (outshaft.ω * (60/(2*pi))), [description = "Power Bearing Losses (W)"]
P_le = -5.0802e-3 * (xPload/1000)^3 + 2.3891 * (xPload/1000)^2 − 2.3492e2 * (xPload/1000) + 8.3781e3 , [description = "Power Electrical Losses (W)"]
end
vars = @variables begin
ω(t)
end
eqs = [ D(ω) ~ (1 / (I * ω)) * (outport.P/1000)
outport.p ~ inport.p
outport.T ~ inport.T
outport.mdot ~ inport.mdot
outport.P ~ inport.P - xPload - P_lb - P_le
outshaft.ω ~ ω
xPload ~ Pload.u]
compose(ODESystem(eqs, t, vars, para;name), inport, outport, outshaft)
end
function Sink(;name)
@named port = Port()
para = @parameters begin
end
vars = @variables begin
p(t)
T(t)
mdot(t)
P(t)
end
eqs = [
mdot ~ port.mdot
p ~ port.p
T ~ port.T
P ~ port.P
]
compose(ODESystem(eqs, t, vars, para;name),port)
end
start_T = 300
start_p = 101325
mdot = 0
start_P = 0
function MicroGasTurbine(;name = :MicroGasTurbine)
@named source = Source(source_temperature = start_T, source_pressure = start_p, source_mdot = mdot, source_power = start_P)
@named compressor = Compressor()
@named recuperator = Recuperator()
@named combustionchamber = CombustionChamber()
@named turbine = Turbine()
@named shaft = RotationalSystem()
@named sink = Sink()
eqs = [ connect(source.port,compressor.inport)
connect(compressor.outport, recuperator.inport_cold)
connect(recuperator.outport_cold, combustionchamber.inport)
connect(combustionchamber.outport, turbine.inport)
connect(turbine.outport, shaft.inport)
connect(turbine.outport, recuperator.inport_hot)
connect(recuperator.outport_hot, sink.port)
connect(shaft.outshaft, turbine.inshaft)]
return compose(ODESystem(eqs, t, systems = [source,compressor,combustionchamber,recuperator,turbine, shaft, sink], name = name))
end
@named mgt = MicroGasTurbine()
let
@named mgt = MicroGasTurbine()
ssys = structural_simplify(mgt)
u0 = [compressor.T2 => 300, combustionchamber.ρ3 => 10,combustionchamber.T3 => 300, combustionchamber.TIT => 300, recuperator.T_w[1] => 157, recuperator.T_w[2] => 403.5, recuperator.T_w[3] => 650, turbine.xT4 => 300, shaft.ω => 5850]
tspan = (0.0, 1000.0)
prob = ODEProblem(ssys, u0, tspan)
sol = solve(prob, saveat = 1.0)
plot(sol, layout = 9)
end
Is there anyone here who can provide guidance on designing the controller for this system? I would really appreciate your help!
Thank you in advance!