Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/src/basics/MTKLanguage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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.
Expand Down
29 changes: 28 additions & 1 deletion src/systems/model_parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down Expand Up @@ -127,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])
Expand All @@ -145,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
Expand Down Expand Up @@ -678,6 +688,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
Expand Down Expand Up @@ -1254,6 +1266,21 @@ 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
dict[:metadata][a] = get_var(mod, b)
end
Expr(:call, :(=>), a, b) => begin
dict[:metadata][a] = get_var(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)
Expand Down
44 changes: 44 additions & 0 deletions test/model_parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1045,3 +1045,47 @@ 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
struct MyBool end
struct NewInt end

@mtkmodel TestMetadataModel begin
@metadata begin
Author = "Test Author"
MyVersion = "1.0.0"
License = "MIT"
Category => "example"
Tags = ["test", "demo", "metadata"]
MyBool => false
NewInt => 1
end

@parameters begin
k = 1.0
end

@variables begin
x(t)
y(t)
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
@test ModelingToolkit.getmetadata(test_model, MyBool, nothing) === false
@test ModelingToolkit.getmetadata(test_model, NewInt, nothing) === 1
end
Loading