You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/src/examples/perturbation.md
+32-31Lines changed: 32 additions & 31 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,8 +4,8 @@
4
4
5
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](https://symbolics.juliasymbolics.org/stable/examples/perturbation/):
6
6
7
-
```julia
8
-
using Symbolics, SymbolicUtils
7
+
```@example perturbation
8
+
using Symbolics
9
9
10
10
def_taylor(x, ps) = sum([a * x^(i - 1) for (i, a) in enumerate(ps)])
powers = Dict(x^j => (i == j ? 1 : 0) for j in 1:last(ns))
18
18
e = substitute(eq, powers)
19
19
20
-
# manually remove zeroth order from higher orders
21
-
if0in 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
24
24
25
25
push!(eqs, e)
26
26
end
@@ -50,7 +50,7 @@ As the first ODE example, we have chosen a simple and well-behaved problem, whic
50
50
51
51
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
52
52
53
-
```julia
53
+
```@example perturbation
54
54
using ModelingToolkit: t_nounits as t, D_nounits as D
55
55
order = 2
56
56
n = order + 1
@@ -59,69 +59,69 @@ n = order + 1
59
59
60
60
Next, we define $x$.
61
61
62
-
```julia
62
+
```@example perturbation
63
63
x = def_taylor(ϵ, y)
64
64
```
65
65
66
66
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
67
67
68
-
```julia
68
+
```@example perturbation
69
69
∂∂x = def_taylor(ϵ, ∂∂y)
70
70
```
71
71
72
72
as the second derivative of `x`. After rearrangement, our governing equation is $\ddot{x}(t)(1 + \epsilon x(t))^{-2} + 1 = 0$, or
73
73
74
-
```julia
74
+
```@example perturbation
75
75
eq = ∂∂x * (1 + ϵ * x)^2 + 1
76
76
```
77
77
78
78
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)
79
79
80
-
```julia
80
+
```@example perturbation
81
81
eqs = collect_powers(eq, ϵ, 0:order)
82
82
```
83
83
84
84
and,
85
85
86
-
```julia
86
+
```@example perturbation
87
87
vals = solve_coef(eqs, ∂∂y)
88
88
```
89
89
90
90
Our system of ODEs is forming. Now is the time to convert `∂∂`s to the correct **Symbolics.jl** form by substitution:
91
91
92
-
```julia
92
+
```@example perturbation
93
93
subs = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
94
94
eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
95
95
```
96
96
97
97
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.
98
98
99
-
```julia
99
+
```@example perturbation
100
100
using ModelingToolkit, DifferentialEquations
101
101
102
102
@mtkbuild sys = ODESystem(eqs, t)
103
103
unknowns(sys)
104
104
```
105
105
106
-
```julia
106
+
```@example perturbation
107
107
# the initial conditions
108
108
# everything is zero except the initial velocity
109
109
u0 = zeros(2order + 2)
110
110
u0[2] = 1.0 # yˍt₁
111
111
112
112
prob = ODEProblem(sys, u0, (0, 3.0))
113
-
sol =solve(prob; dtmax =0.01)
113
+
sol = solve(prob; dtmax = 0.01);
114
114
```
115
115
116
116
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.
117
117
118
-
```julia
118
+
```@example perturbation
119
119
X = 𝜀 -> sum([𝜀^(i - 1) * sol[yi] for (i, yi) in enumerate(y)])
120
120
```
121
121
122
122
Using `X`, we can plot the trajectory for a range of $𝜀$s.
123
123
124
-
```julia
124
+
```@example perturbation
125
125
using Plots
126
126
127
127
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
135
135
136
136
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.
137
137
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)
144
145
```
145
146
146
147
This time we also need the first derivative terms. Continuing,
147
148
148
-
```julia
149
+
```@example perturbation
149
150
eq = ∂∂x + 2 * ϵ * ∂x + x
150
151
eqs = collect_powers(eq, ϵ, 0:n)
151
152
vals = solve_coef(eqs, ∂∂y)
152
153
```
153
154
154
155
Next, we need to replace `∂`s and `∂∂`s with their **Symbolics.jl** counterparts:
155
156
156
-
```julia
157
+
```@example perturbation
157
158
subs1 = Dict(∂y[i] => D(y[i]) for i in eachindex(y))
158
159
subs2 = Dict(∂∂y[i] => D(D(y[i])) for i in eachindex(y))
159
160
subs = subs1 ∪ subs2
@@ -162,19 +163,19 @@ eqs = [substitute(first(v), subs) ~ substitute(last(v), subs) for v in vals]
162
163
163
164
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)$,
164
165
165
-
```julia
166
+
```@example perturbation
166
167
@mtkbuild sys = ODESystem(eqs, t)
167
168
```
168
169
169
-
```julia
170
+
```@example perturbation
170
171
# 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₁
173
174
174
175
prob = ODEProblem(sys, u0, (0, 50.0))
175
176
sol = solve(prob; dtmax = 0.01)
176
177
177
-
X = 𝜀 ->sum([𝜀^(i -1) * sol[y[i]]fori ineachindex(y)])
178
+
X = 𝜀 -> sum([𝜀^(i - 1) * sol[yi] for (i, yi) in enumerate(y)])
0 commit comments