Skip to content

MTKModel: Provision to specify the type of System #3526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 21, 2025
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
39 changes: 33 additions & 6 deletions docs/src/basics/MTKLanguage.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ equations.
### [Defining components with `@mtkmodel`](@id mtkmodel)

`@mtkmodel` is a convenience macro to define components. It returns
`ModelingToolkit.Model`, which includes a constructor that returns the ODESystem, a
`structure` dictionary with metadata, and flag `isconnector` which is set to `false`.
`ModelingToolkit.Model`, which includes a system constructor (`ODESystem` by
default), a `structure` dictionary with metadata, and flag `isconnector` which is
set to `false`.

### What can an MTK-Model definition have?

Expand All @@ -26,7 +27,7 @@ equations.
- `@description`: for describing the whole system with a human-readable string
- `@components`: for listing sub-components of the system
- `@constants`: for declaring constants
- `@defaults`: for passing `defaults` to ODESystem
- `@defaults`: for passing `defaults` to the system
- `@equations`: for the list of equations
- `@extend`: for extending a base system and unpacking its unknowns
- `@icon` : for embedding the model icon
Expand Down Expand Up @@ -196,7 +197,7 @@ getdefault(model_c3.model_a.k_array[2])
#### `@defaults` begin block

- Default values can be passed as pairs.
- This is equivalent to passing `defaults` argument to `ODESystem`.
- This is equivalent to passing `defaults` argument to the system.

#### `@continuous_events` begin block

Expand Down Expand Up @@ -256,6 +257,32 @@ end

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

### Setting the type of system:

By default `@mtkmodel` returns an ODESystem. Different types of system can be
defined with the following syntax:

```
@mtkmodel ModelName::SystemType begin
...
end

```

Example:

```@example mtkmodel-example
@mtkmodel Float2Bool::DiscreteSystem begin
@variables begin
u(t)::Float64
y(t)::Bool
end
@equations begin
y ~ u != 0
end
end
```

## Connectors

Connectors are special models that can be used to connect different components together.
Expand All @@ -270,7 +297,7 @@ MTK provides 3 distinct connectors:
### [Defining connectors with `@connector`](@id connector)

`@connector` returns `ModelingToolkit.Model`. It includes a constructor that returns
a connector ODESystem, a `structure` dictionary with metadata, and flag `isconnector`
a connector system (`ODESystem` by default), a `structure` dictionary with metadata, and flag `isconnector`
which is set to `true`.

A simple connector can be defined with syntax similar to following example:
Expand Down Expand Up @@ -498,7 +525,7 @@ end

## Build structurally simplified models:

`@mtkbuild` builds an instance of a component and returns a structurally simplied `ODESystem`.
`@mtkbuild` builds an instance of a component and returns a structurally simplied system.

```julia
@mtkbuild sys = CustomModel()
Expand Down
23 changes: 17 additions & 6 deletions src/systems/model_parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ModelingToolkit component or connector with metadata
$(FIELDS)
"""
struct Model{F, S}
"""The constructor that returns ODESystem."""
"""The constructor that returns System."""
f::F
"""
The dictionary with metadata like keyword arguments (:kwargs), base
Expand All @@ -29,8 +29,8 @@ Base.parentmodule(m::Model) = parentmodule(m.f)
for f in (:connector, :mtkmodel)
isconnector = f == :connector ? true : false
@eval begin
macro $f(name::Symbol, body)
esc($(:_model_macro)(__module__, name, body, $isconnector))
macro $f(fullname::Union{Expr, Symbol}, body)
esc($(:_model_macro)(__module__, fullname, body, $isconnector))
end
end
end
Expand All @@ -41,7 +41,16 @@ function flatten_equations(eqs::Vector{Union{Equation, Vector{Equation}}})
foldl(flatten_equations, eqs; init = Equation[])
end

function _model_macro(mod, name, expr, isconnector)
function _model_macro(mod, fullname::Union{Expr, Symbol}, expr, isconnector)
if fullname isa Symbol
name, type = fullname, :System
else
if fullname.head == :(::)
name, type = fullname.args
else
error("`$fullname` is not a valid name.")
end
end
exprs = Expr(:block)
dict = Dict{Symbol, Any}(
:constants => Dict{Symbol, Dict}(),
Expand All @@ -61,7 +70,9 @@ function _model_macro(mod, name, expr, isconnector)

push!(exprs.args, :(variables = []))
push!(exprs.args, :(parameters = []))
push!(exprs.args, :(systems = ODESystem[]))
# We build `System` by default; vectors can't be created for `System` as it is
# a function.
push!(exprs.args, :(systems = ModelingToolkit.AbstractSystem[]))
push!(exprs.args, :(equations = Union{Equation, Vector{Equation}}[]))
push!(exprs.args, :(defaults = Dict{Num, Union{Number, Symbol, Function}}()))

Expand Down Expand Up @@ -114,7 +125,7 @@ function _model_macro(mod, name, expr, isconnector)
@inline pop_structure_dict!.(
Ref(dict), [:constants, :defaults, :kwargs, :structural_parameters])

sys = :($ODESystem($(flatten_equations)(equations), $iv, variables, parameters;
sys = :($type($(flatten_equations)(equations), $iv, variables, parameters;
name, description = $description, systems, gui_metadata = $gui_metadata, defaults))

if length(ext) == 0
Expand Down
4 changes: 2 additions & 2 deletions test/initializationsystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ end
end
end

@mtkmodel System begin
@mtkmodel HydraulicSystem begin
@components begin
res₁ = Orifice(p′ = 300e5)
res₂ = Orifice(p′ = 0)
Expand All @@ -234,7 +234,7 @@ end
end
end

@mtkbuild sys = System()
@mtkbuild sys = HydraulicSystem()
initprob = ModelingToolkit.InitializationProblem(sys, 0.0)
conditions = getfield.(equations(initprob.f.sys), :rhs)

Expand Down
15 changes: 15 additions & 0 deletions test/model_parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1011,3 +1011,18 @@ end
@test any(isequal(u), vars)
end
end

@testset "Specify the type of system" begin
@mtkmodel Float2Bool::DiscreteSystem begin
@variables begin
u(t)::Float64
y(t)::Bool
end
@equations begin
y ~ u != 0
end
end

@named sys = Float2Bool()
@test typeof(sys) == DiscreteSystem
end
4 changes: 2 additions & 2 deletions test/split_parameters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ end
end
end

@mtkmodel System begin
@mtkmodel ApexSystem begin
@components begin
subsys = SubSystem()
end
Expand All @@ -300,7 +300,7 @@ end
end
end

@named sys = System()
@named sys = ApexSystem()
sysref = complete(sys)
sys2 = complete(sys; split = true, flatten = false)
ps = Set(full_parameters(sys2))
Expand Down
Loading