Skip to content

Commit a7adb53

Browse files
committed
fix second part of example on perturbation theory for ODEs
1 parent 8f8cf61 commit a7adb53

File tree

1 file changed

+32
-31
lines changed

1 file changed

+32
-31
lines changed

docs/src/examples/perturbation.md

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
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](https://symbolics.juliasymbolics.org/stable/examples/perturbation/):
66

7-
```julia
8-
using Symbolics, SymbolicUtils
7+
```@example perturbation
8+
using Symbolics
99
1010
def_taylor(x, ps) = sum([a * x^(i - 1) for (i, a) in enumerate(ps)])
1111
@@ -17,10 +17,10 @@ function collect_powers(eq, x, ns; max_power = 100)
1717
powers = Dict(x^j => (i == j ? 1 : 0) for j in 1:last(ns))
1818
e = substitute(eq, powers)
1919
20-
# manually remove zeroth order from higher orders
21-
if 0 in ns && i != 0
22-
e = e - eqs[1]
23-
end
20+
# manually remove zeroth order from higher orders
21+
if 0 in ns && i != 0
22+
e = e - eqs[1]
23+
end
2424
2525
push!(eqs, e)
2626
end
@@ -50,7 +50,7 @@ As the first ODE example, we have chosen a simple and well-behaved problem, whic
5050

5151
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
5252

53-
```julia
53+
```@example perturbation
5454
using ModelingToolkit: t_nounits as t, D_nounits as D
5555
order = 2
5656
n = order + 1
@@ -59,69 +59,69 @@ n = order + 1
5959

6060
Next, we define $x$.
6161

62-
```julia
62+
```@example perturbation
6363
x = def_taylor(ϵ, y)
6464
```
6565

6666
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
6767

68-
```julia
68+
```@example perturbation
6969
∂∂x = def_taylor(ϵ, ∂∂y)
7070
```
7171

7272
as the second derivative of `x`. After rearrangement, our governing equation is $\ddot{x}(t)(1 + \epsilon x(t))^{-2} + 1 = 0$, or
7373

74-
```julia
74+
```@example perturbation
7575
eq = ∂∂x * (1 + ϵ * x)^2 + 1
7676
```
7777

7878
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)
7979

80-
```julia
80+
```@example perturbation
8181
eqs = collect_powers(eq, ϵ, 0:order)
8282
```
8383

8484
and,
8585

86-
```julia
86+
```@example perturbation
8787
vals = solve_coef(eqs, ∂∂y)
8888
```
8989

9090
Our system of ODEs is forming. Now is the time to convert `∂∂`s to the correct **Symbolics.jl** form by substitution:
9191

92-
```julia
92+
```@example perturbation
9393
subs = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
9494
eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
9595
```
9696

9797
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.
9898

99-
```julia
99+
```@example perturbation
100100
using ModelingToolkit, DifferentialEquations
101101
102102
@mtkbuild sys = ODESystem(eqs, t)
103103
unknowns(sys)
104104
```
105105

106-
```julia
106+
```@example perturbation
107107
# the initial conditions
108108
# everything is zero except the initial velocity
109109
u0 = zeros(2order + 2)
110110
u0[2] = 1.0 # yˍt₁
111111
112112
prob = ODEProblem(sys, u0, (0, 3.0))
113-
sol = solve(prob; dtmax = 0.01)
113+
sol = solve(prob; dtmax = 0.01);
114114
```
115115

116116
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.
117117

118-
```julia
118+
```@example perturbation
119119
X = 𝜀 -> sum([𝜀^(i - 1) * sol[yi] for (i, yi) in enumerate(y)])
120120
```
121121

122122
Using `X`, we can plot the trajectory for a range of $𝜀$s.
123123

124-
```julia
124+
```@example perturbation
125125
using Plots
126126
127127
plot(sol.t, hcat([X(𝜀) for 𝜀 in 0.0:0.1:0.5]...))
@@ -135,25 +135,26 @@ For the next example, we have chosen a simple example from a very important clas
135135

136136
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.
137137

138-
```julia
139-
n = 3
140-
@variables ϵ t y[1:n](t) ∂y[1:n] ∂∂y[1:n]
141-
x = def_taylor(ϵ, y[3:end], y[2])
142-
∂x = def_taylor(ϵ, ∂y[3:end], ∂y[2])
143-
∂∂x = def_taylor(ϵ, ∂∂y[3:end], ∂∂y[2])
138+
```@example perturbation
139+
order = 2
140+
n = order + 1
141+
@variables ϵ (y(t))[1:n] (∂y)[1:n] (∂∂y)[1:n]
142+
x = def_taylor(ϵ, y)
143+
∂x = def_taylor(ϵ, ∂y)
144+
∂∂x = def_taylor(ϵ, ∂∂y)
144145
```
145146

146147
This time we also need the first derivative terms. Continuing,
147148

148-
```julia
149+
```@example perturbation
149150
eq = ∂∂x + 2 * ϵ * ∂x + x
150151
eqs = collect_powers(eq, ϵ, 0:n)
151152
vals = solve_coef(eqs, ∂∂y)
152153
```
153154

154155
Next, we need to replace ``s and `∂∂`s with their **Symbolics.jl** counterparts:
155156

156-
```julia
157+
```@example perturbation
157158
subs1 = Dict(∂y[i] => D(y[i]) for i in eachindex(y))
158159
subs2 = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
159160
subs = subs1 ∪ subs2
@@ -162,19 +163,19 @@ eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
162163

163164
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)$,
164165

165-
```julia
166+
```@example perturbation
166167
@mtkbuild sys = ODESystem(eqs, t)
167168
```
168169

169-
```julia
170+
```@example perturbation
170171
# the initial conditions
171-
u0 = zeros(2n + 2)
172-
u0[3] = 1.0 # y₀ˍt
172+
u0 = zeros(2order + 2)
173+
u0[1] = 1.0 # yˍt₁
173174
174175
prob = ODEProblem(sys, u0, (0, 50.0))
175176
sol = solve(prob; dtmax = 0.01)
176177
177-
X = 𝜀 -> sum([𝜀^(i - 1) * sol[y[i]] for i in eachindex(y)])
178+
X = 𝜀 -> sum([𝜀^(i - 1) * sol[yi] for (i, yi) in enumerate(y)])
178179
T = sol.t
179180
Y = 𝜀 -> exp.(-𝜀 * T) .* sin.(sqrt(1 - 𝜀^2) * T) / sqrt(1 - 𝜀^2) # exact solution
180181

0 commit comments

Comments
 (0)