@@ -5,6 +5,73 @@ easily build large models. The composition is lazy and only instantiated
5
5
at the time of conversion to numerical models, allowing a more performant
6
6
way in terms of computation time and memory.
7
7
8
+ ## Simple Model Composition Example
9
+
10
+ The following is an example of building a model in a library with
11
+ an optional forcing function, and allowing the user to specify the
12
+ forcing later. Here, the library author defines a component named
13
+ ` decay ` . The user then builds two ` decay ` components and connects them,
14
+ saying the forcing term of ` decay1 ` is a constant while the forcing term
15
+ of ` decay2 ` is the value of the state variable ` x ` .
16
+
17
+ ``` julia
18
+ using ModelingToolkit
19
+
20
+ function decay (;name)
21
+ @parameters t a
22
+ @variables x (t) f (t)
23
+ D = Differential (t)
24
+ ODESystem ([
25
+ D (x) ~ - a* x + f
26
+ ];
27
+ name= name)
28
+ end
29
+
30
+ @named decay1 = decay ()
31
+ @named decay2 = decay ()
32
+
33
+ @parameters t
34
+ D = Differential (t)
35
+ @named connected = ODESystem ([
36
+ decay2. f ~ decay1. x
37
+ D (decay1. f) ~ 0
38
+ ], t, systems= [decay1, decay2])
39
+
40
+ equations (connected)
41
+
42
+ # 4-element Vector{Equation}:
43
+ # Differential(t)(decay1₊f(t)) ~ 0
44
+ # decay2₊f(t) ~ decay1₊x(t)
45
+ # Differential(t)(decay1₊x(t)) ~ decay1₊f(t) - (decay1₊a*(decay1₊x(t)))
46
+ # Differential(t)(decay2₊x(t)) ~ decay2₊f(t) - (decay2₊a*(decay2₊x(t)))
47
+
48
+ equations (alias_elimination (connected))
49
+
50
+ # 4-element Vector{Equation}:
51
+ # Differential(t)(decay1₊f(t)) ~ 0
52
+ # 0 ~ decay1₊x(t) - (decay2₊f(t))
53
+ # Differential(t)(decay1₊x(t)) ~ decay1₊f(t) - (decay1₊a*(decay1₊x(t)))
54
+ # Differential(t)(decay2₊x(t)) ~ decay2₊f(t) - (decay2₊a*(decay2₊x(t)))
55
+ ```
56
+
57
+ Now we can solve the system:
58
+
59
+ ``` julia
60
+ x0 = [
61
+ decay1. x => 1.0
62
+ decay1. f => 0.0
63
+ decay2. x => 1.0
64
+ ]
65
+ p = [
66
+ decay1. a => 0.1
67
+ decay2. a => 0.2
68
+ ]
69
+
70
+ using DifferentialEquations
71
+ prob = ODEProblem (reduced_system, x0, (0.0 , 100.0 ), p)
72
+ sol = solve (prob, Tsit5 ())
73
+ ```
74
+
8
75
## Basics of Model Composition
9
76
10
77
Every ` AbstractSystem ` has a ` system ` keyword argument for specifying
@@ -29,40 +96,121 @@ by specifying their equality: `x ~ subsys.x` in the `eqs` for `sys`.
29
96
This algebraic relationship can then be simplified by transformations
30
97
like ` alias_elimination ` or ` tearing ` which will be described later.
31
98
32
- ### Simple Model Composition Example
99
+ ### Numerics with Composed Models
33
100
34
- The following is an example of building a model in a library with
35
- an optional forcing function, and allowing the user to specify the
36
- forcing later. Here, the library author defines a component named
37
- ` decay ` . The user then builds two ` decay ` components and connects them,
38
- saying the forcing term of ` decay1 ` is a constant while the forcing term
39
- of ` decay2 ` is the value of the state variable ` x ` .
101
+ These composed models can then be directly transformed into their
102
+ associated ` SciMLProblem ` type using the standard constructors. When
103
+ this is done, the initial conditions and parameters must be specified
104
+ in their namespaced form. For example:
40
105
41
106
``` julia
42
- function decay (;name)
43
- @parameters t a
44
- @variables x (t) f (t)
45
- D = Differential (t)
46
- ODESystem ([
47
- D (x) ~ - a* x + f
48
- ];
49
- name= name)
50
- end
107
+ u0 = [
108
+ x => 2.0
109
+ subsys. x => 2.0
110
+ ]
111
+ ```
51
112
52
- @named decay1 = decay ()
53
- @named decay2 = decay ()
113
+ Note that any default values within the given subcomponent will be
114
+ used if no override is provided at construction time. If any values for
115
+ initial conditions or parameters are unspecified an error will be thrown.
116
+
117
+ When the model is numerically solved, the solution can be accessed via
118
+ its symbolic values. For example, if ` sol ` is the ` ODESolution ` , one
119
+ can use ` sol[x] ` and ` sol[subsys.x] ` to access the respective timeseries
120
+ in the solution. All other indexing rules stay the same, so ` sol[x,1:5] `
121
+ accesses the first through fifth values of ` x ` . Note that this can be
122
+ done even if the variable ` x ` is eliminated from the system from
123
+ transformations like ` alias_elimination ` or ` tearing ` : the variable
124
+ will be lazily reconstructed on demand.
125
+
126
+ ## Alias Elimination
127
+
128
+ In many cases, the nicest way to build a model may leave a lot of
129
+ unnecessary variables. Thus one may want to remove these equations
130
+ before numerically solving. The ` alias_elimination ` function removes
131
+ these relationships and trivial singularity equations, i.e. equations
132
+ which result in ` 0~0 ` expressions in over-specified systems.
133
+
134
+ ## Inheritance and Combine (TODO)
135
+
136
+ Model inheritance can be done in two ways: explicitly or implicitly.
137
+ The explicit way is to shadow variables with equality expressions.
138
+ For example, let's assume we have three separate systems which we
139
+ want to compose to a single one. This is how one could explicitly
140
+ forward all states and parameters to the higher level system:
141
+
142
+ ``` julia
143
+ using ModelingToolkit, OrdinaryDiffEq, Plots
144
+
145
+ # # Library code
54
146
55
147
@parameters t
56
148
D = Differential (t)
57
- connected = ODESystem ([
58
- decay2. f ~ decay1. x
59
- D (decay1. f) ~ 0
60
- ], t, systems= [decay1, decay2])
149
+
150
+ @variables S (t), I (t), R (t)
151
+ N = S + I + R
152
+ @parameters β,γ
153
+
154
+ @named seqn = ODESystem ([D (S) ~ - β* S* I/ N])
155
+ @named ieqn = ODESystem ([D (I) ~ β* S* I/ N- γ* I])
156
+ @named reqn = ODESystem ([D (R) ~ γ* I])
157
+
158
+ @named sir = ODESystem ([
159
+ S ~ ieqn. S,
160
+ I ~ seqn. I,
161
+ R ~ ieqn. R,
162
+ ieqn. S ~ seqn. S,
163
+ seqn. I ~ ieqn. I,
164
+ seqn. R ~ reqn. R,
165
+ ieqn. R ~ reqn. R,
166
+ reqn. I ~ ieqn. I], t, [S,I,R], [β,γ], name= :sir ,
167
+ systems= [seqn,ieqn,reqn],
168
+ default_p = [
169
+ seqn. β => β
170
+ ieqn. β => β
171
+ ieqn. γ => γ
172
+ reqn. γ => γ
173
+ ])
61
174
```
62
175
63
- ## Combine
176
+ Note that the states are forwarded by an equality relationship, while
177
+ the parameters are forwarded through a relationship in their default
178
+ values. The user of this model can then solve this model simply by
179
+ specifying the values at the highest level:
64
180
65
- ## Alias Elimination
181
+ ``` julia
182
+ sireqn_simple = alias_elimination (sir)
183
+
184
+ # # User Code
185
+
186
+ u0 = [sir. S => 990.0 ,
187
+ sir. I => 10.0 ,
188
+ sir. R => 0.0 ]
189
+
190
+ p = [
191
+ sir. β => 0.5
192
+ sir. γ => 0.25
193
+ ]
194
+
195
+ # Time span
196
+ tspan = (0.0 ,40.0 )
197
+
198
+ # Define problem
199
+ prob = ODEProblem (sireqn_simple,u0,tspan,p,jac= true )
200
+
201
+ # Solve
202
+ sol = solve (prob,Tsit5 ())
203
+
204
+ sol[R]
205
+ ```
206
+
207
+ However, one can similarly simplify this process of inheritance by
208
+ using ` combine ` which concatenates all of the vectors within the
209
+ systems. For example, we could equivalently have done:
210
+
211
+ ``` julia
212
+ @named sir = combine ([seqn,ieqn,reqn])
213
+ ```
66
214
67
215
## The Tearing Transformation
68
216
0 commit comments