Skip to content

Commit 5ac4469

Browse files
Merge pull request #2427 from cgutsche/dev-add-event-support-mtkmodel
Add event support mtkmodel
2 parents 1b858f6 + 3ab98b4 commit 5ac4469

File tree

3 files changed

+122
-2
lines changed

3 files changed

+122
-2
lines changed

docs/src/basics/MTKModel_Connector.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ equations.
3232
- `@parameters`: for specifying the symbolic parameters
3333
- `@structural_parameters`: for specifying non-symbolic parameters
3434
- `@variables`: for specifying the unknowns
35+
- `@continuous_events`: for specifying a list of continuous events
36+
- `@discrete_events`: for specifying a list of discrete events
3537

3638
Let's explore these in more detail with the following example:
3739

@@ -182,6 +184,59 @@ getdefault(model_c3.model_a.k_array[2])
182184
- Default values can be passed as pairs.
183185
- This is equivalent to passing `defaults` argument to `ODESystem`.
184186

187+
#### `@continuous_events` begin block
188+
189+
- Defining continuous events as described [here](https://docs.sciml.ai/ModelingToolkit/stable/basics/Events/#Continuous-Events).
190+
- If this block is not defined in the model, no continuous events will be added.
191+
192+
```@example mtkmodel-example
193+
using ModelingToolkit
194+
195+
@mtkmodel M begin
196+
@parameters begin
197+
k
198+
end
199+
@variables begin
200+
x(t)
201+
y(t)
202+
end
203+
@equations begin
204+
x ~ k * D(x)
205+
D(y) ~ -k
206+
end
207+
@continuous_events begin
208+
[x ~ 1.5] => [x ~ 5, y ~ 5]
209+
[t ~ 4] => [x ~ 10]
210+
end
211+
end
212+
```
213+
214+
#### `@discrete_events` begin block
215+
216+
- Defining discrete events as described [here](https://docs.sciml.ai/ModelingToolkit/stable/basics/Events/#Discrete-events-support).
217+
- If this block is not defined in the model, no discrete events will be added.
218+
219+
```@example mtkmodel-example
220+
using ModelingToolkit
221+
222+
@mtkmodel M begin
223+
@parameters begin
224+
k
225+
end
226+
@variables begin
227+
x(t)
228+
y(t)
229+
end
230+
@equations begin
231+
x ~ k * D(x)
232+
D(y) ~ -k
233+
end
234+
@discrete_events begin
235+
(t == 1.5) => [x ~ x + 5, y ~ 5]
236+
end
237+
end
238+
```
239+
185240
#### A begin block
186241

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

src/systems/model_parsing.jl

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ function _model_macro(mod, name, expr, isconnector)
4848
eqs = Expr[]
4949
icon = Ref{Union{String, URI}}()
5050
ps, sps, vs, = [], [], []
51+
c_evts = []
52+
d_evts = []
5153
kwargs = Set()
5254
where_types = Expr[]
5355

@@ -61,7 +63,7 @@ function _model_macro(mod, name, expr, isconnector)
6163
for arg in expr.args
6264
if arg.head == :macrocall
6365
parse_model!(exprs.args, comps, ext, eqs, icon, vs, ps,
64-
sps, dict, mod, arg, kwargs, where_types)
66+
sps, c_evts, d_evts, dict, mod, arg, kwargs, where_types)
6567
elseif arg.head == :block
6668
push!(exprs.args, arg)
6769
elseif arg.head == :if
@@ -116,6 +118,16 @@ function _model_macro(mod, name, expr, isconnector)
116118
isconnector && push!(exprs.args,
117119
:($Setfield.@set!(var"#___sys___".connector_type=$connector_type(var"#___sys___"))))
118120

121+
!(c_evts == []) && push!(exprs.args,
122+
:($Setfield.@set!(var"#___sys___".continuous_events=$SymbolicContinuousCallback.([
123+
$(c_evts...)
124+
]))))
125+
126+
!(d_evts == []) && push!(exprs.args,
127+
:($Setfield.@set!(var"#___sys___".discrete_events=$SymbolicDiscreteCallback.([
128+
$(d_evts...)
129+
]))))
130+
119131
f = if length(where_types) == 0
120132
:($(Symbol(:__, name, :__))(; name, $(kwargs...)) = $exprs)
121133
else
@@ -124,6 +136,7 @@ function _model_macro(mod, name, expr, isconnector)
124136
:($(Symbol(:__, name, :__))(; name, $(kwargs...))), where_types...)
125137
:($f_with_where = $exprs)
126138
end
139+
127140
:($name = $Model($f, $dict, $isconnector))
128141
end
129142

