-
-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: add cauer low pass analog filter example #240
Open
jvaverka
wants to merge
5
commits into
SciML:main
Choose a base branch
from
jvaverka:add-example-doc-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
df29058
docs: add cauer low pass analog filter example
jvaverka b50d386
Update docs/src/tutorials/cauer_low_pass_analog.md
jvaverka ae2dc87
Update docs/src/tutorials/cauer_low_pass_analog.md
jvaverka 7a90bc4
docs: clarify cauer low pass analog filter intro
jvaverka 1f2e0a5
docs: add cauer low pass analog filter exmaple to pages
jvaverka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
# Cauer Low Pass Analog Filter | ||
|
||
The example Cauer Filter is a low-pass filter of the fifth order. | ||
It is realized using an analog network. | ||
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. | ||
The step response is calculated. | ||
|
||
## Copy-Pastable Example | ||
|
||
```@example cauer_low_pass_analog | ||
using ModelingToolkit | ||
using ModelingToolkitStandardLibrary.Blocks: Step | ||
using ModelingToolkitStandardLibrary.Electrical | ||
using OrdinaryDiffEq | ||
using Plots | ||
|
||
@variables t | ||
|
||
@mtkmodel CauerLowPassAnalog begin | ||
@parameters begin | ||
L1 = 1.304, [description="Inductance filter coefficient no.1"] | ||
L2 = 0.8586, [description="Inductance filter coefficient no.2"] | ||
C1 = 1.072, [description="Capacitance filter coefficient no.1"] | ||
C2 = 1/(1.704992^2*L1), [description="Capacitance filter coefficient no.2"] | ||
C3 = 1.682, [description="Capacitance filter coefficient no.3"] | ||
C4 = 1/(1.179945^2*L2), [description="Capacitance filter coefficient no.4"] | ||
C5 = 0.7262, [description="Capacitance filter coefficient no.5"] | ||
end | ||
@components begin | ||
step = Step(height=1, start_time=1, smooth=false) | ||
source = Voltage() | ||
ground = Ground() | ||
resistor1 = Resistor(R=1) | ||
resistor2 = Resistor(R=1) | ||
inductor1 = Inductor(L=L1) | ||
inductor2 = Inductor(L=L2) | ||
capacitor1 = Capacitor(C=C1) | ||
capacitor2 = Capacitor(C=C2) | ||
capacitor3 = Capacitor(C=C3) | ||
capacitor4 = Capacitor(C=C4) | ||
capacitor5 = Capacitor(C=C5) | ||
end | ||
@equations begin | ||
connect(step.output, source.V) | ||
connect(resistor1.n, capacitor1.p) | ||
connect(capacitor1.n, ground.g) | ||
connect(inductor1.p, capacitor2.p) | ||
connect(inductor1.p, capacitor1.p) | ||
connect(inductor1.n, capacitor2.n) | ||
connect(capacitor2.n, capacitor3.p) | ||
connect(capacitor2.n, capacitor4.p) | ||
connect(capacitor2.n, inductor2.p) | ||
connect(inductor2.n, capacitor4.n) | ||
connect(capacitor4.n, capacitor5.p) | ||
connect(capacitor4.n, resistor2.p) | ||
connect(capacitor1.n, capacitor3.n) | ||
connect(capacitor1.n, capacitor5.n) | ||
connect(resistor2.n, capacitor1.n) | ||
connect(resistor1.p, source.p) | ||
connect(source.n, ground.g) | ||
end | ||
end | ||
|
||
@mtkbuild model = CauerLowPassAnalog() | ||
|
||
tspan = (0.0, 60.0) | ||
prob = ODEProblem(model, ModelingToolkit.missing_variable_defaults(model), tspan) | ||
sol = solve(prob, Rosenbrock23()); | ||
Plots.plot(sol; idxs=[model.source.p.v, model.resistor2.p.v]) | ||
Plots.savefig("cauer_low_pass_analog.png"); nothing # hide | ||
``` | ||
|
||
![Cauer Low Pass Analog Filter plot](cauer_low_pass_analog.png) | ||
|
||
## Explanation | ||
|
||
### Setting up the Environment | ||
|
||
Each component needed for this example is defined in the [Electrical Components](@ref "ModelingToolkitStandardLibrary: Electrical Components") module with the exception of [`Blocks.Step`](@ref). | ||
These modules are loaded along with | ||
|
||
```julia | ||
using ModelingToolkit | ||
using ModelingToolkitStandardLibrary.Blocks: Step | ||
using ModelingToolkitStandardLibrary.Electrical | ||
using OrdinaryDiffEq | ||
using Plots | ||
``` | ||
|
||
### Defining Independent Variable | ||
|
||
As usual, you must specify the independent variable time, `t`. | ||
If prefered, you may alternatively run `using ModelingToolkitStandardLibrary.Blocks.t` to load [`Blocks.t`]. | ||
|
||
```julia | ||
@variables t | ||
``` | ||
|
||
### Defining the Model | ||
|
||
This example uses the `@mtkmodel` macro to define the model. | ||
This is the recommended method for defining models using `ModelingToolkit` and | ||
provides several conveniences by way of reduced boilerplate when compared to past methods. | ||
You can name the model and prepare the scaffold which you'll fill in below. | ||
|
||
```julia | ||
@mtkmodel CauerLowPassAnalog begin | ||
@parameters begin #= ... =# end | ||
@components begin #= ... =# end | ||
@equations begin #= ... =# end | ||
end | ||
``` | ||
|
||
#### Defining Model Parameters | ||
|
||
There are a few interesting aspects to how the parameters are defined. | ||
|
||
First, note that you are able to define parameters in terms of other paramters. | ||
In this example, capacitance coefficients `C2` and `C4` are defined in terms | ||
of inductance coefficients `L1` and `L2` respectively. | ||
|
||
Second, note that you may optionally provide descriptions for each parameter. | ||
The descriptive string is stored with the parameter and can be easily retrieve e.g., by `ModelingToolkit.getdescription(L1)`. | ||
Storing and retrieving metadata can be useful as your team and models grow over time. | ||
The descriptive string acts as an active comment that never grows stale because it evolves with the model code itself. | ||
|
||
```julia | ||
@mtkmodel CauerLowPassAnalog begin | ||
@parameters begin | ||
L1 = 1.304, [description="Inductance filter coefficient no.1"] | ||
L2 = 0.8586, [description="Inductance filter coefficient no.2"] | ||
C1 = 1.072, [description="Capacitance filter coefficient no.1"] | ||
C2 = 1/(1.704992^2*L1), [description="Capacitance filter coefficient no.2"] | ||
C3 = 1.682, [description="Capacitance filter coefficient no.3"] | ||
C4 = 1/(1.179945^2*L2), [description="Capacitance filter coefficient no.4"] | ||
C5 = 0.7262, [description="Capacitance filter coefficient no.5"] | ||
end | ||
@components begin #= ... =# end | ||
@equations begin #= ... =# end | ||
end | ||
``` | ||
|
||
#### Defining Model Components | ||
|
||
Defining the components is rather straight forward now using your paramters defined above. | ||
Again, each of the components aside from [`Blocks.Step`](@ref) live in the [Electrical Components](@ref "ModelingToolkitStandardLibrary: Electrical Components") module. | ||
|
||
```julia | ||
@mtkmodel CauerLowPassAnalog begin | ||
@parameters begin #= ... =# end | ||
@components begin | ||
step = Step(height=1, start_time=1, smooth=false) | ||
source = Voltage() | ||
ground = Ground() | ||
resistor1 = Resistor(R=1) | ||
resistor2 = Resistor(R=1) | ||
inductor1 = Inductor(L=L1) | ||
inductor2 = Inductor(L=L2) | ||
capacitor1 = Capacitor(C=C1) | ||
capacitor2 = Capacitor(C=C2) | ||
capacitor3 = Capacitor(C=C3) | ||
capacitor4 = Capacitor(C=C4) | ||
capacitor5 = Capacitor(C=C5) | ||
end | ||
@equations begin #= ... =# end | ||
end | ||
``` | ||
|
||
#### Defining Model Equations | ||
|
||
Defining the equations simply requires connecting all the components. | ||
This is done with the `connect` method and knowledge of the component ports. | ||
If you are unsure what ports are available, see the components help section named "Connectors" to learn more. | ||
|
||
```julia | ||
@mtkmodel CauerLowPassAnalog begin | ||
@parameters begin #= ... =# end | ||
@components begin #= ... =# end | ||
@equations begin | ||
connect(step.output, source.V) | ||
connect(resistor1.n, capacitor1.p) | ||
connect(capacitor1.n, ground.g) | ||
connect(inductor1.p, capacitor2.p) | ||
connect(inductor1.p, capacitor1.p) | ||
connect(inductor1.n, capacitor2.n) | ||
connect(capacitor2.n, capacitor3.p) | ||
connect(capacitor2.n, capacitor4.p) | ||
connect(capacitor2.n, inductor2.p) | ||
connect(inductor2.n, capacitor4.n) | ||
connect(capacitor4.n, capacitor5.p) | ||
connect(capacitor4.n, resistor2.p) | ||
connect(capacitor1.n, capacitor3.n) | ||
connect(capacitor1.n, capacitor5.n) | ||
connect(resistor2.n, capacitor1.n) | ||
connect(resistor1.p, source.p) | ||
connect(source.n, ground.g) | ||
end | ||
end | ||
``` | ||
|
||
### Defining the Problem | ||
|
||
Now the convenience of `@mtkmodel` shines. | ||
There is no need to declare an `ODESystem` with a `name`, a list of connections, variables and systems. | ||
The `@mtkmodel` macro has captured all that information for you, and its complement, `@mtkbuild`, will handle the rest. | ||
|
||
```julia | ||
@mtkbuild model = CauerLowPassAnalog() | ||
``` | ||
|
||
You now have a `model` which can be used to construct an `ODEProblem`. | ||
|
||
```julia | ||
prob = ODEProblem(model, ModelingToolkit.missing_variable_defaults(model), (0.0, 60.0)) | ||
``` | ||
|
||
What is happening with `ModelingToolkit.missing_variable_defaults(model)`? | ||
If you try and run `ODEProblem(model, (0.0, 60.0))` then you will encounter the error: | ||
|
||
```plaintext | ||
ERROR: ArgumentError: Equations (7), states (7), and initial conditions (2) are of different lengths. | ||
``` | ||
|
||
This occurs because dummy derivatives were generated without defined initial values. | ||
Thankfully, `ModelingToolkit` provides `missing_variable_defaults` as a solution to this problem. | ||
See [ModelingToolkit FAQ](https://docs.sciml.ai/ModelingToolkit/stable/basics/FAQ/) for more information on this and other common questions. | ||
|
||
### Solving the Problem | ||
|
||
You are finally ready to solve! | ||
However, if you try to run `solve(prob)` then you will encounter an error. | ||
|
||
```plaintext | ||
ERROR: Default algorithm choices require DifferentialEquations.jl. | ||
Please specify an algorithm (e.g., `solve(prob, Tsit5())` or | ||
`init(prob, Tsit5())` for an ODE) or import DifferentialEquations | ||
directly. | ||
|
||
You can find the list of available solvers at https://diffeq.sciml.ai/stable/solvers/ode_solve/ | ||
and its associated pages. | ||
``` | ||
|
||
Thankfully, the error message provides instructions for next steps along with a helpful link. | ||
The issue is resolved by explicitly specifying an algorithm, here `Rosenbrock23`. | ||
|
||
You should now have a solution object with a successful return code. | ||
|
||
```@example cauer_low_pass_analog | ||
SciMLBase.successful_retcode(sol) | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can suggest
using DifferentialEquations
andsolve(prob)
instead of OrdinaryDiffEq?(While keeping the note+url on where they can find all the solvers they can specify if they want to.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean for all the tutorials? Seems like a good idea to me. My understanding of the trade-off being
OrdinaryDiffEq.jl
is more lightweight and therefore faster to load, butDifferentialEquations.jl
is more robust and offers conveniences to new users - which is useful for readers going through the tutorials.Should I leave the note, and open an issue to switch over all tutorials and update
docs/Project.toml
?