Skip to content

Commit 3c1eac3

Browse files
Merge pull request #3892 from DhairyaLGandhi/dg/metamacro
Allow system level metadata information
2 parents 003dfa6 + 71acd76 commit 3c1eac3

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

docs/src/basics/MTKLanguage.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ set to `false`.
3131
- `@equations`: for the list of equations
3232
- `@extend`: for extending a base system and unpacking its unknowns
3333
- `@icon` : for embedding the model icon
34+
- `@metadata`: for assigning key-value pairs as model level metadata
3435
- `@parameters`: for specifying the symbolic parameters
3536
- `@structural_parameters`: for specifying non-symbolic parameters
3637
- `@variables`: for specifying the unknowns
@@ -94,6 +95,16 @@ end
9495
v_for_defaults => 2.0
9596
end
9697
end
98+
99+
struct Author end
100+
struct ModelVersion end
101+
@mtkmodel ModelD begin
102+
@description "A component with some metadata."
103+
@metadata begin
104+
Author = "Test Author"
105+
ModelVersion = "1.0.0"
106+
end
107+
end
97108
```
98109

99110
#### `@description`
@@ -257,6 +268,30 @@ using ModelingToolkit
257268
end
258269
```
259270

271+
#### `@metadata` begin block
272+
273+
- Assign key-value pairs as model level metadata.
274+
- The keys must be `DataType` to avoid any key collisions.
275+
- Assignments can be made using either `=` or `=>`.
276+
- Metadata can be retrieved using [`getmetadata`](@ref).
277+
- Metadata can be set using [`setmetadata`](@ref).
278+
279+
```@example mtkmodel-example
280+
using ModelingToolkit
281+
282+
struct Author end
283+
struct ModelVersion end
284+
285+
@mtkmodel MetadataModel begin
286+
@metadata begin
287+
Author = "Test Author"
288+
ModelVersion => "1.0.0"
289+
end
290+
end
291+
@named model = MetadataModel()
292+
getmetadata(model, Author, nothing) == "Test Author"
293+
```
294+
260295
#### A begin block
261296

262297
- Any other Julia operations can be included with dedicated begin blocks.

src/systems/model_parsing.jl

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector)
5555
dict = Dict{Symbol, Any}(
5656
:defaults => Dict{Symbol, Any}(),
5757
:kwargs => Dict{Symbol, Dict}(),
58-
:structural_parameters => Dict{Symbol, Dict}()
58+
:structural_parameters => Dict{Symbol, Dict}(),
59+
:metadata => Dict{Symbol, Any}()
5960
)
6061
comps = Union{Symbol, Expr}[]
6162
ext = []
@@ -127,6 +128,7 @@ function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector)
127128

128129
consolidate = get(dict, :consolidate, default_consolidate)
129130
description = get(dict, :description, "")
131+
model_meta = get(dict, :metadata, Dict{Symbol, Any}())
130132

131133
@inline pop_structure_dict!.(
132134
Ref(dict), [:defaults, :kwargs, :structural_parameters])
@@ -145,6 +147,14 @@ function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector)
145147
isconnector && push!(exprs.args,
146148
:($Setfield.@set!(var"#___sys___".connector_type=$connector_type(var"#___sys___"))))
147149

150+
meta_exprs = quote
151+
for (k, v) in $model_meta
152+
var"#___sys___" = setmetadata(var"#___sys___", $get_var($mod, k), v)
153+
end
154+
end
155+
push!(exprs.args, meta_exprs)
156+
push!(exprs.args, :(var"#___sys___"))
157+
148158
f = if length(where_types) == 0
149159
:($(Symbol(:__, name, :__))(; name, $(kwargs...)) = $exprs)
150160
else
@@ -678,6 +688,8 @@ function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps, c_evts, d_evts,
678688
parse_costs!(costs, dict, body)
679689
elseif mname == Symbol("@consolidate")
680690
parse_consolidate!(body, dict)
691+
elseif mname == Symbol("@metadata")
692+
parse_metadata_block!(body, dict, mod)
681693
else
682694
error("$mname is not handled.")
683695
end
@@ -1254,6 +1266,21 @@ function parse_description!(body, dict)
12541266
end
12551267
end
12561268

1269+
function parse_metadata_block!(body, dict, mod)
1270+
Base.remove_linenums!(body)
1271+
for arg in body.args
1272+
MLStyle.@match arg begin
1273+
Expr(:(=), a, b) => begin
1274+
dict[:metadata][a] = get_var(mod, b)
1275+
end
1276+
Expr(:call, :(=>), a, b) => begin
1277+
dict[:metadata][a] = get_var(mod, b)
1278+
end
1279+
_ => error("Invalid metadata entry: $arg. Expected key = value or key => value format.")
1280+
end
1281+
end
1282+
end
1283+
12571284
### Parsing Components:
12581285

12591286
function component_args!(a, b, varexpr, kwargs; index_name = nothing)

test/model_parsing.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,3 +1045,47 @@ end
10451045
@test Example.structure[:constraints] == ["(EvalAt(0.3))(x) ~ 3", "y ≲ 4"]
10461046
@test Example.structure[:costs] == ["x + y", "(EvalAt(1))(y) ^ 2"]
10471047
end
1048+
1049+
@testset "Model Level Metadata" begin
1050+
struct Author end
1051+
struct MyVersion end
1052+
struct License end
1053+
struct Category end
1054+
struct Tags end
1055+
struct MyBool end
1056+
struct NewInt end
1057+
1058+
@mtkmodel TestMetadataModel begin
1059+
@metadata begin
1060+
Author = "Test Author"
1061+
MyVersion = "1.0.0"
1062+
License = "MIT"
1063+
Category => "example"
1064+
Tags = ["test", "demo", "metadata"]
1065+
MyBool => false
1066+
NewInt => 1
1067+
end
1068+
1069+
@parameters begin
1070+
k = 1.0
1071+
end
1072+
1073+
@variables begin
1074+
x(t)
1075+
y(t)
1076+
end
1077+
1078+
@equations begin
1079+
D(x) ~ -k * x
1080+
y ~ x
1081+
end
1082+
end
1083+
@named test_model = TestMetadataModel()
1084+
1085+
struct UnknownMetaKey end
1086+
@test ModelingToolkit.getmetadata(test_model, Author, nothing) == "Test Author"
1087+
@test ModelingToolkit.getmetadata(test_model, MyVersion, nothing) == "1.0.0"
1088+
@test ModelingToolkit.getmetadata(test_model, UnknownMetaKey, nothing) === nothing
1089+
@test ModelingToolkit.getmetadata(test_model, MyBool, nothing) === false
1090+
@test ModelingToolkit.getmetadata(test_model, NewInt, nothing) === 1
1091+
end

0 commit comments

Comments
 (0)