From 3c428d36d8d109207ac1c0722fb9970e97eb7d1d Mon Sep 17 00:00:00 2001 From: DhairyaLGandhi Date: Wed, 13 Aug 2025 16:43:57 +0530 Subject: [PATCH 1/7] chore: add at-metadata support for systems --- src/systems/model_parsing.jl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 61edc394b2..8e9415761b 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -55,7 +55,8 @@ function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector) dict = Dict{Symbol, Any}( :defaults => Dict{Symbol, Any}(), :kwargs => Dict{Symbol, Dict}(), - :structural_parameters => Dict{Symbol, Dict}() + :structural_parameters => Dict{Symbol, Dict}(), + :metadata => Dict{Symbol, Any}() ) comps = Union{Symbol, Expr}[] ext = [] @@ -678,6 +679,8 @@ function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps, c_evts, d_evts, parse_costs!(costs, dict, body) elseif mname == Symbol("@consolidate") parse_consolidate!(body, dict) + elseif mname == Symbol("@metadata") + parse_metadata_block!(body, dict, mod) else error("$mname is not handled.") end @@ -1254,6 +1257,24 @@ function parse_description!(body, dict) end end +function parse_metadata_block!(body, dict, mod) + Base.remove_linenums!(body) + for arg in body.args + MLStyle.@match arg begin + Expr(:(=), a, b) => begin + @show esc(b) + # dict[:metadata][a] = get_var(mod, b) + dict[:metadata][a] = Core.eval(mod, b) + end + Expr(:call, :(=>), a, b) => begin + # dict[:metadata][a] = get_var(mod, b) + dict[:metadata][a] = Core.eval(mod, b) + end + _ => error("Invalid metadata entry: $arg. Expected key = value or key => value format.") + end + end +end + ### Parsing Components: function component_args!(a, b, varexpr, kwargs; index_name = nothing) From 3329f26264c542750569daabbade11bd65e9f31e Mon Sep 17 00:00:00 2001 From: DhairyaLGandhi Date: Wed, 13 Aug 2025 16:50:25 +0530 Subject: [PATCH 2/7] chore: rm unused lines --- src/systems/model_parsing.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 8e9415761b..9389da412d 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -1262,13 +1262,10 @@ function parse_metadata_block!(body, dict, mod) for arg in body.args MLStyle.@match arg begin Expr(:(=), a, b) => begin - @show esc(b) - # dict[:metadata][a] = get_var(mod, b) - dict[:metadata][a] = Core.eval(mod, b) + dict[:metadata][a] = get_var(mod, b) end Expr(:call, :(=>), a, b) => begin - # dict[:metadata][a] = get_var(mod, b) - dict[:metadata][a] = Core.eval(mod, b) + dict[:metadata][a] = get_var(mod, b) end _ => error("Invalid metadata entry: $arg. Expected key = value or key => value format.") end From 521d58a47b02d8424f1b0e4aa43471bf4e14343a Mon Sep 17 00:00:00 2001 From: DhairyaLGandhi Date: Thu, 21 Aug 2025 20:39:14 +0530 Subject: [PATCH 3/7] chore: add setmetadata to exprs while returning --- src/systems/model_parsing.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 9389da412d..699cfee8fd 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -128,6 +128,7 @@ function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector) consolidate = get(dict, :consolidate, default_consolidate) description = get(dict, :description, "") + model_meta = get(dict, :metadata, Dict{Symbol, Any}()) @inline pop_structure_dict!.( Ref(dict), [:defaults, :kwargs, :structural_parameters]) @@ -146,6 +147,14 @@ function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector) isconnector && push!(exprs.args, :($Setfield.@set!(var"#___sys___".connector_type=$connector_type(var"#___sys___")))) + meta_exprs = quote + for (k, v) in $model_meta + var"#___sys___" = setmetadata(var"#___sys___", $get_var($mod, k), v) + end + end + push!(exprs.args, meta_exprs) + push!(exprs.args, :(var"#___sys___")) + f = if length(where_types) == 0 :($(Symbol(:__, name, :__))(; name, $(kwargs...)) = $exprs) else From e588a254ad5a08d1a71c979ccdd46fd124e5c285 Mon Sep 17 00:00:00 2001 From: DhairyaLGandhi Date: Thu, 21 Aug 2025 20:39:39 +0530 Subject: [PATCH 4/7] test: metadata accessible from model --- test/model_parsing.jl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index c48628b007..b63577df15 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1045,3 +1045,41 @@ end @test Example.structure[:constraints] == ["(EvalAt(0.3))(x) ~ 3", "y ≲ 4"] @test Example.structure[:costs] == ["x + y", "(EvalAt(1))(y) ^ 2"] end + +@testset "Model Level Metadata" begin + struct Author end + struct MyVersion end + struct License end + struct Category end + struct Tags end + + @mtkmodel TestMetadataModel begin + @metadata begin + Author = "Test Author" + MyVersion = "1.0.0" + License = "MIT" + Category => "example" + Tags = ["test", "demo", "metadata"] + end + + @parameters begin + k = 1.0, [description = "Gain parameter"] + end + + @variables begin + x(t), [description = "State variable"] + y(t), [description = "Output variable"] + end + + @equations begin + D(x) ~ -k * x + y ~ x + end + end + @named test_model = TestMetadataModel() + + struct UnknownMetaKey end + @test ModelingToolkit.getmetadata(test_model, Author, nothing) == "Test Author" + @test ModelingToolkit.getmetadata(test_model, MyVersion, nothing) == "1.0.0" + @test ModelingToolkit.getmetadata(test_model, UnknownMetaKey, nothing) === nothing +end From 1a46ff6acbf77434633cf59a8a463f9f3ab915f6 Mon Sep 17 00:00:00 2001 From: DhairyaLGandhi Date: Mon, 25 Aug 2025 18:53:58 +0530 Subject: [PATCH 5/7] test: check return types for metadata --- test/model_parsing.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index b63577df15..05f539d3de 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1052,6 +1052,8 @@ end struct License end struct Category end struct Tags end + struct MyBool end + struct NewInt end @mtkmodel TestMetadataModel begin @metadata begin @@ -1060,6 +1062,8 @@ end License = "MIT" Category => "example" Tags = ["test", "demo", "metadata"] + MyBool => false + NewInt => 1 end @parameters begin @@ -1082,4 +1086,6 @@ end @test ModelingToolkit.getmetadata(test_model, Author, nothing) == "Test Author" @test ModelingToolkit.getmetadata(test_model, MyVersion, nothing) == "1.0.0" @test ModelingToolkit.getmetadata(test_model, UnknownMetaKey, nothing) === nothing + @test ModelingToolkit.getmetadata(test_model, MyBool, nothing) === false + @test ModelingToolkit.getmetadata(test_model, NewInt, nothing) === 1 end From a59f7ef4463b39f571b1eba6cc63af1c07ab1d8a Mon Sep 17 00:00:00 2001 From: Dhairya Gandhi Date: Mon, 8 Sep 2025 11:34:35 +0000 Subject: [PATCH 6/7] test: rm description --- test/model_parsing.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 05f539d3de..2c713d4149 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -1067,12 +1067,12 @@ end end @parameters begin - k = 1.0, [description = "Gain parameter"] + k = 1.0 end @variables begin - x(t), [description = "State variable"] - y(t), [description = "Output variable"] + x(t) + y(t) end @equations begin From 71acd76472d2f7a6ca820c2e0549aa63a75ae1d0 Mon Sep 17 00:00:00 2001 From: Dhairya Gandhi Date: Wed, 10 Sep 2025 19:18:44 +0000 Subject: [PATCH 7/7] docs: add metadata block in @mtkmodel --- docs/src/basics/MTKLanguage.md | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/src/basics/MTKLanguage.md b/docs/src/basics/MTKLanguage.md index e0be1c1188..02deff803c 100644 --- a/docs/src/basics/MTKLanguage.md +++ b/docs/src/basics/MTKLanguage.md @@ -31,6 +31,7 @@ set to `false`. - `@equations`: for the list of equations - `@extend`: for extending a base system and unpacking its unknowns - `@icon` : for embedding the model icon + - `@metadata`: for assigning key-value pairs as model level metadata - `@parameters`: for specifying the symbolic parameters - `@structural_parameters`: for specifying non-symbolic parameters - `@variables`: for specifying the unknowns @@ -94,6 +95,16 @@ end v_for_defaults => 2.0 end end + +struct Author end +struct ModelVersion end +@mtkmodel ModelD begin + @description "A component with some metadata." + @metadata begin + Author = "Test Author" + ModelVersion = "1.0.0" + end +end ``` #### `@description` @@ -257,6 +268,30 @@ using ModelingToolkit end ``` +#### `@metadata` begin block + + - Assign key-value pairs as model level metadata. + - The keys must be `DataType` to avoid any key collisions. + - Assignments can be made using either `=` or `=>`. + - Metadata can be retrieved using [`getmetadata`](@ref). + - Metadata can be set using [`setmetadata`](@ref). + +```@example mtkmodel-example +using ModelingToolkit + +struct Author end +struct ModelVersion end + +@mtkmodel MetadataModel begin + @metadata begin + Author = "Test Author" + ModelVersion => "1.0.0" + end +end +@named model = MetadataModel() +getmetadata(model, Author, nothing) == "Test Author" +``` + #### A begin block - Any other Julia operations can be included with dedicated begin blocks.