Skip to content

Commit 7e3f5b8

Browse files
Merge pull request #312 from ven-k/vkb/custom-component-mtkmodel
docs: update custom component tutorial with `@mtkmodel`
2 parents 791b227 + b161e40 commit 7e3f5b8

File tree

1 file changed

+52
-62
lines changed

1 file changed

+52
-62
lines changed

docs/src/tutorials/custom_component.md

Lines changed: 52 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ First, we need to make some imports.
1010
using ModelingToolkit
1111
using ModelingToolkit: t_nounits as t
1212
using ModelingToolkitStandardLibrary.Electrical
13-
using ModelingToolkitStandardLibrary.Electrical: OnePort
1413
using OrdinaryDiffEq
15-
using IfElse: ifelse
1614
using Plots
1715
```
1816

@@ -36,102 +34,94 @@ end NonlinearResistor;
3634
this can almost be directly translated to the syntax of `ModelingToolkit`.
3735

3836
```@example components
39-
function NonlinearResistor(; name, Ga, Gb, Ve)
40-
@named oneport = OnePort()
41-
@unpack v, i = oneport
42-
pars = @parameters Ga=Ga Gb=Gb Ve=Ve
43-
eqs = [
37+
@mtkmodel NonlinearResistor begin
38+
@extend OnePort()
39+
@parameters begin
40+
Ga
41+
Gb
42+
Ve
43+
end
44+
@equations begin
4445
i ~ ifelse(v < -Ve,
45-
Gb * (v + Ve) - Ga * Ve,
46-
ifelse(v > Ve,
47-
Gb * (v - Ve) + Ga * Ve,
48-
Ga * v))
49-
]
50-
extend(ODESystem(eqs, t, [], pars; name = name), oneport)
46+
Gb * (v + Ve) - Ga * Ve,
47+
ifelse(v > Ve,
48+
Gb * (v - Ve) + Ga * Ve,
49+
Ga * v))
50+
end
5151
end
5252
nothing # hide
5353
```
5454

5555
### Explanation
5656

57-
All components in `ModelingToolkit` are created via a function that serves as the constructor and returns some form of system, in this case, an `ODESystem`.
5857
Since the non-linear resistor is essentially a standard electrical component with two ports, we can extend from the `OnePort` component of the library.
5958

6059
```julia
61-
@named oneport = OnePort()
60+
@extend OnePort()
6261
```
6362

64-
This creates a `OnePort` with the `name = :oneport`.
65-
For easier notation, we can unpack the states of the component
66-
67-
```julia
68-
@unpack v, i = oneport
69-
```
63+
This extends `OnePort` and unpacks `v` and `i` variables.
7064

7165
It might be a good idea to create parameters for the constants of the `NonlinearResistor`.
7266

7367
```julia
74-
pars = @parameters Ga=Ga Gb=Gb Ve=Ve
68+
@parameters begin
69+
Ga
70+
Gb
71+
Ve
72+
end
7573
```
7674

77-
The syntax looks funny but it simply creates symbolic parameters with the name `Ga` where its default value is set from the function's argument `Ga`.
78-
While this is not strictly necessary it allows the user to `remake` the problem easily with different parameters or allow for auto-tuning or parameter optimization without having to do all the costly steps that may be involved with building and simplifying a model.
79-
The non-linear (in this case piece-wise constant) equation for the current can be implemented using `IfElse.ifelse`.
80-
Finally, the created `oneport` component is extended with the created equations and parameters.
81-
In this case, no extra state variables are added, hence an empty vector is supplied.
82-
The independent variable `t` needs to be supplied as the second argument.
83-
84-
```julia
85-
extend(ODESystem(eqs, t, [], pars; name = name), oneport)
86-
```
75+
This creates symbolic parameters with the name `Ga`, `Gb` and `Ve` whose default values are set from the function's arguments `Ga`, `Gb` and `Ve`, respectively.
76+
This allows the user to `remake` the problem easily with different parameters or allow for auto-tuning or parameter optimization without having to do all the costly steps that may be involved with building and simplifying a model.
77+
The non-linear (in this case piece-wise constant) equation for the current can be implemented using `ifelse`.
8778

8879
## Building the Model
8980

9081
The final model can now be created with the components from the library and the new custom component.
9182

9283
```@example components
93-
systems = @named begin
94-
L = Inductor(L = 18, i = 0)
95-
Ro = Resistor(R = 12.5e-3)
96-
G = Conductor(G = 0.565)
97-
C1 = Capacitor(C = 10, v = 4)
98-
C2 = Capacitor(C = 100, v = 0)
99-
Nr = NonlinearResistor(Ga = -0.757576,
100-
Gb = -0.409091,
101-
Ve = 1)
102-
Gnd = Ground()
84+
@mtkmodel ChaoticAttractor begin
85+
@components begin
86+
inductor = Inductor(L = 18, i = 0)
87+
resistor = Resistor(R = 12.5e-3)
88+
conductor = Conductor(G = 0.565)
89+
capacitor1 = Capacitor(C = 10, v = 4)
90+
capacitor2 = Capacitor(C = 100, v = 0)
91+
non_linear_resistor = NonlinearResistor(
92+
Ga = -0.757576,
93+
Gb = -0.409091,
94+
Ve = 1
95+
)
96+
ground = Ground()
97+
end
98+
@equations begin
99+
connect(inductor.p, conductor.p)
100+
connect(conductor.n, non_linear_resistor.p)
101+
connect(capacitor1.p, conductor.n)
102+
connect(inductor.n, resistor.p)
103+
connect(conductor.p, capacitor2.p)
104+
connect(capacitor1.n, capacitor2.n, non_linear_resistor.n, resistor.n, ground.g)
105+
end
103106
end
104-
105-
connections = [connect(L.p, G.p)
106-
connect(G.n, Nr.p)
107-
connect(Nr.n, Gnd.g)
108-
connect(C1.p, G.n)
109-
connect(L.n, Ro.p)
110-
connect(G.p, C2.p)
111-
connect(C1.n, Gnd.g)
112-
connect(C2.n, Gnd.g)
113-
connect(Ro.n, Gnd.g)]
114-
115-
@named model = ODESystem(connections, t; systems)
116107
nothing # hide
117108
```
118109

119110
## Simulating the Model
120111

121-
Now the model can be simulated.
122-
First, `structural_simplify` is called on the model and an `ODEProblem` is built from the result.
123-
Since the initial voltage of the first capacitor was already specified via `v`, no initial condition is given and an empty pair is supplied.
112+
`@mtkbuild` builds a structurally simplified `ChaoticAttractor` model.
113+
Since the initial voltage of the capacitors was already specified via `v` and the initial current of inductor via `i`, no initial condition is given and an empty pair is supplied.
124114

125115
```@example components
126-
sys = structural_simplify(model)
127-
prob = ODEProblem(sys, Pair[], (0, 5e4))
128-
sol = solve(prob, Rodas4(); saveat = 1.0)
116+
@mtkbuild sys = ChaoticAttractor()
117+
prob = ODEProblem(sys, Pair[], (0, 5e4), saveat = 0.01)
118+
sol = solve(prob)
129119
130-
plot(sol[C1.v], sol[C2.v], title = "Chaotic Attractor", label = "",
120+
plot(sol[capacitor1.v], sol[capacitor2.v], title = "Chaotic Attractor", label = "",
131121
ylabel = "C1 Voltage in V", xlabel = "C2 Voltage in V")
132122
```
133123

134124
```@example components
135-
plot(sol; idxs = [C1.v, C2.v, L.i],
125+
plot(sol; idxs = [capacitor1.v, capacitor2.v, inductor.i],
136126
labels = ["C1 Voltage in V" "C2 Voltage in V" "Inductor Current in A"])
137127
```

0 commit comments

Comments
 (0)