Skip to content
Snippets Groups Projects
Commit 93c047e8 authored by Olivier Cots's avatar Olivier Cots
Browse files

foo

parent 85f9f756
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags:
<img src="https://gitlab.irit.fr/toc/etu-n7/julia/-/raw/main/assets/TSE_Logo_2019.png" alt="TSE" height="200" style="margin-left:50px"/>
# Dynamical system and numerical integration
- Date : 2024-2025
- Approximative duration : 1h15
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Introduction
</div>
To numerically compute solutions of ordinary differential equations, we will use the package `OrdinaryDiffEq.jl`. This package is part of a larger package, `DifferentialEquations.jl`.
For the documentation of these Julia packages on numerical integration, see
[the DifferentialEquations.jl documentation](https://diffeq.sciml.ai/stable/).
%% Cell type:code id: tags:
``` julia
# load packages
using OrdinaryDiffEq
using ForwardDiff
using LinearAlgebra
using Plots
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Example 1
</div>
We will solve the initial value problem
$$(IVP)\left\{\begin{array}{l}
\dot{x}_1(t)=x_1(t)+x_2(t)+\sin t\\
\dot{x}_2(t)=-x_1(t)+3x_2(t)\\
x_1(0)=-9/25\\
x_2(0)=-4/25,
\end{array}\right.
$$
- Write the function $f$ that expresses the differential equation in the form $\dot{x}(t) = f(t, x(t))$.
- Verify that the solution to $(IVP)$ is
$$\begin{align*}
x_1(t)&= (-1/25)(13\sin t+9\cos t)\\
x_2(t)&= (-1/25)(3\sin t+4\cos t).
\end{align*}$$
- Implement the function $f$ and execute the code below. Any comments?
%% Cell type:code id: tags:
``` julia
# Note:
# An IVP (Initial Value Problem) is an ordinary differential equation
# (ODE in English, EDO in French) with an added initial condition.
"""
Right-hand side of the IVP
x : state vector
λ : parameter vector
t : time variable. Here, time appears explicitly, so the system is non-autonomous.
"""
function example1(x, λ, t)
xdot = similar(x)
# TO COMPLETE/MODIFY
xdot[:] = zeros(size(x))
return xdot
end
λ = Float64[] # no parameters
t0 = 0
tf = 10
tspan = (t0, tf) # integration interval
x0 = [-9/25, -4/25] # initial condition
prob = ODEProblem(example1, x0, tspan, λ) # definition of the IVP
sol = solve(prob) # solve the IVP
p1 = plot(sol, label = ["x₁(t)" "x₂(t)"], title = "default options") # plot the solution
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Step Size Control
</div>
Good numerical integrators include automatic step size adjustment. For
[Runge-Kutta methods](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods),
for example, on a step $[t_0, t_{1}]$, two approximate solutions at time $t_{1}$ are computed using two different schemes: $x_{1}$ and $\hat{x}_{1}$, with local errors $O(h^p)$ and $O(h^{p+1})$, respectively (the global errors are $O(h^{p-1})$ and $O(h^p)$). This allows the local error of the less accurate scheme to be estimated using the difference $x_{1}-\hat{x}_{1}$. The step size can then be adjusted so that the norm of this difference is below a given tolerance `Tol`, or such that
$$\frac{\|x_{1}-\hat{x}_{1}\|}{\mathrm{Tol}}<1.$$
In practice, absolute and relative tolerances are considered component-wise, and we define: $\mathrm{sc}_i = \mathrm{abstol}_i + \mathrm{reltol}_i \times \max(|x_{0i}|,|x_{1i}|)$. The step size is then calculated to satisfy
$$\mathrm{err} = \sqrt{\frac{1}{n}\sum_{i=1}^n\left(\frac{x_{1i}-\hat{x}_{1i}}{\mathrm{sc}_i}\right)^2}<1.$$
Reference: [Hairer, Nørsett, Wanner (2008) Solving Ordinary Differential Equations I Nonstiff Problems](https://link.springer.com/book/10.1007/978-3-540-78862-1).
In `Julia`, default values can be modified: `reltol = 1e-3` and `abstol = 1e-6`
(when these values are scalars, the same quantities are used for all components).
Perform numerical integration for:
- `reltol = 1e-3` and `abstol = 1e-6`
- `reltol = 1e-6` and `abstol = 1e-9`
- `reltol = 1e-10` and `abstol = 1e-15`
and display the results including the solution.
%% Cell type:code id: tags:
``` julia
# computation with default options
sol = solve(prob, reltol = 1e-3, abstol = 1e-6)
p1 = plot(sol, label = ["x₁(t)" "x₂(t)"], title = "reltol=1e-3, abstol=1e-6")
# TO COMPLETE/MODIFY
#
p2 = plot()
#
p3 = plot()
# exact solution
T = t0:(tf-t0)/100:tf
sinT = sin.(T) # vectorized operation
cosT = cos.(T)
p4 = plot(T, [(-1/25)*(13*sinT + 9*cosT) (-1/25)*(3*sinT + 4*cosT)], label = ["x₁(t)" "x₂(t)"], title = "Exact Solution")
# display
plot(p1, p2, p3, p4, size=(650, 350))
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
On the Finite-Time Blow-Up of Solutions
</div>
We will solve the initial value problem $\dot{x}(t) = 1 + x^p(t)$ with $p \ge 1$ and $x(0)=0$.
- Implement the function $f$ that reformulates the differential equation as $\dot{x}(t)=f(t,x(t))$.
- Determine the smallest value of $p \ge 1$ for which the solution blows up in finite time (with a precision of $0.1$) less than the value of 3.
%% Cell type:code id: tags:
``` julia
"""
Right-hand side of the IVP
x : state vector
p : scalar parameter
t : time variable. Here, time does not appear explicitly, so the system is autonomous.
"""
function f(x, p, t)
return 0.0
end
#
x0 = 0.0
t0 = 0.0
tf = 3
tspan = (t0, tf) # initial and terminal times
#
xspan = 0:0.01:2
#
pf = plot(title = "xᵖ")
px = plot(title = "x(t, p)")
#
p = 1
prob = ODEProblem(f, x0, tspan, p, reltol = 1.e-8, abstol = 1.e-8) # problem definition in Julia
sol = solve(prob) # numerical integration
pf = plot!(pf, xspan, x -> (f(x, p, t0)-1), label = "p=$p") # plot the right-hand side
px = plot!(px, sol, label = "p=$p") # plot the solution
#
p = 1 # TO MODIFY
prob = ODEProblem(f, x0, tspan, p, reltol = 1.e-8, abstol = 1.e-8) # problem definition in Julia
sol = solve(prob) # numerical integration
pf = plot!(pf, xspan, x -> (f(x, p, t0)-1), label = "p=$p") # plot the right-hand side
px = plot!(px, sol, label = "p=$p") # plot the solution
#
p = 2
prob = ODEProblem(f, x0, tspan, p, reltol = 1.e-8, abstol = 1.e-8) # problem definition in Julia
sol = solve(prob) # numerical integration
pf = plot!(pf, xspan, x -> (f(x, p, t0)-1), label = "p=$p") # plotting the second member
px = plot!(px, sol, label = "p=$p") # plotting the solution
#
plot!(pf, xlims=(0, 2), ylims=(0, 4))
plot!(px, xlims=(t0, tf), ylims=(0, 100))
plot(pf, px, size=(650, 350))
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Simple Pendulum
</div>
We focus here on the [simple pendulum](https://en.wikipedia.org/wiki/Pendulum_(mathematics)). The physical principles of classical mechanics give the governing equation for the motion:
$$ ml^2\ddot{\alpha}(t)+mlg\sin(\alpha(t)) +k\dot{\alpha}(t)=0, $$
where $\ddot{\alpha}(t)$ denotes the second derivative of the angle $\alpha$ with respect to time $t$.
- Using the state variable $ x = (x_1, x_2) = (\alpha, \dot{\alpha}) $, write the function $ f $ to represent the differential equation in the form $ \dot{x}(t) = f(t, x(t)) $.
- Implement this function below and execute the code. Based on your observations, do you see an oscillation or a rotation of the pendulum?
- Replace the initial angular velocity $ \dot{\alpha}(0) $ with 2. Comments?
%% Cell type:code id: tags:
``` julia
"""
RHS of the IVP
x : state vector
λ : parameter vector
t : time variable. Here, time does not explicitly appear; the system is autonomous.
"""
function pendulum(x, λ, t)
xdot = similar(x)
g, l, k, m = λ
# TO COMPLETE/MODIFY
xdot[:] = zeros(size(x))
return xdot
end
#
g = 9.81
l = 10
k = 0
m = 1
λ = [g, l, k, m] # constant parameters
theta0 = pi/3
x0 = [theta0, 0] # initial state
t0 = 0
tf = 3*pi*sqrt(l/g)*(1 + theta0^2/16 + theta0^4/3072) # 2*approximation of the period
tspan = (t0, tf) # initial and final instants
prob = ODEProblem(pendulum, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8) # defining the problem in Julia
sol = solve(prob) # numerical integration
plot(sol, label = ["x₁(t)" "x₂(t)"]) # plotting the solution
```
%% Cell type:markdown id: tags:
**Phase Diagram.**
- Run the code below and interpret the graph: where are the oscillations, rotations, and stable and unstable equilibrium points?
- Consider the case where $k=0.15$ (introducing friction). What happens?
%% Cell type:code id: tags:
``` julia
#
g = 9.81
l = 1.5
k = 0
m = 1
λ = [g, l, k, m] # constant parameters
plt = plot(xlabel = "x₁", ylabel = "x₂", legend = false) # initialize the plot
for theta0 in 0:(2*pi)/10:2*pi
theta0_princ = theta0
tf = 3*pi*sqrt(l/g)*(1 + theta0_princ^2/16 + theta0_princ^4/3072) # 2*approximation of the period
tspan = (0.0,tf)
x0 = [theta0 0]
prob = ODEProblem(pendulum, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(plt, sol, idxs=(1,2), color="blue") # lw = linewidth
end
theta0 = pi-10*eps()
x0 = [theta0 0]
tf = 50 # problem for tf=50 1/4 of the period!
tspan = (0.0,tf)
prob = ODEProblem(pendulum, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(plt, sol, idxs=(1,2), xlims = (-2*pi,4*pi), color="green") # lw = linewidth
theta0 = pi+10*eps()
x0 = [theta0 0]
tf = 50
tspan = (0.0,tf)
prob = ODEProblem(pendulum, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(plt, sol, idxs=(1,2), xlims = (-2*pi,4*pi), color="green") # lw = linewidth
# circulation case
for thetapoint0 in 0:1.:4
tf = 10
tspan = (0.,tf)
x0 = [-pi thetapoint0] # thetapoint0 > 0 so theta increases from -pi to ...
prob = ODEProblem(pendulum, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(plt, sol, idxs=(1,2), color="red") # lw = linewidth
end
for thetapoint0 in -4:1.:0
tf = 10
tspan = (0.,tf)
x0 = [3*pi thetapoint0] # thetapoint0 < 0 so theta decreases from 3pi to ...
prob = ODEProblem(pendulum, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(plt, sol, idxs=(1,2), color="purple") # lw = linewidth
end
plot!(plt, [-pi 0 pi 2*pi 3*pi], [0 0 0 0 0], seriestype=:scatter)
plot!(plt, xlims = (-pi,3*pi), size=(650, 350))
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Van der Pol Model
</div>
The differential equation considered is the [Van der Pol equation](https://en.wikipedia.org/wiki/Van_der_Pol_oscillator)
$$(IVP)\left\{\begin{array}{l}
\dot{x}_1(t)=x_2(t)\\
\dot{x}_2(t)=(1-x_1^2(t))x_2(t)-x_1(t)\\
x_1(0)=2.00861986087484313650940188\\
x_2(0)=0
\end{array}\right.
$$
up to the time $t_f=T=6.6632868593231301896996820305$, where $T$ is the period of the solution.
Code the right-hand side of the IVP below and execute the code. Comments.
%% Cell type:code id: tags:
``` julia
"""
Right-hand side of the IVP: Van der Pol model
x : state vector
λ : parameter vector
t : time variable. Here, time does not explicitly intervene, the system is autonomous.
"""
function vdp(x, λ, t)
xpoint = similar(x)
# TO BE COMPLETED/MODIFIED
xpoint[:] = zeros(size(x))
return xpoint
end
#
t0 = 0
tf = 6.6632868593231301896996820305
tspan = (t0, tf)
#
plot(xlabel = "x₁", ylabel = "x₂", legend = false)
# Inner trajectories
for x01 in -2:0.4:0
x0 = [x01,0]
prob = ODEProblem(vdp, x0, tspan, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(sol, idxs=(1,2), color = "blue")
end
# Outer trajectories
for x02 in 2.5:0.5:4
x0 = [0,x02]
prob = ODEProblem(vdp, x0, tspan, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(sol, idxs=(1,2), color = "green")
end
# Periodic limit cycle
x0 = [2.00861986087484313650940188,0]
prob = ODEProblem(vdp, x0, tspan, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
plot!(sol, idxs=(1,2), color = "red", lw = 2.)
#
plot!([0], [0], seriestype=:scatter) # equilibrium point
plot!(xlims = (-2.5, 2.5), ylims = (-3, 5), size=(650, 350))
```
%% Cell type:markdown id: tags:
**Stability.**
Below, we compute the eigenvalues of the Jacobian matrix of the right-hand side of the IVP at the equilibrium point $ x_e = (0, 0) $. Comments.
%% Cell type:code id: tags:
``` julia
# Jacobian of the vdp function
dvdp(x) = ForwardDiff.jacobian(x -> vdp(x, [], 0), x)
# Equilibrium point and the Jacobian matrix at this point
xe = [0, 0]
A = dvdp(xe)
# Eigenvalues of the Jacobian matrix
eigvals(A)
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Predator-Prey Model (Lotka-Volterra Model)
</div>
The differential equation considered is given by the [Lotka-Volterra predation equations](https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations)
$$\left\{\begin{array}{l}
\dot{x}_1(t)= \phantom{-}x_1(t) ( \alpha - \beta x_2(t)) \\[0.5em]
\dot{x}_2(t)= -x_2(t) ( \gamma - \delta x_1(t))
\end{array}\right.
$$
where
- $t$ is time;
- $x(t)$ is the population of prey as a function of time;
- $y(t)$ is the population of predators as a function of time;
The following parameters characterize the interactions between the two species:
- $\alpha$, intrinsic reproduction rate of prey (constant, independent of predator population);
- $\beta$, mortality rate of prey due to encounters with predators;
- $\delta$, reproduction rate of predators based on prey encountered and consumed;
- $\gamma$, intrinsic mortality rate of predators (constant, independent of prey population);
**Questions.**
- The point $(0, 0)$ is clearly a stable equilibrium point. There is another equilibrium point, which one?
- Display the other equilibrium point along with some trajectories of the system in the phase plane, within the limits of the plot below. What do you observe about the trajectories?
%% Cell type:code id: tags:
``` julia
# ------------------------------
# BEGIN TO COMPLETE/MODIFY THE RIGHT-HAND SIDE OF THE IVP
# ...
# END TO COMPLETE/MODIFY THE RIGHT-HAND SIDE OF THE IVP
# ------------------------------
# Parameters
α = 2/3
β = 4/3
γ = 1
δ = 1
λ = [α, β, γ, δ]
t0 = 0
tf = 20
tspan = (t0, tf)
plt = plot(xlabel = "x₁", ylabel = "x₂", legend = false)
# ------------------------------
# BEGIN TO COMPLETE/MODIFY THE DISPLAY
# ...
# END TO COMPLETE/MODIFY THE DISPLAY
# ------------------------------
plot!(xlims = (0, 4), ylims = (0, 2.5), size=(650, 350))
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Resonance Phenomenon
</div>
Let $\omega$ and $\omega_0$ be two strictly positive real numbers. Consider the differential equation
$$\ddot{y}(t) + \omega_0^2 y(t) = \cos(\omega t).$$
- By taking the state variable $x=(x_1,x_2)=(y, \dot{y})$, write the function $f$ that allows you to express the differential equation in the form $\dot{x}(t) = f(t,x(t))$.
- Code this function below and run the code with $\omega_0 = \omega/2$: are the solutions bounded?
- Replace the angular frequency to have $\omega_0 = \omega$. Comments.
Note: See the Wikipedia page on the [resonance phenomenon](https://en.wikipedia.org/wiki/Resonance) for more information.
%% Cell type:code id: tags:
``` julia
"""
Right-hand side of the IVP
x : state vector
λ : parameter vector
t : time variable. Here, time explicitly intervenes, the system is non-autonomous.
"""
function resonance(x, λ, t)
xdot = similar(x)
# TO BE COMPLETED/MODIFIED
xdot[:] = zeros(size(x))
return xdot
end
# parameters
ω = 1
ω₀ = ω/2
λ = [ω, ω₀]
# integration times
t0 = 0
tf = 100
tspan = (t0, tf)
# initial condition
x0 = [0, 0]
# define the problem and solve it
prob = ODEProblem(resonance, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
function makegif()
#
plt = plot(xlabel = "x₁", ylabel = "x₂", zlabel = "t", legend = false)
#
ω == ω₀ ? lim = 50 : lim = 5
plot!(xlims = (-lim, lim), ylims = (-lim, lim), zlims = (t0, tf), size=(850, 550))
# get x1, x2, t from sol
x1 = [sol.u[i][1] for i 1:length(sol.u)]
x2 = [sol.u[i][2] for i 1:length(sol.u)]
t = sol.t
# plot the trajectory step by step and make a gif
i = 1
@gif for j 2:2:length(sol.t)
# plot the trajectory part from i to j
plot!(plt, x1[i:j], x2[i:j], t[i:j], color=:blue)
# update i
i = j
end
end
makegif()
```
%% Cell type:markdown id: tags:
<div style="width:95%;
margin:10px;
padding:8px;
color:white;
background-color: rgb(46, 109, 4);
border-radius:10px;
font-weight:bold;
font-size:1.5em;
text-align:center;">
Lorenz Attractor
</div>
The differential equation considered is the [Lorenz equation](https://en.wikipedia.org/wiki/Lorenz_system), given by
$$\left\{\begin{array}{l}
\dot{x}_1(t)= \phantom{-}\sigma (x_2(t) - x_1(t)) \\[0.5em]
\dot{x}_2(t)= \rho\, x_1(t) - x_2(t) - x_1(t)\, x_3(t) \\[0.5em]
\dot{x}_3(t)= x_1(t)\, x_2(t) - \beta x_3(t)
\end{array}\right.
$$
where
- $t$ is time;
- $x(t)$ is proportional to the intensity of the convection motion;
- $y(t)$ is proportional to the temperature difference between the rising and descending currents;
- $z(t)$ is proportional to the deviation of the vertical temperature profile from the reference linear value;
- $\sigma$, $\beta$, and $\rho$ are positive real parameters;
- $\sigma$ is the Prandtl number;
- $\rho$ is the Rayleigh number.
It is common to set $\sigma = 10$, $\beta = 8/3$, and $\rho = 28$ to illustrate the phenomenon of chaos.
Complete the code below and execute it. Observe the phenomenon of chaos. You can modify the initial condition.
%% Cell type:code id: tags:
``` julia
"""
Second member of the IVP
x : state vector
λ : parameter vector
t : time variable. Here time does not explicitly intervene, the system is autonomous.
"""
function Lorentz(x, λ, t)
xdot = similar(x)
σ, ρ, β = λ
# TO COMPLETE/MODIFY
xdot[:] = zeros(size(x))
return xdot
end
# parameters
σ = 10
ρ = 28
β = 8/3
λ = [σ, ρ, β]
# integration times
t0 = 0
tf = 100
tspan = (t0, tf)
# initial condition
x0 = [0, 1, 0]
# define the problem and solve it
prob = ODEProblem(Lorentz, x0, tspan, λ, reltol = 1.e-8, abstol = 1.e-8)
sol = solve(prob)
function makegif()
#
plt = plot(xlabel = "x₁", ylabel = "x₂", zlabel = "x₃", legend = false)
#
plot!(plt, background_color = :royalblue4)
# update the color of axis of the figure to white
plot!(plt, axiscolor = :white, tickfontcolor = :white, bordercolor = :white, fg_color_guide = :white)
# plot the three equilibrium points
plot!(plt, [0], [0], [0], seriestype=:scatter, color=:red)
plot!(plt, [ sqrt(β*(ρ-1))], [ sqrt(β*(ρ-1))], [ρ-1], seriestype=:scatter, color=:red)
plot!(plt, [-sqrt(β*(ρ-1))], [-sqrt(β*(ρ-1))], [ρ-1], seriestype=:scatter, color=:red)
# get x1, x2, t from sol
x1 = [sol.u[i][1] for i 1:length(sol.u)]
x2 = [sol.u[i][2] for i 1:length(sol.u)]
x3 = [sol.u[i][3] for i 1:length(sol.u)]
#
plot!(xlims = (-20, 20), ylims = (-25, 30), zlims = (0, 50), size=(850, 550))
#
duration = 10
fps = 10
frames = fps*duration
step = length(sol.t) ÷ frames
# return if there is nothing to compute
if (step == 0)
return;
end
# plot the trajectory step by step and make a gif
i = 1
animation = @animate for j 2:step:length(sol.t)
# plot the trajectory part from i to j
plot!(plt, x1[i:j], x2[i:j], x3[i:j], color=:gold2)
# update i
i = j
end
gif(animation, "lorentz.gif", fps=fps)
end
makegif()
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment