@@ -9,21 +9,34 @@ illustrate the basic user-facing functionality.
9
9
A much deeper tutorial with forcing functions and sparse Jacobians is below.
10
10
But if you want to just see some code and run, here's an example:
11
11
12
- ``` @example ode
12
+ ``` @example first-mtkmodel
13
13
using ModelingToolkit
14
14
15
- @variables t x(t) # independent and dependent variables
16
- @parameters τ # parameters
17
- @constants h = 1 # constants have an assigned value
18
- D = Differential(t) # define an operator for the differentiation w.r.t. time
19
-
20
- # your first ODE, consisting of a single equation, the equality indicated by ~
21
- @named fol = ODESystem([D(x) ~ (h - x) / τ])
15
+ @variables t
16
+ D = Differential(t)
17
+
18
+ @mtkmodel FOL begin
19
+ @parameters begin
20
+ τ # parameters
21
+ end
22
+ @variables begin
23
+ x(t) # dependent variables
24
+ end
25
+ @structural_parameters begin
26
+ h = 1
27
+ end
28
+ @equations begin
29
+ D(x) ~ (h - x) / τ
30
+ end
31
+ end
22
32
23
33
using DifferentialEquations: solve
24
34
25
- prob = ODEProblem(fol, [x => 0.0], (0.0, 10.0), [τ => 3.0])
26
- # parameter `τ` can be assigned a value, but constant `h` cannot
35
+ @named fol = FOL()
36
+ fol = complete(fol)
37
+
38
+ prob = ODEProblem(fol, [fol.x => 0.0], (0.0, 10.0), [fol.τ => 3.0])
39
+ # parameter `τ` can be assigned a value, but structural parameter `h` cannot'.
27
40
sol = solve(prob)
28
41
29
42
using Plots
@@ -43,48 +56,110 @@ first-order lag element:
43
56
44
57
Here, `` t `` is the independent variable (time), `` x(t) `` is the (scalar) state
45
58
variable, `` f(t) `` is an external forcing function, and `` \tau `` is a
46
- parameter. In MTK, this system can be modelled as follows. For simplicity, we
47
- first set the forcing function to a time-independent value.
59
+ parameter.
60
+ In MTK, this system can be modelled as follows. For simplicity, we
61
+ first set the forcing function to a time-independent value `` h `` . And the
62
+ independent variable `` t `` is automatically added by `` @mtkmodel `` .
48
63
49
64
``` @example ode2
50
65
using ModelingToolkit
51
66
52
- @variables t x(t) # independent and dependent variables
53
- @parameters τ # parameters
54
- @constants h = 1 # constants
55
- D = Differential(t) # define an operator for the differentiation w.r.t. time
67
+ @variables t
68
+ D = Differential(t)
69
+
70
+ @mtkmodel FOL begin
71
+ @parameters begin
72
+ τ # parameters
73
+ end
74
+ @variables begin
75
+ x(t) # dependent variables
76
+ end
77
+ @structural_parameters begin
78
+ h = 1
79
+ end
80
+ @equations begin
81
+ D(x) ~ (h - x) / τ
82
+ end
83
+ end
56
84
57
- # your first ODE, consisting of a single equation, indicated by ~
58
- @named fol_model = ODESystem(D(x) ~ (h - x) / τ )
85
+ @named fol_incomplete = FOL()
86
+ fol = complete(fol_incomplete )
59
87
```
60
88
61
89
Note that equations in MTK use the tilde character (` ~ ` ) as equality sign.
62
- Also note that the ` @named ` macro simply ensures that the symbolic name
63
- matches the name in the REPL. If omitted, you can directly set the ` name ` keyword.
90
+
91
+ ` @named ` creates an instance of ` FOL ` named as ` fol ` . Before creating an
92
+ ODEProblem with ` fol ` run ` complete ` . Once the system is complete, it will no
93
+ longer namespace its subsystems or variables. This is necessary to correctly pass
94
+ the intial values of states and parameters to the ODEProblem.
95
+
96
+ ``` julia
97
+ julia> fol_incomplete. x
98
+ fol_incomplete₊x (t)
99
+
100
+ julia> fol. x
101
+ x (t)
102
+ ```
64
103
65
104
After construction of the ODE, you can solve it using [ DifferentialEquations.jl] ( https://docs.sciml.ai/DiffEqDocs/stable/ ) :
66
105
67
106
``` @example ode2
68
107
using DifferentialEquations
69
108
using Plots
70
109
71
- prob = ODEProblem(fol_model , [x => 0.0], (0.0, 10.0), [τ => 3.0])
110
+ prob = ODEProblem(fol , [fol. x => 0.0], (0.0, 10.0), [fol. τ => 3.0])
72
111
plot(solve(prob))
73
112
```
74
113
75
114
The initial state and the parameter values are specified using a mapping
76
115
from the actual symbolic elements to their values, represented as an array
77
116
of ` Pair ` s, which are constructed using the ` => ` operator.
78
117
118
+ ## Non-DSL way of defining an ODESystem
119
+
120
+ Using ` @mtkmodel ` is the preferred way of defining ODEs with MTK. However, let us
121
+ look at how we can define the same system without ` @mtkmodel ` . This is useful for
122
+ defining PDESystem etc.
123
+
124
+ ``` @example first-mtkmodel
125
+ @variables t x(t) # independent and dependent variables
126
+ @parameters τ # parameters
127
+ @constants h = 1 # constants
128
+ D = Differential(t) # define an operator for the differentiation w.r.t. time
129
+
130
+ # your first ODE, consisting of a single equation, indicated by ~
131
+ @named fol_model = ODESystem(D(x) ~ (h - x) / τ)
132
+ ```
133
+
79
134
## Algebraic relations and structural simplification
80
135
81
136
You could separate the calculation of the right-hand side, by introducing an
82
137
intermediate variable ` RHS ` :
83
138
84
139
``` @example ode2
85
- @variables RHS(t)
86
- @named fol_separate = ODESystem([RHS ~ (h - x) / τ,
87
- D(x) ~ RHS])
140
+ using ModelingToolkit
141
+
142
+ @mtkmodel FOL begin
143
+ @parameters begin
144
+ τ # parameters
145
+ end
146
+ @variables begin
147
+ x(t) # dependent variables
148
+ RHS(t)
149
+ end
150
+ @structural_parameters begin
151
+ h = 1
152
+ end
153
+ begin
154
+ D = Differential(t)
155
+ end
156
+ @equations begin
157
+ RHS ~ (h - x) / τ
158
+ D(x) ~ RHS
159
+ end
160
+ end
161
+
162
+ @named fol_separate = FOL()
88
163
```
89
164
90
165
To directly solve this system, you would have to create a Differential-Algebraic
@@ -94,12 +169,12 @@ transformed into the single ODE we used in the first example above. MTK achieves
94
169
this by structural simplification:
95
170
96
171
``` @example ode2
97
- fol_simplified = structural_simplify(fol_separate)
172
+ fol_simplified = structural_simplify(complete( fol_separate) )
98
173
equations(fol_simplified)
99
174
```
100
175
101
176
``` @example ode2
102
- equations(fol_simplified) == equations(fol_model )
177
+ equations(fol_simplified) == equations(fol )
103
178
```
104
179
105
180
You can extract the equations from a system using ` equations ` (and, in the same
@@ -114,9 +189,12 @@ along with the state variable. Note that this has to be requested explicitly,
114
189
through:
115
190
116
191
``` @example ode2
117
- prob = ODEProblem(fol_simplified, [x => 0.0], (0.0, 10.0), [τ => 3.0])
192
+ prob = ODEProblem(fol_simplified,
193
+ [fol_simplified.x => 0.0],
194
+ (0.0, 10.0),
195
+ [fol_simplified.τ => 3.0])
118
196
sol = solve(prob)
119
- plot(sol, vars = [x, RHS])
197
+ plot(sol, vars = [fol_simplified. x, fol_simplified. RHS])
120
198
```
121
199
122
200
By default, ` structural_simplify ` also replaces symbolic ` constants ` with
@@ -136,8 +214,27 @@ What if the forcing function (the “external input”) ``f(t)`` is not constant
136
214
Obviously, one could use an explicit, symbolic function of time:
137
215
138
216
``` @example ode2
139
- @variables f(t)
140
- @named fol_variable_f = ODESystem([f ~ sin(t), D(x) ~ (f - x) / τ])
217
+ @mtkmodel FOL begin
218
+ @parameters begin
219
+ τ # parameters
220
+ end
221
+ @variables begin
222
+ x(t) # dependent variables
223
+ f(t)
224
+ end
225
+ @structural_parameters begin
226
+ h = 1
227
+ end
228
+ begin
229
+ D = Differential(t)
230
+ end
231
+ @equations begin
232
+ f ~ sin(t)
233
+ D(x) ~ (f - x) / τ
234
+ end
235
+ end
236
+
237
+ @named fol_variable_f = FOL()
141
238
```
142
239
143
240
But often this function might not be available in an explicit form.
@@ -154,11 +251,35 @@ value_vector = randn(10)
154
251
f_fun(t) = t >= 10 ? value_vector[end] : value_vector[Int(floor(t)) + 1]
155
252
@register_symbolic f_fun(t)
156
253
157
- @named fol_external_f = ODESystem([f ~ f_fun(t), D(x) ~ (f - x) / τ])
158
- prob = ODEProblem(structural_simplify(fol_external_f), [x => 0.0], (0.0, 10.0), [τ => 0.75])
254
+ @mtkmodel FOLExternalFunction begin
255
+ @parameters begin
256
+ τ # parameters
257
+ end
258
+ @variables begin
259
+ x(t) # dependent variables
260
+ f(t)
261
+ end
262
+ @structural_parameters begin
263
+ h = 1
264
+ end
265
+ begin
266
+ D = Differential(t)
267
+ end
268
+ @equations begin
269
+ f ~ f_fun(t)
270
+ D(x) ~ (f - x) / τ
271
+ end
272
+ end
273
+
274
+ @named fol_external_f = FOLExternalFunction()
275
+ fol_external_f = complete(fol_external_f)
276
+ prob = ODEProblem(structural_simplify(fol_external_f),
277
+ [fol_external_f.x => 0.0],
278
+ (0.0, 10.0),
279
+ [fol_external_f.τ => 0.75])
159
280
160
281
sol = solve(prob)
161
- plot(sol, vars = [x, f])
282
+ plot(sol, vars = [fol_external_f. x, fol_external_f. f])
162
283
```
163
284
164
285
## Building component-based, hierarchical models
@@ -235,19 +356,43 @@ plot(solve(prob))
235
356
236
357
More on this topic may be found in [ Composing Models and Building Reusable Components] (@ref acausal).
237
358
238
- ## Defaults
359
+ ## Inital Guess
239
360
240
361
It is often a good idea to specify reasonable values for the initial state and the
241
362
parameters of a model component. Then, these do not have to be explicitly specified when constructing the ` ODEProblem ` .
242
363
243
364
``` @example ode2
244
- function unitstep_fol_factory(; name)
365
+ @mtkmodel UnitstepFOLFactory begin
366
+ @parameters begin
367
+ τ = 1.0
368
+ end
369
+ @variables begin
370
+ x(t) = 0.0
371
+ end
372
+ @equations begin
373
+ D(x) ~ (1 - x) / τ
374
+ end
375
+ end
376
+ ```
377
+
378
+ While defining the model ` UnitstepFOLFactory ` , an initial guess of 0.0 is assigned to ` x(t) ` and 1.0 to ` τ ` .
379
+ Additionaly, these initial guesses can be modified while creating instances of ` UnitstepFOLFactory ` by passing arguements.
380
+
381
+ ``` @example ode2
382
+ @named fol = UnitstepFOLFactory(; x = 0.1)
383
+ sol = ODEProblem(fol, [], (0.0, 5.0), []) |> solve
384
+ ```
385
+
386
+ In non-DSL definitions, one can pass ` defaults ` dictionary to set the initial guess of the symbolic variables.
387
+
388
+ ``` @example ode3
389
+ using ModelingToolkit
390
+
391
+ function UnitstepFOLFactory(; name)
245
392
@parameters τ
246
393
@variables t x(t)
247
394
ODESystem(D(x) ~ (1 - x) / τ; name, defaults = Dict(x => 0.0, τ => 1.0))
248
395
end
249
-
250
- ODEProblem(unitstep_fol_factory(name = :fol), [], (0.0, 5.0), []) |> solve
251
396
```
252
397
253
398
Note that the defaults can be functions of the other variables, which is then
@@ -316,6 +461,8 @@ Where to go next?
316
461
317
462
- Not sure how MTK relates to similar tools and packages? Read
318
463
[ Comparison of ModelingToolkit vs Equation-Based and Block Modeling Languages] ( @ref ) .
464
+ - For a more detailed explanation of ` @mtkmodel ` checkout
465
+ [ Defining components with ` @mtkmodel ` and connectors with ` @connectors ` ] (@ref mtkmodel_connector)
319
466
- Depending on what you want to do with MTK, have a look at some of the other
320
467
** Symbolic Modeling Tutorials** .
321
468
- If you want to automatically convert an existing function to a symbolic
0 commit comments