From 756d80dea9f1bf0e6662d4c86de988f6e2284a8d Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Wed, 2 Apr 2025 08:41:32 +0000 Subject: [PATCH 1/2] feat(mtkmodel): provision to specify the type of System --- docs/src/basics/MTKLanguage.md | 39 ++++++++++++++++++++++++++++------ src/systems/model_parsing.jl | 23 ++++++++++++++------ test/model_parsing.jl | 15 +++++++++++++ 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/docs/src/basics/MTKLanguage.md b/docs/src/basics/MTKLanguage.md index 2bcec99b6a..e91f2bcb67 100644 --- a/docs/src/basics/MTKLanguage.md +++ b/docs/src/basics/MTKLanguage.md @@ -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? @@ -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 @@ -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 @@ -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. @@ -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: @@ -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() diff --git a/src/systems/model_parsing.jl b/src/systems/model_parsing.jl index 4632c1b889..195b02118e 100644 --- a/src/systems/model_parsing.jl +++ b/src/systems/model_parsing.jl @@ -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 @@ -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 @@ -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}(), @@ -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}}())) @@ -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 diff --git a/test/model_parsing.jl b/test/model_parsing.jl index 62c19d2055..e8464707de 100644 --- a/test/model_parsing.jl +++ b/test/model_parsing.jl @@ -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 From 070659d488e4403724b6995306a70b36b43a25fe Mon Sep 17 00:00:00 2001 From: Venkateshprasad <32921645+ven-k@users.noreply.github.com> Date: Fri, 4 Apr 2025 02:51:43 +0000 Subject: [PATCH 2/2] test: rename test to not use `System` System is an exported function. And now it is used to build ODESystem by default --- test/initializationsystem.jl | 4 ++-- test/split_parameters.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/initializationsystem.jl b/test/initializationsystem.jl index 0f40d4eaf1..4ade3481cb 100644 --- a/test/initializationsystem.jl +++ b/test/initializationsystem.jl @@ -216,7 +216,7 @@ end end end -@mtkmodel System begin +@mtkmodel HydraulicSystem begin @components begin res₁ = Orifice(p′ = 300e5) res₂ = Orifice(p′ = 0) @@ -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) diff --git a/test/split_parameters.jl b/test/split_parameters.jl index 1052f4ad27..18fdb49a48 100644 --- a/test/split_parameters.jl +++ b/test/split_parameters.jl @@ -285,7 +285,7 @@ end end end - @mtkmodel System begin + @mtkmodel ApexSystem begin @components begin subsys = SubSystem() end @@ -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))