Skip to content

Commit fc32fe9

Browse files
committed
fix: improve handling of expression in metadata
The variables in mtkmodel can have complicated expressions as `guess` and other metadata. Instead of evaluating them while defining the mtk-model, make those exprs part of the the definition. Along with this, the metadata (in structure dict) is kept in expr form.
1 parent 335c7ba commit fc32fe9

File tree

3 files changed

+62
-39
lines changed

3 files changed

+62
-39
lines changed

docs/src/basics/MTKModel_Connector.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ end
298298
```
299299

300300
!!! note
301-
301+
302302
For more examples of usage, checkout [ModelingToolkitStandardLibrary.jl](https://github.com/SciML/ModelingToolkitStandardLibrary.jl/)
303303

304304
## More on `Model.structure`
@@ -408,12 +408,13 @@ The conditional parts are reflected in the `structure`. For `BranchOutsideTheBlo
408408

409409
```julia
410410
julia> BranchOutsideTheBlock.structure
411-
Dict{Symbol, Any} with 6 entries:
411+
Dict{Symbol, Any} with 7 entries:
412412
:components => Any[(:if, :flag, Vector{Union{Expr, Symbol}}[[:sys1, :C]], Any[])]
413413
:kwargs => Dict{Symbol, Dict}(:flag=>Dict{Symbol, Bool}(:value=>1))
414414
:structural_parameters => Dict{Symbol, Dict}(:flag=>Dict{Symbol, Bool}(:value=>1))
415415
:independent_variable => t
416-
:parameters => Dict{Symbol, Dict{Symbol, Any}}(:a2 => Dict(:type => AbstractArray{Real}, :condition => (:if, :flag, Dict{Symbol, Any}(:kwargs => Dict{Any, Any}(:a1 => Dict{Symbol, Union{Nothing, DataType}}(:value => nothing, :type => Real)), :parameters => Any[Dict{Symbol, Dict{Symbol, Any}}(:a1 => Dict(:type => AbstractArray{Real}))]), Dict{Symbol, Any}(:variables => Any[Dict{Symbol, Dict{Symbol, Any}}()], :kwargs => Dict{Any, Any}(:a2 => Dict{Symbol, Union{Nothing, DataType}}(:value => nothing, :type => Real)), :parameters => Any[Dict{Symbol, Dict{Symbol, Any}}(:a2 => Dict(:type => AbstractArray{Real}))]))), :a1 => Dict(:type => AbstractArray{Real}, :condition => (:if, :flag, Dict{Symbol, Any}(:kwargs => Dict{Any, Any}(:a1 => Dict{Symbol, Union{Nothing, DataType}}(:value => nothing, :type => Real)), :parameters => Any[Dict{Symbol, Dict{Symbol, Any}}(:a1 => Dict(:type => AbstractArray{Real}))]), Dict{Symbol, Any}(:variables => Any[Dict{Symbol, Dict{Symbol, Any}}()], :kwargs => Dict{Any, Any}(:a2 => Dict{Symbol, Union{Nothing, DataType}}(:value => nothing, :type => Real)), :parameters => Any[Dict{Symbol, Dict{Symbol, Any}}(:a2 => Dict(:type => AbstractArray{Real}))]))))
416+
:parameters => Dict{Symbol, Dict{Symbol, Any}}(:a2 => Dict(:type=>AbstractArray{Real}, :condition=>(:if, :flag, Dict{Symbol, Any}(:kwargs=>Dict{Any, Any}(:a1=>Dict{Symbol, Union{Nothing, DataType}}(:value=>nothing, :type=>Real)), :parameters=>Any[Dict{Symbol, Dict{Symbol, Any}}(:a1=>Dict(:type=>AbstractArray{Real}))]), Dict{Symbol, Any}(:variables=>Any[Dict{Symbol, Dict{Symbol, Any}}()], :kwargs=>Dict{Any, Any}(:a2=>Dict{Symbol, Union{Nothing, DataType}}(:value=>nothing, :type=>Real)), :parameters=>Any[Dict{Symbol, Dict{Symbol, Any}}(:a2=>Dict(:type=>AbstractArray{Real}))]))), :a1 => Dict(:type=>AbstractArray{Real}, :condition=>(:if, :flag, Dict{Symbol, Any}(:kwargs=>Dict{Any, Any}(:a1=>Dict{Symbol, Union{Nothing, DataType}}(:value=>nothing, :type=>Real)), :parameters=>Any[Dict{Symbol, Dict{Symbol, Any}}(:a1=>Dict(:type=>AbstractArray{Real}))]), Dict{Symbol, Any}(:variables=>Any[Dict{Symbol, Dict{Symbol, Any}}()], :kwargs=>Dict{Any, Any}(:a2=>Dict{Symbol, Union{Nothing, DataType}}(:value=>nothing, :type=>Real)), :parameters=>Any[Dict{Symbol, Dict{Symbol, Any}}(:a2=>Dict(:type=>AbstractArray{Real}))]))))
417+
:defaults => Dict{Symbol, Any}(:a1=>10)
417418
:equations => Any[(:if, :flag, ["a1 ~ 0"], ["a2 ~ 0"])]
418419
```
419420

