Skip to content

Commit e60a063

Browse files
Merge pull request #1993 from SciML/docs
Reorganize the documentation
2 parents 0d8d1f6 + 33020e0 commit e60a063

13 files changed

+267
-54
lines changed

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e"
1212
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
1313
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1414
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
15+
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
1516
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
1617

1718
[compat]

docs/pages.jl

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
pages = [
22
"Home" => "index.md",
3-
"Symbolic Modeling Tutorials" => Any["tutorials/ode_modeling.md",
4-
"tutorials/spring_mass.md",
5-
"tutorials/acausal_components.md",
6-
"tutorials/higher_order.md",
7-
"tutorials/tearing_parallelism.md",
8-
"tutorials/nonlinear.md",
9-
"tutorials/optimization.md",
10-
"tutorials/stochastic_diffeq.md",
11-
"tutorials/parameter_identifiability.md"],
12-
"ModelingToolkitize Tutorials" => Any["mtkitize_tutorials/modelingtoolkitize.md",
13-
"mtkitize_tutorials/modelingtoolkitize_index_reduction.md",
14-
"mtkitize_tutorials/sparse_jacobians.md"],
3+
"tutorials/ode_modeling.md",
4+
"Tutorials" => Any["tutorials/acausal_components.md",
5+
"tutorials/nonlinear.md",
6+
"tutorials/optimization.md",
7+
"tutorials/modelingtoolkitize.md",
8+
"tutorials/stochastic_diffeq.md",
9+
"tutorials/parameter_identifiability.md"],
10+
"Examples" => Any["Basic Examples" => Any["examples/higher_order.md",
11+
"examples/spring_mass.md",
12+
"examples/modelingtoolkitize_index_reduction.md"],
13+
"Advanced Examples" => Any["examples/tearing_parallelism.md",
14+
"examples/sparse_jacobians.md",
15+
"examples/perturbation.md"]],
1516
"Basics" => Any["basics/AbstractSystem.md",
1617
"basics/ContextualVariables.md",
1718
"basics/Variable_metadata.md",

docs/src/tutorials/higher_order.md renamed to docs/src/examples/higher_order.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ ModelingToolkit has a system for transformations of mathematical
44
systems. These transformations allow for symbolically changing
55
the representation of the model to problems that are easier to
66
numerically solve. One simple to demonstrate transformation is the
7-
`ode_order_lowering` transformation that sends an Nth order ODE
7+
`structural_simplify` with does a lot of tricks, one being the
8+
transformation that sends an Nth order ODE
89
to a 1st order ODE.
910

1011
To see this, let's define a second order riff on the Lorenz equations.
@@ -33,7 +34,7 @@ Now let's transform this into the `ODESystem` of first order components.
3334
We do this by simply calling `ode_order_lowering`:
3435

3536
```@example orderlowering
36-
sys = ode_order_lowering(sys)
37+
sys = structural_simplify(sys)
3738
```
3839

3940
Now we can directly numerically solve the lowered system. Note that,
File renamed without changes.

docs/src/examples/perturbation.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# [Symbolic-Numeric Perturbation Theory for ODEs](@id perturb_diff)
2+
3+
## Prelims
4+
5+
In the previous tutorial, [Mixed Symbolic-Numeric Perturbation Theory](https://symbolics.juliasymbolics.org/stable/examples/perturbation), we discussed how to solve algebraic equations using **Symbolics.jl**. Here, our goal is to extend the method to differential equations. First, we import the following helper functions that were introduced in [Mixed Symbolic/Numerical Methods for Perturbation Theory - Algebraic Equations](@ref perturb_alg):
6+
7+
```julia
8+
using Symbolics, SymbolicUtils
9+
10+
def_taylor(x, ps) = sum([a*x^i for (i,a) in enumerate(ps)])
11+
def_taylor(x, ps, p₀) = p₀ + def_taylor(x, ps)
12+
13+
function collect_powers(eq, x, ns; max_power=100)
14+
eq = substitute(expand(eq), Dict(x^j => 0 for j=last(ns)+1:max_power))
15+
16+
eqs = []
17+
for i in ns
18+
powers = Dict(x^j => (i==j ? 1 : 0) for j=1:last(ns))
19+
push!(eqs, substitute(eq, powers))
20+
end
21+
eqs
22+
end
23+
24+
function solve_coef(eqs, ps)
25+
vals = Dict()
26+
27+
for i = 1:length(ps)
28+
eq = substitute(eqs[i], vals)
29+
vals[ps[i]] = Symbolics.solve_for(eq ~ 0, ps[i])
30+
end
31+
vals
32+
end
33+
```
34+
35+
## The Trajectory of a Ball!
36+
37+
In the first two examples, we applied the perturbation method to algebraic problems. However, the main power of the perturbation method is to solve differential equations (usually ODEs, but also occasionally PDEs). Surprisingly, the main procedure developed to solve algebraic problems works well for differential equations. In fact, we will use the same two helper functions, `collect_powers` and `solve_coef`. The main difference is in the way we expand the dependent variables. For algebraic problems, the coefficients of $\epsilon$ are constants; whereas, for differential equations, they are functions of the dependent variable (usually time).
38+
39+
As the first ODE example, we have chosen a simple and well-behaved problem, which is a variation of a standard first-year physics problem: what is the trajectory of an object (say, a ball or a rocket) thrown vertically at velocity $v$ from the surface of a planet? Assuming a constant acceleration of gravity, $g$, every burgeoning physicist knows the answer: $x(t) = x(0) + vt - \frac{1}{2}gt^2$. However, what happens if $g$ is not constant? Specifically, $g$ is inversely proportional to the distant from the center of the planet. If $v$ is large and the projectile travels a large fraction of the radius of the planet, the assumption of constant gravity does not hold anymore. However, unless $v$ is large compared to the escape velocity, the correction is usually small. After simplifications and change of variables to dimensionless, the problem becomes
40+
41+
```math
42+
\ddot{x}(t) = -\frac{1}{(1 + \epsilon x(t))^2}
43+
```
44+
45+
with the initial conditions $x(0) = 0$, and $\dot{x}(0) = 1$. Note that for $\epsilon = 0$, this equation transforms back to the standard one. Let's start with defining the variables
46+
47+
```julia
48+
n = 3
49+
@variables ϵ t y[1:n](t) ∂∂y[1:n](t)
50+
```
51+
52+
Next, we define $x$.
53+
54+
```julia
55+
x = def_taylor(ϵ, y[3:end], y[2])
56+
```
57+
58+
We need the second derivative of `x`. It may seem that we can do this using `Differential(t)`; however, this operation needs to wait for a few steps because we need to manipulate the differentials as separate variables. Instead, we define dummy variables `∂∂y` as the placeholder for the second derivatives and define
59+
60+
```julia
61+
∂∂x = def_taylor(ϵ, ∂∂y[3:end], ∂∂y[2])
62+
```
63+
64+
as the second derivative of `x`. After rearrangement, our governing equation is $\ddot{x}(t)(1 + \epsilon x(t))^{-2} + 1 = 0$, or
65+
66+
```julia
67+
eq = ∂∂x * (1 + ϵ*x)^2 + 1
68+
```
69+
70+
The next two steps are the same as the ones for algebraic equations (note that we pass `1:n` to `collect_powers` because the zeroth order term is needed here)
71+
72+
```julia
73+
eqs = collect_powers(eq, ϵ, 1:n)
74+
```
75+
76+
and,
77+
78+
```julia
79+
vals = solve_coef(eqs, ∂∂y)
80+
```
81+
82+
Our system of ODEs is forming. Now is the time to convert `∂∂`s to the correct **Symbolics.jl** form by substitution:
83+
84+
```julia
85+
D = Differential(t)
86+
subs = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
87+
eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
88+
```
89+
90+
We are nearly there! From this point on, the rest is standard ODE solving procedures. Potentially we can use a symbolic ODE solver to find a closed form solution to this problem. However, **Symbolics.jl** currently does not support this functionality. Instead, we solve the problem numerically. We form an `ODESystem`, lower the order (convert second derivatives to first), generate an `ODEProblem` (after passing the correct initial conditions), and, finally, solve it.
91+
92+
```julia
93+
using ModelingToolkit, DifferentialEquations
94+
95+
@named sys = ODESystem(eqs, t)
96+
sys = structural_simplify(sys)
97+
states(sys)
98+
```
99+
100+
```julia
101+
# the initial conditions
102+
# everything is zero except the initial velocity
103+
u0 = zeros(2n+2)
104+
u0[3] = 1.0 # y₀ˍt
105+
106+
prob = ODEProblem(sys, u0, (0, 3.0))
107+
sol = solve(prob; dtmax=0.01)
108+
```
109+
110+
Finally, we calculate the solution to the problem as a function of `ϵ` by substituting the solution to the ODE system back into the defining equation for `x`. Note that `𝜀` is a number, compared to `ϵ`, which is a symbolic variable.
111+
112+
```julia
113+
X = 𝜀 -> sum([𝜀^(i-1) * sol[y[i]] for i in eachindex(y)])
114+
```
115+
116+
Using `X`, we can plot the trajectory for a range of $𝜀$s.
117+
118+
```julia
119+
using Plots
120+
121+
plot(sol.t, hcat([X(𝜀) for 𝜀 = 0.0:0.1:0.5]...))
122+
```
123+
124+
As expected, as `𝜀` becomes larger (meaning the gravity is less with altitude), the object goes higher and stays up for a longer duration. Of course, we could have solved the problem directly using as ODE solver. One of the benefits of the perturbation method is that we need to run the ODE solver only once and then can just calculate the answer for different values of `𝜀`; whereas, if we had used the direct method, we would need to run the solver once for each value of `𝜀`.
125+
126+
## A Weakly Nonlinear Oscillator
127+
128+
For the next example, we have chosen a simple example from a very important class of problems, the nonlinear oscillators. As we will see, perturbation theory has difficulty providing a good solution to this problem, but the process is instructive. This example closely follows the chapter 7.6 of *Nonlinear Dynamics and Chaos* by Steven Strogatz.
129+
130+
The goal is to solve $\ddot{x} + 2\epsilon\dot{x} + x = 0$, where the dot signifies time-derivatives and the initial conditions are $x(0) = 0$ and $\dot{x}(0) = 1$. If $\epsilon = 0$, the problem reduces to the simple linear harmonic oscillator with the exact solution $x(t) = \sin(t)$. We follow the same steps as the previous example.
131+
132+
```julia
133+
n = 3
134+
@variables ϵ t y[1:n](t) ∂y[1:n] ∂∂y[1:n]
135+
x = def_taylor(ϵ, y[3:end], y[2])
136+
∂x = def_taylor(ϵ, ∂y[3:end], ∂y[2])
137+
∂∂x = def_taylor(ϵ, ∂∂y[3:end], ∂∂y[2])
138+
```
139+
140+
This time we also need the first derivative terms. Continuing,
141+
142+
```julia
143+
eq = ∂∂x + 2*ϵ*∂x + x
144+
eqs = collect_powers(eq, ϵ, 0:n)
145+
vals = solve_coef(eqs, ∂∂y)
146+
```
147+
148+
Next, we need to replace ``s and `∂∂`s with their **Symbolics.jl** counterparts:
149+
150+
```julia
151+
D = Differential(t)
152+
subs1 = Dict(∂y[i] => D(y[i]) for i in eachindex(y))
153+
subs2 = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
154+
subs = subs1 subs2
155+
eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
156+
```
157+
158+
We continue with converting 'eqs' to an `ODEProblem`, solving it, and finally plot the results against the exact solution to the original problem, which is $x(t, \epsilon) = (1 - \epsilon)^{-1/2} e^{-\epsilon t} \sin((1- \epsilon^2)^{1/2}t)$,
159+
160+
```julia
161+
@named sys = ODESystem(eqs, t)
162+
sys = structural_simplify(sys)
163+
```
164+
165+
```julia
166+
# the initial conditions
167+
u0 = zeros(2n+2)
168+
u0[3] = 1.0 # y₀ˍt
169+
170+
prob = ODEProblem(sys, u0, (0, 50.0))
171+
sol = solve(prob; dtmax=0.01)
172+
173+
X = 𝜀 -> sum([𝜀^(i-1) * sol[y[i]] for i in eachindex(y)])
174+
T = sol.t
175+
Y = 𝜀 -> exp.(-𝜀*T) .* sin.(sqrt(1 - 𝜀^2)*T) / sqrt(1 - 𝜀^2) # exact solution
176+
177+
plot(sol.t, [Y(0.1), X(0.1)])
178+
```
179+
180+
The figure is similar to Figure 7.6.2 in *Nonlinear Dynamics and Chaos*. The two curves fit well for the first couple of cycles, but then the perturbation method curve diverges from the true solution. The main reason is that the problem has two or more time-scales that introduce secular terms in the solution. One solution is to explicitly account for the two time scales and use an analytic method called *two-timing*.
File renamed without changes.
File renamed without changes.
File renamed without changes.

docs/src/mtkitize_tutorials/modelingtoolkitize.md

Lines changed: 0 additions & 31 deletions
This file was deleted.

docs/src/tutorials/acausal_components.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# [Acausal Component-Based Modeling the RC Circuit](@id acausal)
1+
# [Acausal Component-Based Modeling](@id acausal)
22

33
In this tutorial we will build a hierarchical acausal component-based model of
44
the RC circuit. The RC circuit is a simple example where we connect a resistor
@@ -14,7 +14,7 @@ equalities before solving. Let's see this in action.
1414
This tutorial teaches how to build the entire RC circuit from scratch.
1515
However, to simulate electrical components with more ease, check out the
1616
[ModelingToolkitStandardLibrary.jl](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/)
17-
which includes a
17+
which includes a
1818
[tutorial for simulating RC circuits with pre-built components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/rc_circuit/)
1919

2020
## Copy-Paste Example

0 commit comments

Comments
 (0)