@@ -15,58 +15,70 @@ equalities before solving. Let's see this in action.
15
15
using ModelingToolkit, Plots, DifferentialEquations
16
16
17
17
@parameters t
18
+ @connector function Pin (;name)
19
+ sts = @variables v (t)= 1.0 i (t)= 1.0
20
+ ODESystem (Equation[], t, sts, []; name= name)
21
+ end
22
+
23
+ function ModelingToolkit. connect (:: Type{Pin} , ps... )
24
+ eqs = [
25
+ 0 ~ sum (p-> p. i, ps) # KCL
26
+ ]
27
+ # KVL
28
+ for i in 1 : length (ps)- 1
29
+ push! (eqs, ps[i]. v ~ ps[i+ 1 ]. v)
30
+ end
18
31
19
- # Basic electric components
20
- function Pin (;name)
21
- @variables v (t) i (t)
22
- ODESystem (Equation[], t, [v, i], [], name= name, defaults= [v=> 1.0 , i=> 1.0 ])
32
+ return eqs
23
33
end
24
34
25
35
function Ground (;name)
26
36
@named g = Pin ()
27
37
eqs = [g. v ~ 0 ]
28
- ODESystem (eqs, t, [], [], systems= [g], name= name)
38
+ ODESystem (eqs, t, [], [], systems= [g]; name= name)
29
39
end
30
40
31
- function Resistor (;name, R = 1.0 )
32
- val = R
41
+ function OnePort (;name)
33
42
@named p = Pin ()
34
43
@named n = Pin ()
35
- @variables v (t)
36
- @parameters R
44
+ sts = @variables v (t)= 1.0 i (t)= 1.0
37
45
eqs = [
38
46
v ~ p. v - n. v
39
47
0 ~ p. i + n. i
40
- v ~ p. i * R
48
+ i ~ p. i
41
49
]
42
- ODESystem (eqs, t, [v] , [R ], systems= [p, n], defaults = Dict (R => val), name= name)
50
+ ODESystem (eqs, t, sts , [], systems= [p, n]; name= name)
43
51
end
44
52
45
- function Capacitor (; name, C = 1.0 )
46
- val = C
47
- @named p = Pin ()
48
- @named n = Pin ()
49
- @variables v (t)
50
- @parameters C
53
+ function Resistor (;name, R = 1.0 )
54
+ @named oneport = OnePort ()
55
+ @unpack v, i = oneport
56
+ ps = @parameters R= R
57
+ eqs = [
58
+ v ~ i * R
59
+ ]
60
+ extend (oneport, ODESystem (eqs, t, [], ps; name= name); name= name)
61
+ end
62
+
63
+ function Capacitor (;name, C = 1.0 )
64
+ @named oneport = OnePort ()
65
+ @unpack v, i = oneport
66
+ ps = @parameters C= C
51
67
D = Differential (t)
52
68
eqs = [
53
- v ~ p. v - n. v
54
- 0 ~ p. i + n. i
55
- D (v) ~ p. i / C
69
+ D (v) ~ i / C
56
70
]
57
- ODESystem (eqs, t, [v ], [C], systems = [p, n], defaults = Dict (C => val), name= name)
71
+ extend (oneport, ODESystem (eqs, t, [], ps; name = name); name= name)
58
72
end
59
73
60
74
function ConstantVoltage (;name, V = 1.0 )
61
- val = V
62
- @named p = Pin ()
63
- @named n = Pin ()
64
- @parameters V
75
+ @named oneport = OnePort ()
76
+ @unpack v = oneport
77
+ ps = @parameters V= V
65
78
eqs = [
66
- V ~ p. v - n. v
67
- 0 ~ p. i + n. i
79
+ V ~ v
68
80
]
69
- ODESystem (eqs, t, [], [V], systems = [p, n], defaults = Dict (V => val), name= name)
81
+ extend (oneport, ODESystem (eqs, t, [], ps; name = name); name= name)
70
82
end
71
83
72
84
R = 1.0
@@ -77,22 +89,10 @@ V = 1.0
77
89
@named source = ConstantVoltage (V= V)
78
90
@named ground = Ground ()
79
91
80
- function connect_pins (ps... )
81
- eqs = [
82
- 0 ~ sum (p-> p. i, ps) # KCL
83
- ]
84
- # KVL
85
- for i in 1 : length (ps)- 1
86
- push! (eqs, ps[i]. v ~ ps[i+ 1 ]. v)
87
- end
88
-
89
- return eqs
90
- end
91
-
92
92
rc_eqs = [
93
- connect_pins (source. p, resistor. p)
94
- connect_pins (resistor. n, capacitor. p)
95
- connect_pins (capacitor. n, source. n, ground. g)
93
+ connect (source. p, resistor. p)
94
+ connect (resistor. n, capacitor. p)
95
+ connect (capacitor. n, source. n, ground. g)
96
96
]
97
97
98
98
@named rc_model = ODESystem (rc_eqs, t,
@@ -117,12 +117,12 @@ For each of our components we use a Julia function which emits an `ODESystem`.
117
117
At the top we start with defining the fundamental qualities of an electrical
118
118
circuit component. At every input and output pin a circuit component has
119
119
two values: the current at the pin and the voltage. Thus we define the ` Pin `
120
- component to simply be the values there:
120
+ component (connector) to simply be the values there:
121
121
122
122
``` julia
123
- function Pin (;name)
124
- @variables v (t) i (t)
125
- ODESystem (Equation[], t, [v, i], [], name= name, defaults = [v => 1.0 , i => 1.0 ] )
123
+ @connector function Pin (;name)
124
+ sts = @variables v (t)= 1.0 i (t)= 1.0
125
+ ODESystem (Equation[], t, sts, []; name= name)
126
126
end
127
127
```
128
128
@@ -153,7 +153,27 @@ that the voltage in such a `Pin` is equal to zero. This gives:
153
153
function Ground (;name)
154
154
@named g = Pin ()
155
155
eqs = [g. v ~ 0 ]
156
- ODESystem (eqs, t, [], [], systems= [g], name= name)
156
+ ODESystem (eqs, t, [], [], systems= [g]; name= name)
157
+ end
158
+ ```
159
+
160
+ Next we build a ` OnePort ` : an abstraction for all simple electrical component
161
+ with two pins. The voltage difference between the positive pin and the negative
162
+ pin is the voltage of the component, the current between two pins must sum to
163
+ zero, and the current of the component equals to the current of the positive
164
+ pin.
165
+
166
+ ``` julia
167
+ function OnePort (;name)
168
+ @named p = Pin ()
169
+ @named n = Pin ()
170
+ sts = @variables v (t)= 1.0 i (t)= 1.0
171
+ eqs = [
172
+ v ~ p. v - n. v
173
+ 0 ~ p. i + n. i
174
+ i ~ p. i
175
+ ]
176
+ ODESystem (eqs, t, sts, [], systems= [p, n]; name= name)
157
177
end
158
178
```
159
179
@@ -166,41 +186,37 @@ zero. This leads to our resistor equations:
166
186
167
187
``` julia
168
188
function Resistor (;name, R = 1.0 )
169
- val = R
170
- @named p = Pin ()
171
- @named n = Pin ()
172
- @variables v (t)
173
- @parameters R
189
+ @named oneport = OnePort ()
190
+ @unpack v, i = oneport
191
+ ps = @parameters R= R
174
192
eqs = [
175
- v ~ p. v - n. v
176
- 0 ~ p. i + n. i
177
- v ~ p. i * R
193
+ v ~ i * R
178
194
]
179
- ODESystem (eqs, t, [v ], [R], systems = [p, n], defaults = Dict (R => val), name= name)
195
+ extend (oneport, ODESystem (eqs, t, [], ps; name = name); name= name)
180
196
end
181
197
```
182
198
183
- Notice that we have created this system with a ` defaults ` for the resistor's
184
- resistance. By doing so, if the resistance of this resistor is not overridden
185
- by a higher level default or overridden at ` ODEProblem ` construction time, this
186
- will be the value of the resistance.
199
+ Notice that we have created this system with a default parameter ` R ` for the
200
+ resistor's resistance. By doing so, if the resistance of this resistor is not
201
+ overridden by a higher level default or overridden at ` ODEProblem ` construction
202
+ time, this will be the value of the resistance. Also, note the use of ` @unpack `
203
+ and ` extend ` . For the ` Resistor ` , we want to simply inherit ` OnePort ` 's
204
+ equations and states and extend them with a new equation. ModelingToolkit makes
205
+ a new namespaced variable ` oneport₊v(t) ` when using the syntax ` oneport.v ` , and
206
+ we can use ` @unpack ` avoid the namespacing.
187
207
188
- Using our knowledge of circuits we similarly construct the Capacitor:
208
+ Using our knowledge of circuits we similarly construct the ` Capacitor ` :
189
209
190
210
``` julia
191
- function Capacitor (; name, C = 1.0 )
192
- val = C
193
- @named p = Pin ()
194
- @named n = Pin ()
195
- @variables v (t)
196
- @parameters C
211
+ function Capacitor (;name, C = 1.0 )
212
+ @named oneport = OnePort ()
213
+ @unpack v, i = oneport
214
+ ps = @parameters C= C
197
215
D = Differential (t)
198
216
eqs = [
199
- v ~ p. v - n. v
200
- 0 ~ p. i + n. i
201
- D (v) ~ p. i / C
217
+ D (v) ~ i / C
202
218
]
203
- ODESystem (eqs, t, [v ], [C], systems = [p, n], defaults = Dict (C => val), name= name)
219
+ extend (oneport, ODESystem (eqs, t, [], ps; name = name); name= name)
204
220
end
205
221
```
206
222
@@ -211,15 +227,13 @@ model this as:
211
227
212
228
``` julia
213
229
function ConstantVoltage (;name, V = 1.0 )
214
- val = V
215
- @named p = Pin ()
216
- @named n = Pin ()
217
- @parameters V
230
+ @named oneport = OnePort ()
231
+ @unpack v = oneport
232
+ ps = @parameters V= V
218
233
eqs = [
219
- V ~ p. v - n. v
220
- 0 ~ p. i + n. i
234
+ V ~ v
221
235
]
222
- ODESystem (eqs, t, [], [V], systems = [p, n], defaults = Dict (V => val), name= name)
236
+ extend (oneport, ODESystem (eqs, t, [], ps; name = name); name= name)
223
237
end
224
238
```
225
239
@@ -246,7 +260,7 @@ i.e. that currents sum to zero and voltages across the pins are equal. Thus
246
260
we will build a helper function ` connect_pins ` which implements these rules:
247
261
248
262
``` julia
249
- function connect_pins ( ps... )
263
+ function ModelingToolkit . connect ( :: Type{Pin} , ps... )
250
264
eqs = [
251
265
0 ~ sum (p-> p. i, ps) # KCL
252
266
]
@@ -266,9 +280,9 @@ the source and the ground. This would mean our connection equations are:
266
280
267
281
``` julia
268
282
rc_eqs = [
269
- connect_pins (source. p, resistor. p)
270
- connect_pins (resistor. n, capacitor. p)
271
- connect_pins (capacitor. n, source. n, ground. g)
283
+ connect (source. p, resistor. p)
284
+ connect (resistor. n, capacitor. p)
285
+ connect (capacitor. n, source. n, ground. g)
272
286
]
273
287
```
274
288
@@ -288,15 +302,26 @@ equations are:
288
302
``` julia
289
303
equations (rc_model)
290
304
291
- 16 - element Vector{Equation}:
305
+ 20 - element Vector{Equation}:
292
306
0 ~ resistor₊p₊i (t) + source₊p₊i (t)
293
307
source₊p₊v (t) ~ resistor₊p₊v (t)
294
308
0 ~ capacitor₊p₊i (t) + resistor₊n₊i (t)
295
309
resistor₊n₊v (t) ~ capacitor₊p₊v (t)
296
- ⋮
297
- Differential (t)(capacitor₊v (t)) ~ capacitor₊p₊i (t)* (capacitor₊C^- 1 )
298
- source₊V ~ source₊p₊v (t) - (source₊n₊v (t))
310
+ 0 ~ capacitor₊n₊i (t) + ground₊g₊i (t) + source₊n₊i (t)
311
+ capacitor₊n₊v (t) ~ source₊n₊v (t)
312
+ source₊n₊v (t) ~ ground₊g₊v (t)
313
+ resistor₊v (t) ~ resistor₊p₊v (t) - resistor₊n₊v (t)
314
+ 0 ~ resistor₊n₊i (t) + resistor₊p₊i (t)
315
+ resistor₊i (t) ~ resistor₊p₊i (t)
316
+ resistor₊v (t) ~ resistor₊R* resistor₊i (t)
317
+ capacitor₊v (t) ~ capacitor₊p₊v (t) - capacitor₊n₊v (t)
318
+ 0 ~ capacitor₊n₊i (t) + capacitor₊p₊i (t)
319
+ capacitor₊i (t) ~ capacitor₊p₊i (t)
320
+ Differential (t)(capacitor₊v (t)) ~ capacitor₊i (t)* (capacitor₊C^- 1 )
321
+ source₊v (t) ~ source₊p₊v (t) - source₊n₊v (t)
299
322
0 ~ source₊n₊i (t) + source₊p₊i (t)
323
+ source₊i (t) ~ source₊p₊i (t)
324
+ source₊V ~ source₊v (t)
300
325
ground₊g₊v (t) ~ 0
301
326
```
302
327
@@ -305,16 +330,27 @@ the states are:
305
330
``` julia
306
331
states (rc_model)
307
332
308
- 16 - element Vector{Term{Real}}:
309
- resistor₊p₊i (t)
333
+ 20 - element Vector{Term{Real, Base. ImmutableDict{DataType, Any}}}:
310
334
source₊p₊i (t)
335
+ resistor₊p₊i (t)
311
336
source₊p₊v (t)
312
337
resistor₊p₊v (t)
313
- ⋮
338
+ capacitor₊p₊i (t)
339
+ resistor₊n₊i (t)
340
+ resistor₊n₊v (t)
341
+ capacitor₊p₊v (t)
342
+ source₊n₊i (t)
343
+ capacitor₊n₊i (t)
344
+ ground₊g₊i (t)
345
+ capacitor₊n₊v (t)
314
346
source₊n₊v (t)
315
347
ground₊g₊v (t)
316
348
resistor₊v (t)
349
+ resistor₊i (t)
317
350
capacitor₊v (t)
351
+ capacitor₊i (t)
352
+ source₊v (t)
353
+ source₊i (t)
318
354
```
319
355
320
356
and the parameters are:
@@ -342,8 +378,8 @@ sys = structural_simplify(rc_model)
342
378
equations (sys)
343
379
344
380
2 - element Vector{Equation}:
345
- 0 ~ capacitor₊v (t) + resistor₊R* capacitor₊p ₊i (t) - source₊V
346
- Differential (t)(capacitor₊v (t)) ~ capacitor₊p ₊i (t)* (capacitor₊C^- 1 )
381
+ 0 ~ capacitor₊v (t) + resistor₊R* resistor ₊i (t) - source₊V
382
+ Differential (t)(capacitor₊v (t)) ~ resistor ₊i (t)* (capacitor₊C^- 1 )
347
383
```
348
384
349
385
``` julia
@@ -401,21 +437,25 @@ variables. Let's see what our observed variables are:
401
437
``` julia
402
438
observed (sys)
403
439
404
- 14 - element Vector{Equation}:
405
- resistor₊p₊i (t) ~ capacitor₊p₊i (t)
440
+ 18 - element Vector{Equation}:
441
+ capacitor₊i (t) ~ resistor₊i (t)
442
+ ground₊g₊i (t) ~ 0.0
443
+ source₊n₊i (t) ~ resistor₊i (t)
444
+ source₊i (t) ~ - resistor₊i (t)
445
+ source₊p₊i (t) ~ - resistor₊i (t)
446
+ capacitor₊n₊i (t) ~ - resistor₊i (t)
447
+ resistor₊n₊v (t) ~ capacitor₊v (t)
448
+ resistor₊n₊i (t) ~ - resistor₊i (t)
449
+ resistor₊p₊i (t) ~ resistor₊i (t)
450
+ capacitor₊p₊i (t) ~ resistor₊i (t)
451
+ capacitor₊p₊v (t) ~ capacitor₊v (t)
406
452
capacitor₊n₊v (t) ~ 0.0
407
453
source₊n₊v (t) ~ 0.0
408
- ground₊g₊i (t) ~ 0.0
409
- source₊n₊i (t) ~ capacitor₊p₊i (t)
410
- source₊p₊i (t) ~ - capacitor₊p₊i (t)
411
- capacitor₊n₊i (t) ~ - capacitor₊p₊i (t)
412
- resistor₊n₊i (t) ~ - capacitor₊p₊i (t)
413
454
ground₊g₊v (t) ~ 0.0
414
- source₊p₊v (t) ~ source₊V
415
- capacitor₊p₊v (t) ~ capacitor₊v (t)
416
- resistor₊p₊v (t) ~ source₊p₊v (t)
417
- resistor₊n₊v (t) ~ capacitor₊p₊v (t)
418
- resistor₊v (t) ~ - ((capacitor₊p₊v (t)) - (source₊p₊v (t)))
455
+ source₊v (t) ~ source₊V
456
+ source₊p₊v (t) ~ source₊v (t)
457
+ resistor₊p₊v (t) ~ source₊v (t)
458
+ resistor₊v (t) ~ source₊v (t) - capacitor₊v (t)
419
459
```
420
460
421
461
These are explicit algebraic equations which can then be used to reconstruct
0 commit comments