src/systems/model_parsing.jl

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types;
187187
var = generate_var!(dict, a, varclass; indices, type)
188188
update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var,
189189
varclass, where_types)
190-
(var, def)
190+
return var, def, Dict()
191191
end
192192
Expr(:(::), a, type) => begin
193193
type = getfield(mod, type)
@@ -201,13 +201,13 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types;
201201
Expr(:call, a, b) => begin
202202
var = generate_var!(dict, a, b, varclass, mod; indices, type)
203203
update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var,
204-
varclass, where_types)
205-
(var, def)
204+
varclass, where_types)
205+
return var, def, Dict()
206206
end
207207
Expr(:(=), a, b) => begin
208208
Base.remove_linenums!(b)
209209
def, meta = parse_default(mod, b)
210-
var, def = parse_variable_def!(
210+
var, def, _ = parse_variable_def!(
211211
dict, mod, a, varclass, kwargs, where_types; def, type)
212212
if dict[varclass] isa Vector
213213
dict[varclass][1][getname(var)][:default] = def
@@ -225,12 +225,13 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types;
225225
end
226226
end
227227
end
228-
var = set_var_metadata(var, meta)
228+
var, metadata_with_exprs = set_var_metadata(var, meta)
229+
return var, def, metadata_with_exprs
229230
end
230-
(var, def)
231+
return var, def, Dict()
231232
end
232233
Expr(:tuple, a, b) => begin
233-
var, def = parse_variable_def!(
234+
var, def, _ = parse_variable_def!(
234235
dict, mod, a, varclass, kwargs, where_types; type)
235236
meta = parse_metadata(mod, b)
236237
if meta !== nothing
@@ -244,9 +245,10 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types;
244245
end
245246
end
246247
end
247-
var = set_var_metadata(var, meta)
248+
var, metadata_with_exprs = set_var_metadata(var, meta)
249+
return var, def, metadata_with_exprs
248250
end
249-
(var, def)
251+
return var, def, Dict()
250252
end
251253
Expr(:ref, a, b...) => begin
252254
indices = map(i -> UnitRange(i.args[2], i.args[end]), b)
@@ -350,21 +352,27 @@ function parse_metadata(mod, a)
350352
end
351353
end
352354

355+
function _set_var_metadata!(metadata_with_exprs, a, m, v::Expr)
356+
push!(metadata_with_exprs, m => v)
357+
a
358+
end
359+
_set_var_metadata!(metadata_with_exprs, a, m, v) = wrap(set_scalar_metadata(unwrap(a), m, v))
360+
353361
function set_var_metadata(a, ms)
362+
metadata_with_exprs = Dict{DataType, Expr}()
354363
for (m, v) in ms
355-
a = wrap(set_scalar_metadata(unwrap(a), m, v))
364+
m == VariableGuess && v isa Symbol && (v = quote $v end)
365+
a = _set_var_metadata!(metadata_with_exprs, a, m, v)
356366
end
357-
a
367+
a, metadata_with_exprs
358368
end
359369

360370
function get_var(mod::Module, b)
361371
if b isa Symbol
362-
getproperty(mod, b)
363-
elseif b isa Expr
364-
Core.eval(mod, b)
365-
else
366-
b
372+
isdefined(mod, b) && return getproperty(mod, b)
373+
isdefined(@__MODULE__, b) && return getproperty(@__MODULE__, b)
367374
end
375+
b
368376
end
369377

370378
function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps, c_evts, d_evts,
@@ -595,10 +603,24 @@ function parse_variable_arg!(exprs, vs, dict, mod, arg, varclass, kwargs, where_
595603
end
596604

597605
function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types)
598-
vv, def = parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types)
606+
vv, def, metadata_with_exprs = parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types)
599607
name = getname(vv)
600-
return vv isa Num ? name : :($name...),
601-
:($name = $name === nothing ? $setdefault($vv, $def) : $setdefault($vv, $name))
608+
609+
varexpr = quote
610+
$name = if $name === nothing
611+
$setdefault($vv, $def)
612+
else
613+
$setdefault($vv, $name)
614+
end
615+
end
616+
617+
metadata_expr = Expr(:block)
618+
for (k,v) in metadata_with_exprs
619+
push!(metadata_expr.args, :($name = $wrap($set_scalar_metadata($unwrap($name), $k, $v))))
620+
end
621+
622+
push!(varexpr.args, metadata_expr)
623+
return vv isa Num ? name : :($name...), varexpr
602624
end
603625

