From 6f4b5900b57f367b712d1f87bde4040d1ca1df34 Mon Sep 17 00:00:00 2001 From: contradict Date: Fri, 26 Jul 2024 11:43:00 -0700 Subject: [PATCH 01/12] Make default value units consistent --- src/systems/model_parsing.jl | 47 +++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index b10b7125a2..0d2ccd8890 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -51,7 +51,7 @@ function _model_macro(mod, name, expr, isconnector) c_evts = [] d_evts = [] kwargs = OrderedCollections.OrderedSet() - where_types = Expr[] + where_types = Union{Symbol, Expr}[] push!(exprs.args, :(variables = [])) push!(exprs.args, :(parameters = [])) @@ -143,9 +143,17 @@ end pop_structure_dict!(dict, key) = length(dict[key]) == 0 && pop!(dict, key) function update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var, - varclass, where_types) + varclass, where_types, meta) if indices isa Nothing - push!(kwargs, Expr(:kw, Expr(:(::), a, Union{Nothing, type}), nothing)) + kwtype = if !isnothing(meta) && haskey(meta, VariableUnit) + dim = dimension(eval(meta[VariableUnit])) + units = gensym(:U) + push!(where_types, units) + Expr(:curly, Union, :Nothing, Expr(:curly, Quantity, Expr(:(<:), type), dim, units)) + else + Expr(:curly, Union, Nothing, type) + end + push!(kwargs, Expr(:kw, Expr(:(::), a, kwtype), nothing)) dict[:kwargs][getname(var)] = Dict(:value => def, :type => type) else vartype = gensym(:T) @@ -166,7 +174,7 @@ end function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; def = nothing, indices::Union{Vector{UnitRange{Int}}, Nothing} = nothing, - type::Type = Real) + meta = nothing, type::Type = Real) metatypes = [(:connection_type, VariableConnectType), (:description, VariableDescription), (:unit, VariableUnit), @@ -186,7 +194,7 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; a::Symbol => begin var = generate_var!(dict, a, varclass; indices, type) update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var, - varclass, where_types) + varclass, where_types, meta) return var, def, Dict() end Expr(:(::), a, type) => begin @@ -201,14 +209,14 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; Expr(:call, a, b) => begin var = generate_var!(dict, a, b, varclass, mod; indices, type) update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var, - varclass, where_types) + varclass, where_types, meta) return var, def, Dict() end Expr(:(=), a, b) => begin Base.remove_linenums!(b) def, meta = parse_default(mod, b) var, def, _ = parse_variable_def!( - dict, mod, a, varclass, kwargs, where_types; def, type) + dict, mod, a, varclass, kwargs, where_types; def, meta, type) if dict[varclass] isa Vector dict[varclass][1][getname(var)][:default] = def else @@ -231,9 +239,9 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; return var, def, Dict() end Expr(:tuple, a, b) => begin - var, def, _ = parse_variable_def!( - dict, mod, a, varclass, kwargs, where_types; type) meta = parse_metadata(mod, b) + var, def, _ = parse_variable_def!( + dict, mod, a, varclass, kwargs, where_types; meta, type) if meta !== nothing for (type, key) in metatypes if (mt = get(meta, key, nothing)) !== nothing @@ -616,11 +624,22 @@ function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) dict, mod, arg, varclass, kwargs, where_types) name = getname(vv) - varexpr = quote - $name = if $name === nothing - $setdefault($vv, $def) - else - $setdefault($vv, $name) + varexpr = if haskey(metadata_with_exprs, VariableUnit) + unit = metadata_with_exprs[VariableUnit] + quote + $name = if $name === nothing + $setdefault($vv, $def) + else + $setdefault($vv, $ustrip($unit, $name)) + end + end + else + quote + $name = if $name === nothing + $setdefault($vv, $def) + else + $setdefault($vv, $name) + end end end From 1c45ee2915c7bc9f8b167e35be13665ea9d4fe35 Mon Sep 17 00:00:00 2001 From: contradict Date: Tue, 30 Jul 2024 17:01:02 -0700 Subject: [PATCH 02/12] I don't see how to get this approach to work. I need to evaluate the unit macro to know the types to put in the model definition. I need a type parameter to be generic over Unitful units or DynamicQuantities Dimensions, but that can't exist until after evaluation. --- src/systems/model_parsing.jl | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 0d2ccd8890..455293db8b 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -142,17 +142,33 @@ end pop_structure_dict!(dict, key) = length(dict[key]) == 0 && pop!(dict, key) +create_kwarg_type(meta, where_types, type) = haskey(meta, VariableUnit) ? create_kwarg_type_(meta[VariableUnit], where_types, type) : Expr(:curly, Union, Nothing, type) +function create_kwarg_type_(unitmacro, where_types, type) + fn = gensym() + quote + #Expr(:curly, Union, :Nothing, Expr(:curly, Unitful.Quantity, Expr(:(<:), type), D, units)) + function $fn() + let u = eval($unitmacro) + if typeof(u) <: Unitful.FreeUnits + Union{Nothing, Unitful.Quantity{<:$type, dimension(u), u}} + elseif typeof(u) <: DynamicQuantities.Quantity + Union{Nothing, DynamicQuantities.Quantity{<:$type, $units}} + else + throw("Unsupported units library") + end + end + end + $fn() + end +end +# function create_kwarg_type_(::DynamicQuantities.Quantity{T, D}, where_types, type) where {T, D} +# Expr(:curly, Union, :Nothing, Expr(:curly, DynamicQuantities.Quantity, Expr(:(<:), type), D)) +# end + function update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var, varclass, where_types, meta) if indices isa Nothing - kwtype = if !isnothing(meta) && haskey(meta, VariableUnit) - dim = dimension(eval(meta[VariableUnit])) - units = gensym(:U) - push!(where_types, units) - Expr(:curly, Union, :Nothing, Expr(:curly, Quantity, Expr(:(<:), type), dim, units)) - else - Expr(:curly, Union, Nothing, type) - end + kwtype = create_kwarg_type(meta, where_types, type) push!(kwargs, Expr(:kw, Expr(:(::), a, kwtype), nothing)) dict[:kwargs][getname(var)] = Dict(:value => def, :type => type) else From 8c7068624f5d117b4a72048fe092c1b73788897c Mon Sep 17 00:00:00 2001 From: contradict Date: Wed, 31 Jul 2024 17:33:37 -0700 Subject: [PATCH 03/12] Support both DynamicQuantites and Unitful for scalar valiables --- src/systems/model_parsing.jl | 62 +++++++++++++++++------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 455293db8b..a619c6887d 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -142,34 +142,16 @@ end pop_structure_dict!(dict, key) = length(dict[key]) == 0 && pop!(dict, key) -create_kwarg_type(meta, where_types, type) = haskey(meta, VariableUnit) ? create_kwarg_type_(meta[VariableUnit], where_types, type) : Expr(:curly, Union, Nothing, type) -function create_kwarg_type_(unitmacro, where_types, type) - fn = gensym() - quote - #Expr(:curly, Union, :Nothing, Expr(:curly, Unitful.Quantity, Expr(:(<:), type), D, units)) - function $fn() - let u = eval($unitmacro) - if typeof(u) <: Unitful.FreeUnits - Union{Nothing, Unitful.Quantity{<:$type, dimension(u), u}} - elseif typeof(u) <: DynamicQuantities.Quantity - Union{Nothing, DynamicQuantities.Quantity{<:$type, $units}} - else - throw("Unsupported units library") - end - end - end - $fn() - end -end -# function create_kwarg_type_(::DynamicQuantities.Quantity{T, D}, where_types, type) where {T, D} -# Expr(:curly, Union, :Nothing, Expr(:curly, DynamicQuantities.Quantity, Expr(:(<:), type), D)) -# end - function update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var, varclass, where_types, meta) if indices isa Nothing - kwtype = create_kwarg_type(meta, where_types, type) - push!(kwargs, Expr(:kw, Expr(:(::), a, kwtype), nothing)) + if !isnothing(meta) && haskey(meta, VariableUnit) + uvar = gensym() + push!(where_types, uvar) + push!(kwargs, Expr(:kw, :($a::Union{Nothing, $uvar}), nothing)) + else + push!(kwargs, Expr(:kw, :($a::Union{Nothing, $type}), nothing)) + end dict[:kwargs][getname(var)] = Dict(:value => def, :type => type) else vartype = gensym(:T) @@ -190,7 +172,7 @@ end function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; def = nothing, indices::Union{Vector{UnitRange{Int}}, Nothing} = nothing, - meta = nothing, type::Type = Real) + type::Type = Real, meta = Dict{DataType, Expr}()) metatypes = [(:connection_type, VariableConnectType), (:description, VariableDescription), (:unit, VariableUnit), @@ -215,12 +197,12 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; end Expr(:(::), a, type) => begin type = getfield(mod, type) - parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; def, type) + parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; def, type, meta) end Expr(:(::), Expr(:call, a, b), type) => begin type = getfield(mod, type) def = _type_check!(def, a, type, varclass) - parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; def, type) + parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; def, type, meta) end Expr(:call, a, b) => begin var = generate_var!(dict, a, b, varclass, mod; indices, type) @@ -232,7 +214,7 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; Base.remove_linenums!(b) def, meta = parse_default(mod, b) var, def, _ = parse_variable_def!( - dict, mod, a, varclass, kwargs, where_types; def, meta, type) + dict, mod, a, varclass, kwargs, where_types; def, type, meta) if dict[varclass] isa Vector dict[varclass][1][getname(var)][:default] = def else @@ -257,7 +239,7 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; Expr(:tuple, a, b) => begin meta = parse_metadata(mod, b) var, def, _ = parse_variable_def!( - dict, mod, a, varclass, kwargs, where_types; meta, type) + dict, mod, a, varclass, kwargs, where_types, meta; type, meta) if meta !== nothing for (type, key) in metatypes if (mt = get(meta, key, nothing)) !== nothing @@ -277,7 +259,7 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; Expr(:ref, a, b...) => begin indices = map(i -> UnitRange(i.args[2], i.args[end]), b) parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; - def, indices, type) + def, indices, type, meta) end _ => error("$arg cannot be parsed") end @@ -635,6 +617,14 @@ function parse_variable_arg!(exprs, vs, dict, mod, arg, varclass, kwargs, where_ push!(exprs, ex) end +function convert_units(varunits::DynamicQuantities.Quantity, value) + DynamicQuantities.ustrip(DynamicQuantities.uconvert(DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) +end + +function convert_units(varunits::Unitful.FreeUnits, value) + Unitful.ustrip(varunits, value) +end + function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) vv, def, metadata_with_exprs = parse_variable_def!( dict, mod, arg, varclass, kwargs, where_types) @@ -646,7 +636,15 @@ function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) $name = if $name === nothing $setdefault($vv, $def) else - $setdefault($vv, $ustrip($unit, $name)) + try + $setdefault($vv, $convert_units($unit, $name)) + catch e + if isa(e, DynamicQuantities.DimensionError) || isa(e, Unitful.DimensionError) + error("Unable to convert units for \'"*string(:($$vv))*"\'") + else + rethrow(e) + end + end end end else From f521f6d8e632867014f771a0e2dbe7202681ef3f Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 10:45:08 -0700 Subject: [PATCH 04/12] Missed a spot --- src/systems/model_parsing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index a619c6887d..297d4ab75b 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -239,7 +239,7 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; Expr(:tuple, a, b) => begin meta = parse_metadata(mod, b) var, def, _ = parse_variable_def!( - dict, mod, a, varclass, kwargs, where_types, meta; type, meta) + dict, mod, a, varclass, kwargs, where_types; type, meta) if meta !== nothing for (type, key) in metatypes if (mt = get(meta, key, nothing)) !== nothing From a347d10d10b21d9f7ad0c06a2919a0d1e046c6b3 Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 10:45:27 -0700 Subject: [PATCH 05/12] Fix error reporting Interpolate types so the modules don't need to be imported add MethodError case --- src/systems/model_parsing.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 297d4ab75b..db2771691f 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -639,8 +639,10 @@ function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) try $setdefault($vv, $convert_units($unit, $name)) catch e - if isa(e, DynamicQuantities.DimensionError) || isa(e, Unitful.DimensionError) + if isa(e, $(DynamicQuantities.DimensionError)) || isa(e, $(Unitful.DimensionError)) error("Unable to convert units for \'"*string(:($$vv))*"\'") + elseif isa(e, MethodError) + error("No or invalid units provided for \'"*string(:($$vv))*"\'") else rethrow(e) end From 8b0733a56d6a982fac44491d41dffe683c46b9ad Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 10:46:36 -0700 Subject: [PATCH 06/12] Add tests for new functionality --- test/dq_units.jl | 17 +++++++++++++++++ test/units.jl | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/test/dq_units.jl b/test/dq_units.jl index 75bbd4c4a9..2ef909f850 100644 --- a/test/dq_units.jl +++ b/test/dq_units.jl @@ -157,3 +157,20 @@ maj2 = MassActionJump(γ, [I => 1], [I => -1, R => 1]) maj1 = MassActionJump(2.0, [0 => 1], [S => 1]) maj2 = MassActionJump(γ, [S => 1], [S => -1]) @named js4 = JumpSystem([maj1, maj2], ModelingToolkit.t_nounits, [S], [β, γ]) + +@mtkmodel ParamTest begin + @parameters begin + a, [unit=u"m"] + end + @variables begin + b(t), [unit=u"kg"] + end +end + +@named sys = ParamTest() + +@named sys = ParamTest(a=3.0u"cm") +@test ModelingToolkit.getdefault(sys.a) ≈ 0.03 + +@test_throws ErrorException ParamTest(;name=:t, a=1.0) +@test_throws ErrorException ParamTest(;name=:t, a=1.0u"s") diff --git a/test/units.jl b/test/units.jl index 033a64c0e3..ebcf1e9a19 100644 --- a/test/units.jl +++ b/test/units.jl @@ -192,3 +192,20 @@ maj2 = MassActionJump(γ, [I => 1], [I => -1, R => 1]) maj1 = MassActionJump(2.0, [0 => 1], [S => 1]) maj2 = MassActionJump(γ, [S => 1], [S => -1]) @named js4 = JumpSystem([maj1, maj2], t, [S], [β, γ]) + +@mtkmodel ParamTest begin + @parameters begin + a, [unit=u"m"] + end + @variables begin + b(t), [unit=u"kg"] + end +end + +@named sys = ParamTest() + +@named sys = ParamTest(a=3.0u"cm") +@test ModelingToolkit.getdefault(sys.a) ≈ 0.03 + +@test_throws ErrorException ParamTest(;name=:t, a=1.0) +@test_throws ErrorException ParamTest(;name=:t, a=1.0u"s") From db13f03a3bd41a7125855ce252882fdf16a8b76c Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 10:47:10 -0700 Subject: [PATCH 07/12] Update test for new functionality Values with units must provide units when supplied. --- test/model_parsing.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 9236dfc975..84e3ed38e5 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -53,8 +53,9 @@ end end end -@named p = Pin(; v = π) -@test getdefault(p.v) == π +@named p = Pin(; v = π * u"V") + +@test getdefault(p.v) ≈ π @test Pin.isconnector == true @mtkmodel OnePort begin From 63f853ad3d81133deec7a3b885f6714b7c638a02 Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 11:36:49 -0700 Subject: [PATCH 08/12] Fix more tests for new behavior. --- test/model_parsing.jl | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 84e3ed38e5..8442461897 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1,7 +1,8 @@ using ModelingToolkit, Test using ModelingToolkit: get_connector_type, get_defaults, get_gui_metadata, get_systems, get_ps, getdefault, getname, readable_code, - scalarize, symtype, VariableDescription, RegularConnector + scalarize, symtype, VariableDescription, RegularConnector, + get_unit using URIs: URI using Distributions using DynamicQuantities, OrdinaryDiffEq @@ -106,14 +107,14 @@ end @parameters begin C, [unit = u"F"] end - @extend OnePort(; v = 0.0) + @extend OnePort(; v = 0.0u"V") @icon "https://upload.wikimedia.org/wikipedia/commons/7/78/Capacitor_symbol.svg" @equations begin D(v) ~ i / C end end -@named capacitor = Capacitor(C = 10, v = 10.0) +@named capacitor = Capacitor(C = 10u"F", v = 10.0u"V") @test getdefault(capacitor.v) == 10.0 @mtkmodel Voltage begin @@ -128,9 +129,9 @@ end @mtkmodel RC begin @structural_parameters begin - R_val = 10 - C_val = 10 - k_val = 10 + R_val = 10u"Ω" + C_val = 10u"F" + k_val = 10u"V" end @components begin resistor = Resistor(; R = R_val) @@ -148,9 +149,9 @@ end end end -C_val = 20 -R_val = 20 -res__R = 100 +C_val = 20u"F" +R_val = 20u"Ω" +res__R = 100u"Ω" @mtkbuild rc = RC(; C_val, R_val, resistor.R = res__R) prob = ODEProblem(rc, [], (0, 1e9)) sol = solve(prob, Rodas5P()) @@ -161,11 +162,11 @@ resistor = getproperty(rc, :resistor; namespace = false) @test getname(rc.resistor.R) === getname(resistor.R) @test getname(rc.resistor.v) === getname(resistor.v) # Test that `resistor.R` overrides `R_val` in the argument. -@test getdefault(rc.resistor.R) == res__R != R_val +@test getdefault(rc.resistor.R) * get_unit(rc.resistor.R) == res__R != R_val # Test that `C_val` passed via argument is set as default of C. -@test getdefault(rc.capacitor.C) == C_val +@test getdefault(rc.capacitor.C) * get_unit(rc.capacitor.C) == C_val # Test that `k`'s default value is unchanged. -@test getdefault(rc.constant.k) == RC.structure[:kwargs][:k_val][:value] +@test getdefault(rc.constant.k) * get_unit(rc.constant.k) == eval(RC.structure[:kwargs][:k_val][:value]) @test getdefault(rc.capacitor.v) == 0.0 @test get_gui_metadata(rc.resistor).layout == Resistor.structure[:icon] == From c7bd8da2dadef7f99d74121435ad5e444e3fbe3b Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 11:37:11 -0700 Subject: [PATCH 09/12] Removue unused code --- test/model_parsing.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 8442461897..370c82c4ef 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -78,7 +78,6 @@ end @test OnePort.isconnector == false -resistor_log = "$(@__DIR__)/logo/resistor.svg" @mtkmodel Resistor begin @extend v, i = oneport = OnePort() @parameters begin From 8ab6bfe3709bdc9f5859b4ac23cbe949a6b565f0 Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 11:41:19 -0700 Subject: [PATCH 10/12] Run formatter --- src/systems/model_parsing.jl | 17 +++++++++++------ test/dq_units.jl | 10 +++++----- test/model_parsing.jl | 3 ++- test/units.jl | 10 +++++----- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index db2771691f..6dbdf4ebd5 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -197,12 +197,14 @@ function parse_variable_def!(dict, mod, arg, varclass, kwargs, where_types; end Expr(:(::), a, type) => begin type = getfield(mod, type) - parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; def, type, meta) + parse_variable_def!( + dict, mod, a, varclass, kwargs, where_types; def, type, meta) end Expr(:(::), Expr(:call, a, b), type) => begin type = getfield(mod, type) def = _type_check!(def, a, type, varclass) - parse_variable_def!(dict, mod, a, varclass, kwargs, where_types; def, type, meta) + parse_variable_def!( + dict, mod, a, varclass, kwargs, where_types; def, type, meta) end Expr(:call, a, b) => begin var = generate_var!(dict, a, b, varclass, mod; indices, type) @@ -618,7 +620,8 @@ function parse_variable_arg!(exprs, vs, dict, mod, arg, varclass, kwargs, where_ end function convert_units(varunits::DynamicQuantities.Quantity, value) - DynamicQuantities.ustrip(DynamicQuantities.uconvert(DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) + DynamicQuantities.ustrip(DynamicQuantities.uconvert( + DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) end function convert_units(varunits::Unitful.FreeUnits, value) @@ -639,10 +642,12 @@ function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) try $setdefault($vv, $convert_units($unit, $name)) catch e - if isa(e, $(DynamicQuantities.DimensionError)) || isa(e, $(Unitful.DimensionError)) - error("Unable to convert units for \'"*string(:($$vv))*"\'") + if isa(e, $(DynamicQuantities.DimensionError)) || + isa(e, $(Unitful.DimensionError)) + error("Unable to convert units for \'" * string(:($$vv)) * "\'") elseif isa(e, MethodError) - error("No or invalid units provided for \'"*string(:($$vv))*"\'") + error("No or invalid units provided for \'" * string(:($$vv)) * + "\'") else rethrow(e) end diff --git a/test/dq_units.jl b/test/dq_units.jl index 2ef909f850..6cf855fcc8 100644 --- a/test/dq_units.jl +++ b/test/dq_units.jl @@ -160,17 +160,17 @@ maj2 = MassActionJump(γ, [S => 1], [S => -1]) @mtkmodel ParamTest begin @parameters begin - a, [unit=u"m"] + a, [unit = u"m"] end @variables begin - b(t), [unit=u"kg"] + b(t), [unit = u"kg"] end end @named sys = ParamTest() -@named sys = ParamTest(a=3.0u"cm") +@named sys = ParamTest(a = 3.0u"cm") @test ModelingToolkit.getdefault(sys.a) ≈ 0.03 -@test_throws ErrorException ParamTest(;name=:t, a=1.0) -@test_throws ErrorException ParamTest(;name=:t, a=1.0u"s") +@test_throws ErrorException ParamTest(; name = :t, a = 1.0) +@test_throws ErrorException ParamTest(; name = :t, a = 1.0u"s") diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 370c82c4ef..352c2ee3d4 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -165,7 +165,8 @@ resistor = getproperty(rc, :resistor; namespace = false) # Test that `C_val` passed via argument is set as default of C. @test getdefault(rc.capacitor.C) * get_unit(rc.capacitor.C) == C_val # Test that `k`'s default value is unchanged. -@test getdefault(rc.constant.k) * get_unit(rc.constant.k) == eval(RC.structure[:kwargs][:k_val][:value]) +@test getdefault(rc.constant.k) * get_unit(rc.constant.k) == + eval(RC.structure[:kwargs][:k_val][:value]) @test getdefault(rc.capacitor.v) == 0.0 @test get_gui_metadata(rc.resistor).layout == Resistor.structure[:icon] == diff --git a/test/units.jl b/test/units.jl index ebcf1e9a19..6e07a66d13 100644 --- a/test/units.jl +++ b/test/units.jl @@ -195,17 +195,17 @@ maj2 = MassActionJump(γ, [S => 1], [S => -1]) @mtkmodel ParamTest begin @parameters begin - a, [unit=u"m"] + a, [unit = u"m"] end @variables begin - b(t), [unit=u"kg"] + b(t), [unit = u"kg"] end end @named sys = ParamTest() -@named sys = ParamTest(a=3.0u"cm") +@named sys = ParamTest(a = 3.0u"cm") @test ModelingToolkit.getdefault(sys.a) ≈ 0.03 -@test_throws ErrorException ParamTest(;name=:t, a=1.0) -@test_throws ErrorException ParamTest(;name=:t, a=1.0u"s") +@test_throws ErrorException ParamTest(; name = :t, a = 1.0) +@test_throws ErrorException ParamTest(; name = :t, a = 1.0u"s") From 914a209bfe1c58a79941400097e6a67e06b3e51f Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 14:04:30 -0700 Subject: [PATCH 11/12] Support typed arrays --- src/systems/model_parsing.jl | 16 +++++++++++++++- test/dq_units.jl | 11 +++++++++++ test/units.jl | 11 +++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 6dbdf4ebd5..2fa4e13f7a 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -160,7 +160,11 @@ function update_kwargs_and_metadata!(dict, kwargs, a, def, indices, type, var, Expr(:(::), a, Expr(:curly, :Union, :Nothing, Expr(:curly, :AbstractArray, vartype))), nothing)) - push!(where_types, :($vartype <: $type)) + if !isnothing(meta) && haskey(meta, VariableUnit) + push!(where_types, vartype) + else + push!(where_types, :($vartype <: $type)) + end dict[:kwargs][getname(var)] = Dict(:value => def, :type => AbstractArray{type}) end if dict[varclass] isa Vector @@ -624,10 +628,20 @@ function convert_units(varunits::DynamicQuantities.Quantity, value) DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) end +function convert_units(varunits::DynamicQuantities.Quantity, value::AbstractArray{T}) where T + DynamicQuantities.ustrip.(DynamicQuantities.uconvert.( + DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) +end + function convert_units(varunits::Unitful.FreeUnits, value) Unitful.ustrip(varunits, value) end +function convert_units(varunits::Unitful.FreeUnits, value::AbstractArray{T}) where T + Unitful.ustrip.(varunits, value) +end + + function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) vv, def, metadata_with_exprs = parse_variable_def!( dict, mod, arg, varclass, kwargs, where_types) diff --git a/test/dq_units.jl b/test/dq_units.jl index 6cf855fcc8..76a9c5aa11 100644 --- a/test/dq_units.jl +++ b/test/dq_units.jl @@ -174,3 +174,14 @@ end @test_throws ErrorException ParamTest(; name = :t, a = 1.0) @test_throws ErrorException ParamTest(; name = :t, a = 1.0u"s") + +@mtkmodel ArrayParamTest begin + @parameters begin + a[1:2], [unit = u"m"] + end +end + +@named sys = ArrayParamTest() + +@named sys = ArrayParamTest(a = [1.0, 3.0]u"cm") +@test ModelingToolkit.getdefault(sys.a) ≈ [0.01, 0.03] diff --git a/test/units.jl b/test/units.jl index 6e07a66d13..8d7f9e451e 100644 --- a/test/units.jl +++ b/test/units.jl @@ -209,3 +209,14 @@ end @test_throws ErrorException ParamTest(; name = :t, a = 1.0) @test_throws ErrorException ParamTest(; name = :t, a = 1.0u"s") + +@mtkmodel ArrayParamTest begin + @parameters begin + a[1:2], [unit = u"m"] + end +end + +@named sys = ArrayParamTest() + +@named sys = ArrayParamTest(a = [1.0, 3.0]u"cm") +@test ModelingToolkit.getdefault(sys.a) ≈ [0.01, 0.03] From 49cf9efcf25158f5d8fcf4be1c61b08db9b5e7ff Mon Sep 17 00:00:00 2001 From: contradict Date: Thu, 1 Aug 2024 14:27:56 -0700 Subject: [PATCH 12/12] Run formatter --- src/systems/model_parsing.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 2fa4e13f7a..5df951a566 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -628,7 +628,8 @@ function convert_units(varunits::DynamicQuantities.Quantity, value) DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) end -function convert_units(varunits::DynamicQuantities.Quantity, value::AbstractArray{T}) where T +function convert_units( + varunits::DynamicQuantities.Quantity, value::AbstractArray{T}) where {T} DynamicQuantities.ustrip.(DynamicQuantities.uconvert.( DynamicQuantities.SymbolicUnits.as_quantity(varunits), value)) end @@ -637,11 +638,10 @@ function convert_units(varunits::Unitful.FreeUnits, value) Unitful.ustrip(varunits, value) end -function convert_units(varunits::Unitful.FreeUnits, value::AbstractArray{T}) where T +function convert_units(varunits::Unitful.FreeUnits, value::AbstractArray{T}) where {T} Unitful.ustrip.(varunits, value) end - function parse_variable_arg(dict, mod, arg, varclass, kwargs, where_types) vv, def, metadata_with_exprs = parse_variable_def!( dict, mod, arg, varclass, kwargs, where_types)