Skip to content

Commit df29058

Browse files
jvaverkaJacob Vaverka
authored and
Jacob Vaverka
committed
docs: add cauer low pass analog filter example
1 parent c85424e commit df29058

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Cauer Low Pass Analog Filter
2+
3+
The example Cauer Filter is a low-pass-filter of the fifth order.
4+
It is realized using an analog network.
5+
The [`Electrical.Voltage`](@ref) source is the input voltage (whose value varies as defined by [`Blocks.Step`](@ref)), and the `resistor.p.v` is the filter output voltage.
6+
The pulse response is calculated.
7+
8+
## Copy-Pastable Example
9+
10+
```@example cauer_low_pass_analog
11+
using ModelingToolkit
12+
using ModelingToolkitStandardLibrary.Blocks: Step
13+
using ModelingToolkitStandardLibrary.Electrical
14+
using OrdinaryDiffEq
15+
using Plots
16+
17+
@variables t
18+
19+
@mtkmodel CauerLowPassAnalog begin
20+
@parameters begin
21+
L1 = 1.304, [description="Inductance filter coefficient no.1"]
22+
L2 = 0.8586, [description="Inductance filter coefficient no.2"]
23+
C1 = 1.072, [description="Capacitance filter coefficient no.1"]
24+
C2 = 1/(1.704992^2*L1), [description="Capacitance filter coefficient no.2"]
25+
C3 = 1.682, [description="Capacitance filter coefficient no.3"]
26+
C4 = 1/(1.179945^2*L2), [description="Capacitance filter coefficient no.4"]
27+
C5 = 0.7262, [description="Capacitance filter coefficient no.5"]
28+
end
29+
@components begin
30+
step = Step(height=1, start_time=1, smooth=false)
31+
source = Voltage()
32+
ground = Ground()
33+
resistor1 = Resistor(R=1)
34+
resistor2 = Resistor(R=1)
35+
inductor1 = Inductor(L=L1)
36+
inductor2 = Inductor(L=L2)
37+
capacitor1 = Capacitor(C=C1)
38+
capacitor2 = Capacitor(C=C2)
39+
capacitor3 = Capacitor(C=C3)
40+
capacitor4 = Capacitor(C=C4)
41+
capacitor5 = Capacitor(C=C5)
42+
end
43+
@equations begin
44+
connect(step.output, source.V)
45+
connect(resistor1.n, capacitor1.p)
46+
connect(capacitor1.n, ground.g)
47+
connect(inductor1.p, capacitor2.p)
48+
connect(inductor1.p, capacitor1.p)
49+
connect(inductor1.n, capacitor2.n)
50+
connect(capacitor2.n, capacitor3.p)
51+
connect(capacitor2.n, capacitor4.p)
52+
connect(capacitor2.n, inductor2.p)
53+
connect(inductor2.n, capacitor4.n)
54+
connect(capacitor4.n, capacitor5.p)
55+
connect(capacitor4.n, resistor2.p)
56+
connect(capacitor1.n, capacitor3.n)
57+
connect(capacitor1.n, capacitor5.n)
58+
connect(resistor2.n, capacitor1.n)
59+
connect(resistor1.p, source.p)
60+
connect(source.n, ground.g)
61+
end
62+
end
63+
64+
@mtkbuild model = CauerLowPassAnalog()
65+
66+
tspan = (0.0, 60.0)
67+
prob = ODEProblem(model, ModelingToolkit.missing_variable_defaults(model), tspan)
68+
sol = solve(prob, Rosenbrock23());
69+
Plots.plot(sol; idxs=[model.source.p.v, model.resistor2.p.v])
70+
Plots.savefig("cauer_low_pass_analog.png"); nothing # hide
71+
```
72+
73+
![Cauer Low Pass Analog Filter plot](cauer_low_pass_analog.png)
74+
75+
## Explanation
76+
77+
### Setting up the Environment
78+
79+
Each component needed for this example is defined in the [Electrical Components](@ref "ModelingToolkitStandardLibrary: Electrical Components") module with the exception of [`Blocks.Step`](@ref).
80+
These modules are loaded along with
81+
82+
```julia
83+
using ModelingToolkit
84+
using ModelingToolkitStandardLibrary.Blocks: Step
85+
using ModelingToolkitStandardLibrary.Electrical
86+
using OrdinaryDiffEq
87+
using Plots
88+
```
89+
90+
### Defining Independent Variable
91+
92+
As usual, you must specify the independent variable time, `t`.
93+
If prefered, you may alternatively run `using ModelingToolkitStandardLibrary.Blocks.t` to load [`Blocks.t`].
94+
95+
```julia
96+
@variables t
97+
```
98+
99+
### Defining the Model
100+
101+
This example uses the `@mtkmodel` macro to define the model.
102+
This is the recommended method for defining models using `ModelingToolkit` and
103+
provides several conveniences by way of reduced boilerplate when compared to past methods.
104+
You can name the model and prepare the scaffold which you'll fill in below.
105+
106+
```julia
107+
@mtkmodel CauerLowPassAnalog begin
108+
@parameters begin #= ... =# end
109+
@components begin #= ... =# end
110+
@equations begin #= ... =# end
111+
end
112+
```
113+
114+
#### Defining Model Parameters
115+
116+
There are a few interesting aspects to how the parameters are defined.
117+
118+
First, note that you are able to define parameters in terms of other paramters.
119+
In this example, capacitance coefficients `C2` and `C4` are defined in terms
120+
of inductance coefficients `L1` and `L2` respectively.
121+
122+
Second, note that you may optionally provide descriptions for each parameter.
123+
The descriptive string is stored with the parameter and can be easily retrieve e.g., by `ModelingToolkit.getdescription(L1)`.
124+
Storing and retrieving metadata can be useful as your team and models grow over time.
125+
The descriptive string acts as an active comment that never grows stale because it evolves with the model code itself.
126+
127+
```julia
128+
@mtkmodel CauerLowPassAnalog begin
129+
@parameters begin
130+
L1 = 1.304, [description="Inductance filter coefficient no.1"]
131+
L2 = 0.8586, [description="Inductance filter coefficient no.2"]
132+
C1 = 1.072, [description="Capacitance filter coefficient no.1"]
133+
C2 = 1/(1.704992^2*L1), [description="Capacitance filter coefficient no.2"]
134+
C3 = 1.682, [description="Capacitance filter coefficient no.3"]
135+
C4 = 1/(1.179945^2*L2), [description="Capacitance filter coefficient no.4"]
136+
C5 = 0.7262, [description="Capacitance filter coefficient no.5"]
137+
end
138+
@components begin #= ... =# end
139+
@equations begin #= ... =# end
140+
end
141+
```
142+
143+
#### Defining Model Components
144+
145+
Defining the components is rather straight forward now using your paramters defined above.
146+
Again, each of the components aside from [`Blocks.Step`](@ref) live in the [Electrical Components](@ref "ModelingToolkitStandardLibrary: Electrical Components") module.
147+
148+
```julia
149+
@mtkmodel CauerLowPassAnalog begin
150+
@parameters begin #= ... =# end
151+
@components begin
152+
step = Step(height=1, start_time=1, smooth=false)
153+
source = Voltage()
154+
ground = Ground()
155+
resistor1 = Resistor(R=1)
156+
resistor2 = Resistor(R=1)
157+
inductor1 = Inductor(L=L1)
158+
inductor2 = Inductor(L=L2)
159+
capacitor1 = Capacitor(C=C1)
160+
capacitor2 = Capacitor(C=C2)
161+
capacitor3 = Capacitor(C=C3)
162+
capacitor4 = Capacitor(C=C4)
163+
capacitor5 = Capacitor(C=C5)
164+
end
165+
@equations begin #= ... =# end
166+
end
167+
```
168+
169+
#### Defining Model Equations
170+
171+
Defining the equations simply requires connecting all the components.
172+
This is done with the `connect` method and knowledge of the component ports.
173+
If you are unsure what ports are available, see the components help section named "Connectors" to learn more.
174+
175+
```julia
176+
@mtkmodel CauerLowPassAnalog begin
177+
@parameters begin #= ... =# end
178+
@components begin #= ... =# end
179+
@equations begin
180+
connect(step.output, source.V)
181+
connect(resistor1.n, capacitor1.p)
182+
connect(capacitor1.n, ground.g)
183+
connect(inductor1.p, capacitor2.p)
184+
connect(inductor1.p, capacitor1.p)
185+
connect(inductor1.n, capacitor2.n)
186+
connect(capacitor2.n, capacitor3.p)
187+
connect(capacitor2.n, capacitor4.p)
188+
connect(capacitor2.n, inductor2.p)
189+
connect(inductor2.n, capacitor4.n)
190+
connect(capacitor4.n, capacitor5.p)
191+
connect(capacitor4.n, resistor2.p)
192+
connect(capacitor1.n, capacitor3.n)
193+
connect(capacitor1.n, capacitor5.n)
194+
connect(resistor2.n, capacitor1.n)
195+
connect(resistor1.p, source.p)
196+
connect(source.n, ground.g)
197+
end
198+
end
199+
```
200+
201+
### Defining the Problem
202+
203+
Now the convenience of `@mtkmodel` shines.
204+
There is no need to declare an `ODESystem` with a `name`, a list of connections, variables and systems.
205+
The `@mtkmodel` macro has captured all that information for you, and its complement, `@mtkbuild`, will handle the rest.
206+
207+
```julia
208+
@mtkbuild model = CauerLowPassAnalog()
209+
```
210+
211+
You now have a `model` which can be used to construct an `ODEProblem`.
212+
213+
```julia
214+
prob = ODEProblem(model, ModelingToolkit.missing_variable_defaults(model), (0.0, 60.0))
215+
```
216+
217+
What is happening with `ModelingToolkit.missing_variable_defaults(model)`?
218+
If you try and run `ODEProblem(model, (0.0, 60.0))` then you will encounter the error:
219+
220+
```plaintext
221+
ERROR: ArgumentError: Equations (7), states (7), and initial conditions (2) are of different lengths.
222+
```
223+
224+
This occurs because dummy derivatives where generated without defined initial values.
225+
Thankfully, `ModelingToolkit` provides `missing_variable_defaults` as a solution to this problem.
226+
See [ModelingToolkit FAQ](https://docs.sciml.ai/ModelingToolkit/stable/basics/FAQ/) for more information on this and other common questions.
227+
228+
### Solving the Problem
229+
230+
You are finally ready to solve!
231+
However, if you try to run `solve(prob)` then you will encounter an error.
232+
233+
```plaintext
234+
ERROR: Default algorithm choices require DifferentialEquations.jl.
235+
Please specify an algorithm (e.g., `solve(prob, Tsit5())` or
236+
`init(prob, Tsit5())` for an ODE) or import DifferentialEquations
237+
directly.
238+
239+
You can find the list of available solvers at https://diffeq.sciml.ai/stable/solvers/ode_solve/
240+
and its associated pages.
241+
```
242+
243+
Thankfully, the error message provides instructions for next steps along with a helpful link.
244+
The issue is resolved by explicitly specifying an algorithm, here `Rosenbrock23`.
245+
246+
You should now have a solution object with a successful return code.
247+
248+
```@example cauer_low_pass_analog
249+
SciMLBase.successful_retcode(sol)
250+
```

0 commit comments

Comments
 (0)