604626
function handle_conditional_vars!(
@@ -882,7 +904,7 @@ function _parse_components!(body, kwargs)
882904
Base.remove_linenums!(body)
883905
arg = body.args[end]
884906

885-
MLStyle.@match arg begin
907+
MLStyle.@match arg begin
886908
Expr(:(=), a, Expr(:comprehension, Expr(:generator, b, Expr(:(=), c, d)))) => begin
887909
array_varexpr = Expr(:block)
888910

@@ -901,10 +923,10 @@ function _parse_components!(body, kwargs)
901923
# Note that `e` is of the form `Tuple{Expr(:(=), c, d)}`
902924
error("More than one index isn't supported while building component array")
903925
end
904-
Expr(:block) => begin
905-
# TODO: Do we need this?
906-
error("Multiple `@components` block detected within a single block")
907-
end
926+
Expr(:block) => begin
927+
# TODO: Do we need this?
928+
error("Multiple `@components` block detected within a single block")
929+
end
908930
Expr(:(=), a, Expr(:for, Expr(:(=), c, d), b)) => begin
909931
Base.remove_linenums!(b)
910932
array_varexpr = Expr(:block)
@@ -917,21 +939,21 @@ function _parse_components!(body, kwargs)
917939

918940
expr = _named_idxs(a, d, :($c -> $(b.args[end])); extra_args = array_varexpr)
919941
end
920-
Expr(:(=), a, b) => begin
921-
arg = deepcopy(arg)
922-
b = deepcopy(arg.args[2])
942+
Expr(:(=), a, b) => begin
943+
arg = deepcopy(arg)
944+
b = deepcopy(arg.args[2])
923945

924946
component_args!(a, b, varexpr, kwargs)
925947

926-
arg.args[2] = b
948+
arg.args[2] = b
927949
expr = :(@named $arg)
928-
push!(comp_names, a)
929-
if (isa(b.args[1], Symbol) || Meta.isexpr(b.args[1], :.))
930-
push!(comps, [a, b.args[1]])
950+
push!(comp_names, a)
951+
if (isa(b.args[1], Symbol) || Meta.isexpr(b.args[1], :.))
952+
push!(comps, [a, b.args[1]])
953+
end
931954
end
955+
_ => error("Couldn't parse the component body: $arg")
932956
end
933-
_ => error("Couldn't parse the component body: $arg")
934-
end
935957

936958
return comp_names, comps, expr, varexpr
937959
end

test/model_parsing.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,8 @@ end
376376

377377
@testset "Metadata in variables" begin
378378
metadata = Dict(:description => "Variable to test metadata in the Model.structure",
379-
:input => true, :bounds => (-1, 1), :connection_type => :Flow,
380-
:tunable => false, :disturbance => true, :dist => Normal(1, 1))
379+
:input => true, :bounds => :((-1, 1)), :connection_type => :Flow,
380+
:tunable => false, :disturbance => true, :dist => :(Normal(1, 1)))
381381

382382
@connector MockMeta begin
383383
m(t),
@@ -473,7 +473,7 @@ using ModelingToolkit: getdefault, scalarize
473473

474474
@named model_with_component_array = ModelWithComponentArray()
475475

476-
@test ModelWithComponentArray.structure[:parameters][:r][:unit] == u""
476+
@test eval(ModelWithComponentArray.structure[:parameters][:r][:unit]) == eval(u"")
477477
@test lastindex(parameters(model_with_component_array)) == 3
478478

479479
# Test the constant `k`. Manually k's value should be kept in sync here

0 commit comments

Comments
 (0)