using ModelingToolkit
using ModelingToolkitStandardLibrary
using ModelingToolkitStandardLibrary.Blocks
rc = 0.25 # Reference concentration
@component function MixingTank(; name, c0 = 0.8, T0 = 308.5, a1 = 0.2674, a21 = 1.815, a22 = 0.4682, b = 1.5476, k0 = 1.05e14, ϵ = 34.2894, xc=c0, xT=T0)
pars = @parameters begin
c0=c0, [description = "Nominal concentration"]
T0=T0, [description = "Nominal temperature"]
a1=a1
a21=a21
a22=a22
b=b
k0=k0
ϵ=ϵ
end
systems = @named begin
T_c = RealInput()
c = RealOutput()
T = RealOutput()
end
vars = @variables begin
gamma(t), [description = "Reaction speed"]
xc(t) = xc, [description = "Concentration"]
xT(t) = xT, [description = "Temperature"]
xT_c(t), [description = "Cooling temperature"]
end
begin
τ0 = 60
wk0 = k0 / c0
wϵ = ϵ * T0
wa11 = a1 / τ0
wa12 = c0 / τ0
wa13 = c0 * a1 / τ0
wa21 = a21 / τ0
wa22 = a22 * T0 / τ0
wa23 = T0 * (a21 - b) / τ0
wb = b / τ0
end
equations = [
gamma ~ xc * wk0 * exp(-wϵ / xT)
D(xc) ~ -wa11 * xc - wa12 * gamma + wa13
D(xT) ~ -wa21 * xT + wa22 * gamma + wa23 + wb * xT_c
xc ~ c.u
xT ~ T.u
xT_c ~ T_c.u
]
return System(equations, t, vars, pars; name, systems)
end
@component function InverseControlledTank(; name)
pars = @parameters begin
end
begin
c0 = 0.8 # "Nominal concentration
T0 = 308.5 # "Nominal temperature
x10 = 0.42
x20 = 0.01
u0 = -0.0224
c_start = c0 * (1 - x10) # Initial concentration
T_start = T0 * (1 + x20) # Initial temperature
c_high_start = c0 * (1 - 0.72) # Reference concentration
T_c_start = T0 * (1 + u0) # Initial cooling temperature
end
systems = @named begin
ref = Constant(k = 0.25) # Concentration reference
ff_gain = Gain(k = 1) # To allow turning ff off
controller = PI(k = 10, T = 500)
tank = MixingTank(xc = c_start, xT = T_start, c0 = c0, T0 = T0)
inverse_tank = MixingTank(xc = nothing, xT = nothing, c0 = c0, T0 = T0)
feedback = Feedback()
add = Add()
filter = FirstOrder(k=1, T=1)
noise_filter = FirstOrder(k = 1, T = 1, x = nothing)
# limiter = Gain(k=1)
limiter = Limiter(y_max = 370, y_min = 250) # Saturate the control input
end
vars = @variables begin
end
equations = [
connect(ref.output, :r, filter.input)
connect(filter.output, inverse_tank.c)
connect(inverse_tank.T_c, ff_gain.input)
connect(ff_gain.output, :uff, limiter.input)
connect(limiter.output, add.input1)
connect(controller.ctr_output, :u, add.input2)
connect(add.output, :u_tot, tank.T_c)
connect(inverse_tank.T, feedback.input1)
connect(tank.T, :y, noise_filter.input)
connect(noise_filter.output, feedback.input2)
connect(feedback.output, :e, controller.err_input)
]
return System(equations, t, vars, pars; name, systems)
end;
@named model = InverseControlledTank()
ssys = mtkcompile(model, split=true)
cm = complete(model)
op = Dict(
cm.filter.output.u => 0.8 * (1 - 0.42),
# D(cm.filter.y.u) => 0
)
tspan = (0.0, 1000.0)
prob = ODEProblem(ssys, op, tspan)
julia> prob = ODEProblem(ssys, op, tspan)
ERROR: ArgumentError: Symbol inverse_tank₊xT(t) is not present in the system.
Stacktrace:
[1] (::ModelingToolkitBase.CheckInvalidAndTrackNamespaced)(x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{…})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/codegen.jl:1166
[2] query(predicate::ModelingToolkitBase.CheckInvalidAndTrackNamespaced, expr::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{…}; recurse::typeof(iscall), default::Bool)
@ SymbolicUtils ~/.julia/packages/SymbolicUtils/6HFeR/src/substitute.jl:292
[3] query(predicate::ModelingToolkitBase.CheckInvalidAndTrackNamespaced, expr::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{…})
@ SymbolicUtils ~/.julia/packages/SymbolicUtils/6HFeR/src/substitute.jl:291
[4] build_explicit_observed_function(sys::System, ts::Vector{…}; inputs::Nothing, disturbance_inputs::Nothing, known_disturbance_inputs::Nothing, disturbance_argument::Bool, expression::Bool, eval_expression::Bool, eval_module::Module, output_type::Type, checkbounds::Bool, ps::Vector{…}, return_inplace::Bool, param_only::Bool, throw::Bool, cse::Bool, mkarray::Nothing, wrap_delays::Bool, force_time_independent::Bool, kwargs::@Kwargs{…})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/codegen.jl:1320
[5] build_explicit_observed_function
@ ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/codegen.jl:1219 [inlined]
[6] #observed#230
@ ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/abstractsystem.jl:439 [inlined]
[7] observed
@ ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/abstractsystem.jl:411 [inlined]
[8] observed(fn::NonlinearFunction{…}, sym::Vector{…})
@ SciMLBase ~/.julia/packages/SciMLBase/MzDs3/src/scimlfunctions.jl:5619
[9] observed
@ ~/.julia/packages/SymbolicIndexingInterface/D9aQf/src/index_provider_interface.jl:212 [inlined]
[10] _getsym(sys::SCCNonlinearProblem{…}, ::SymbolicIndexingInterface.NotSymbolic, elt::SymbolicIndexingInterface.ScalarSymbolic, sym::Vector{…})
@ SymbolicIndexingInterface ~/.julia/packages/SymbolicIndexingInterface/D9aQf/src/state_indexing.jl:269
[11] getsym(sys::SCCNonlinearProblem{…}, sym::Vector{…})
@ SymbolicIndexingInterface ~/.julia/packages/SymbolicIndexingInterface/D9aQf/src/state_indexing.jl:31
[12] ModelingToolkitBase.GetUpdatedU0(sys::System, initprob::SciMLBase.AbstractNonlinearProblem, op::ModelingToolkitBase.AtomicArrayDict{…})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/problem_utils.jl:1098
[13] maybe_build_initialization_problem(sys::System, iip::Bool, op::ModelingToolkitBase.AtomicArrayDict{…}, t::Float64, guesses::Dict{…}; time_dependent_init::Bool, u0_constructor::Function, p_constructor::Function, floatT::Type, initialization_eqs::Vector{…}, use_scc::Bool, eval_expression::Bool, eval_module::Module, missing_guess_value::ModelingToolkitBase.MissingGuessValue.var"typeof(MissingGuessValue)", implicit_dae::Bool, is_steadystateprob::Bool, expression::Type, kwargs::@Kwargs{…})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/problem_utils.jl:1237
[14] process_SciMLProblem(constructor::Type, sys::System, op::Dict{…}; build_initializeprob::Bool, implicit_dae::Bool, t::Float64, guesses::Dict{…}, warn_initialize_determined::Bool, initialization_eqs::Vector{…}, eval_expression::Bool, eval_module::Module, fully_determined::Nothing, check_initialization_units::Bool, u0_eltype::Nothing, tofloat::Bool, u0_constructor::typeof(identity), p_constructor::typeof(identity), check_length::Bool, symbolic_u0::Bool, warn_cyclic_dependency::Bool, circular_dependency_max_cycle_length::Int64, circular_dependency_max_cycles::Int64, initsys_mtkcompile_kwargs::@NamedTuple{}, substitution_limit::Int64, use_scc::Bool, time_dependent_init::Bool, algebraic_only::Bool, missing_guess_value::ModelingToolkitBase.MissingGuessValue.var"typeof(MissingGuessValue)", allow_incomplete::Bool, is_initializeprob::Bool, is_steadystateprob::Bool, return_operating_point::Bool, kwargs::@Kwargs{…})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/Sq573/src/systems/problem_utils.jl:1555
[15] (ODEProblem{…})(sys::System, op::Any, tspan::Tuple{…}; callback::Any, check_length::Bool, eval_expression::Bool, expression::Type, eval_module::Module, check_compatibility::Bool, _skip_events::Bool, kwargs::@Kwargs{})
@ ModelingToolkitBase ~/.julia/packages/ModelingToolkitBase/Sq573/src/problems/odeproblem.jl:110
[16] ODEProblem
@ ~/.julia/packages/ModelingToolkitBase/Sq573/src/problems/odeproblem.jl:101 [inlined]
[17] ODEProblem
@ ./none:-1 [inlined]
[18] ODEProblem(sys::System, op::Dict{Num, Float64}, tspan::Tuple{Float64, Float64})
@ ModelingToolkitBase ./none:-1
[19] top-level scope
@ REPL[13]:1
Some type information was truncated. Use `show(err)` to see complete types.
MWE