1
1
# Getting Started with ModelingToolkit.jl
2
2
3
- This is an introductory tutorial for ModelingToolkit (MTK).
4
- Some examples of Ordinary Differential Equations (ODE) are used to
5
- illustrate the basic user-facing functionality.
3
+ This is an introductory tutorial for ModelingToolkit (MTK). We will demonstrate
4
+ the basics of the package by demonstrating how to define and simulate simple
5
+ Ordinary Differential Equation (ODE) systems.
6
+
7
+ ## Installing ModelingToolkit
8
+
9
+ To install ModelingToolkit, use the Julia package manager. This can be done as follows:
10
+
11
+ ``` julia
12
+ using Pkg
13
+ Pkg. add (" ModelingToolkit" )
14
+ ```
6
15
7
16
## Copy-Pastable Simplified Example
8
17
@@ -22,21 +31,14 @@ D = Differential(t)
22
31
@variables begin
23
32
x(t) # dependent variables
24
33
end
25
- @structural_parameters begin
26
- h = 1
27
- end
28
34
@equations begin
29
- D(x) ~ (h - x) / τ
35
+ D(x) ~ (1 - x) / τ
30
36
end
31
37
end
32
38
33
39
using DifferentialEquations: solve
34
-
35
- @named fol = FOL()
36
- fol = complete(fol)
37
-
40
+ @mtkbuild fol = FOL()
38
41
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'.
40
42
sol = solve(prob)
41
43
42
44
using Plots
@@ -58,7 +60,7 @@ Here, ``t`` is the independent variable (time), ``x(t)`` is the (scalar) state
58
60
variable, `` f(t) `` is an external forcing function, and `` \tau `` is a
59
61
parameter.
60
62
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
63
+ first set the forcing function to a time-independent value `` 1 `` . And the
62
64
independent variable `` t `` is automatically added by `` @mtkmodel `` .
63
65
64
66
``` @example ode2
@@ -74,32 +76,17 @@ D = Differential(t)
74
76
@variables begin
75
77
x(t) # dependent variables
76
78
end
77
- @structural_parameters begin
78
- h = 1
79
- end
80
79
@equations begin
81
- D(x) ~ (h - x) / τ
80
+ D(x) ~ (1 - x) / τ
82
81
end
83
82
end
84
83
85
- @named fol_incomplete = FOL()
86
- fol = complete(fol_incomplete)
84
+ @mtkbuild fol = FOL()
87
85
```
88
86
89
87
Note that equations in MTK use the tilde character (` ~ ` ) as equality sign.
90
88
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
- ```
89
+ ` @mtkbuild ` creates an instance of ` FOL ` named as ` fol ` .
103
90
104
91
After construction of the ODE, you can solve it using [ DifferentialEquations.jl] ( https://docs.sciml.ai/DiffEqDocs/stable/ ) :
105
92
@@ -115,22 +102,6 @@ The initial state and the parameter values are specified using a mapping
115
102
from the actual symbolic elements to their values, represented as an array
116
103
of ` Pair ` s, which are constructed using the ` => ` operator.
117
104
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
-
134
105
## Algebraic relations and structural simplification
135
106
136
107
You could separate the calculation of the right-hand side, by introducing an
@@ -147,46 +118,40 @@ using ModelingToolkit
147
118
x(t) # dependent variables
148
119
RHS(t)
149
120
end
150
- @structural_parameters begin
151
- h = 1
152
- end
153
121
begin
154
122
D = Differential(t)
155
123
end
156
124
@equations begin
157
- RHS ~ (h - x) / τ
125
+ RHS ~ (1 - x) / τ
158
126
D(x) ~ RHS
159
127
end
160
128
end
161
129
162
- @named fol_separate = FOL()
130
+ @mtkbuild fol = FOL()
163
131
```
164
132
165
- To directly solve this system, you would have to create a Differential-Algebraic
166
- Equation (DAE) problem, since besides the differential equation, there is an
167
- additional algebraic equation now. However, this DAE system can obviously be
168
- transformed into the single ODE we used in the first example above. MTK achieves
169
- this by structural simplification:
133
+ You can look at the equations by using the command ` equations ` :
170
134
171
135
``` @example ode2
172
- fol_simplified = structural_simplify(complete(fol_separate))
173
- equations(fol_simplified)
136
+ equations(fol)
174
137
```
175
138
139
+ Notice that there is only one equation in this system, ` Differential(t)(x(t)) ~ RHS(t) ` .
140
+ The other equation was removed from the system and was transformed into an ` observed `
141
+ variable. Observed equations are variables which can be computed on-demand but are not
142
+ necessary for the solution of the system, and thus MTK tracks it separately. One can
143
+ check the observed equations via the ` observed ` function:
144
+
176
145
``` @example ode2
177
- equations(fol_simplified) == equations (fol)
146
+ observed (fol)
178
147
```
179
148
180
- You can extract the equations from a system using ` equations ` (and, in the same
181
- way, ` states ` and ` parameters ` ). The simplified equation is exactly the same
182
- as the original one, so the simulation performance will also be the same.
183
- However, there is one difference. MTK does keep track of the eliminated
184
- algebraic variables as "observables" (see
185
- [ Observables and Variable Elimination] ( @ref ) ).
186
- That means, MTK still knows how to calculate them out of the information available
149
+ For more information on this process, see [ Observables and Variable Elimination] ( @ref ) .
150
+
151
+ MTK still knows how to calculate them out of the information available
187
152
in a simulation result. The intermediate variable ` RHS ` therefore can be plotted
188
- along with the state variable. Note that this has to be requested explicitly,
189
- through :
153
+ along with the state variable. Note that this has to be requested explicitly
154
+ like as follows :
190
155
191
156
``` @example ode2
192
157
prob = ODEProblem(fol_simplified,
@@ -197,16 +162,26 @@ sol = solve(prob)
197
162
plot(sol, vars = [fol_simplified.x, fol_simplified.RHS])
198
163
```
199
164
200
- By default, ` structural_simplify ` also replaces symbolic ` constants ` with
201
- their default values. This allows additional simplifications not possible
202
- when using ` parameters ` (e.g., solution of linear equations by dividing out
203
- the constant's value, which cannot be done for parameters, since they may
204
- be zero).
165
+ ## Named Indexing of Solutions
166
+
167
+ Note that the indexing of the solution similarly works via the names, and so to get
168
+ the time series for ` x ` , one would do:
169
+
170
+ ``` @example ode2
171
+ sol[fol.x]
172
+ ```
173
+
174
+ or to get the second value in the time series for ` x ` :
175
+
176
+ ``` @example ode2
177
+ sol[fol.x,2]
178
+ ```
205
179
206
- Note that the indexing of the solution similarly works via the names, and so
207
- ` sol[x] ` gives the time-series for ` x ` , ` sol[x,2:10] ` gives the 2nd through 10th
208
- values of ` x ` matching ` sol.t ` , etc. Note that this works even for variables
209
- which have been eliminated, and thus ` sol[RHS] ` retrieves the values of ` RHS ` .
180
+ Similarly, the time series for ` RHS ` can be retrieved using the same indexing:
181
+
182
+ ``` @example ode2
183
+ sol[fol.RHS]
184
+ ```
210
185
211
186
## Specifying a time-variable forcing function
212
187
@@ -222,9 +197,6 @@ Obviously, one could use an explicit, symbolic function of time:
222
197
x(t) # dependent variables
223
198
f(t)
224
199
end
225
- @structural_parameters begin
226
- h = 1
227
- end
228
200
begin
229
201
D = Differential(t)
230
202
end
@@ -445,14 +417,9 @@ using the structural information. For more information, see the
445
417
446
418
Here are some notes that may be helpful during your initial steps with MTK:
447
419
448
- - Sometimes, the symbolic engine within MTK cannot correctly identify the
449
- independent variable (e.g. time) out of all variables. In such a case, you
450
- usually get an error that some variable(s) is "missing from variable map". In
451
- most cases, it is then sufficient to specify the independent variable as second
452
- argument to ` ODESystem ` , e.g. ` ODESystem(eqs, t) ` .
453
- - A completely macro-free usage of MTK is possible and is discussed in a
454
- separate tutorial. This is for package developers, since the macros are only
455
- essential for automatic symbolic naming for modelers.
420
+ - The ` @mtkmodel ` macro is for high-level usage of MTK. However, in many cases you
421
+ may need to programmatically generate ` ODESystem ` s. If that's the case, check out
422
+ the [ Programmatically Generating and Scripting ODESystems Tutorial] (@ref programmatically).
456
423
- Vector-valued parameters and variables are possible. A cleaner, more
457
424
consistent treatment of these is still a work in progress, however. Once finished,
458
425
this introductory tutorial will also cover this feature.
0 commit comments