@@ -341,7 +354,7 @@ function get_var(mod::Module, b)
341354
end
342355
end
343356

344-
function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps,
357+
function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps, c_evts, d_evts,
345358
dict, mod, arg, kwargs, where_types)
346359
mname = arg.args[1]
347360
body = arg.args[end]
@@ -359,6 +372,10 @@ function parse_model!(exprs, comps, ext, eqs, icon, vs, ps, sps,
359372
parse_equations!(exprs, eqs, dict, body)
360373
elseif mname == Symbol("@constants")
361374
parse_constants!(exprs, dict, body, mod)
375+
elseif mname == Symbol("@continuous_events")
376+
parse_continuous_events!(c_evts, dict, body)
377+
elseif mname == Symbol("@discrete_events")
378+
parse_discrete_events!(d_evts, dict, body)
362379
elseif mname == Symbol("@icon")
363380
isassigned(icon) && error("This model has more than one icon.")
364381
parse_icon!(body, dict, icon, mod)
@@ -753,6 +770,24 @@ function parse_equations!(exprs, eqs, dict, body)
753770
end
754771
end
755772

773+
function parse_continuous_events!(c_evts, dict, body)
774+
dict[:continuous_events] = []
775+
Base.remove_linenums!(body)
776+
for arg in body.args
777+
push!(c_evts, arg)
778+
push!(dict[:continuous_events], readable_code.(c_evts)...)
779+
end
780+
end
781+
782+
function parse_discrete_events!(d_evts, dict, body)
783+
dict[:discrete_events] = []
784+
Base.remove_linenums!(body)
785+
for arg in body.args
786+
push!(d_evts, arg)
787+
push!(dict[:discrete_events], readable_code.(d_evts)...)
788+
end
789+
end
790+
756791
function parse_icon!(body::String, dict, icon, mod)
757792
icon_dir = get(ENV, "MTK_ICONS_DIR", joinpath(DEPOT_PATH[1], "mtk_icons"))
758793
dict[:icon] = icon[] = if isfile(body)

test/model_parsing.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,36 @@ end
426426
@test A.structure[:components] == [[:cc, :C]]
427427
end
428428

429+
@testset "Event handling in MTKModel" begin
430+
@mtkmodel M begin
431+
@variables begin
432+
x(t)
433+
y(t)
434+
z(t)
435+
end
436+
@equations begin
437+
x ~ -D(x)
438+
D(y) ~ 0
439+
D(z) ~ 0
440+
end
441+
@continuous_events begin
442+
[x ~ 1.5] => [x ~ 5, y ~ 1]
443+
end
444+
@discrete_events begin
445+
(t == 1.5) => [x ~ x + 5, z ~ 2]
446+
end
447+
end
448+
449+
@mtkbuild model = M()
450+
u0 = [model.x => 10, model.y => 0, model.z => 0]
451+
452+
prob = ODEProblem(model, u0, (0, 5.0))
453+
sol = solve(prob, Tsit5(), tstops = [1.5])
454+
455+
@test isequal(sol[model.y][end], 1.0)
456+
@test isequal(sol[model.z][end], 2.0)
457+
end
458+
429459
# Ensure that modules consisting MTKModels with component arrays and icons of
430460
# `Expr` type and `unit` metadata can be precompiled.
431461
module PrecompilationTest

0 commit comments

Comments
 (0)