Série 9: Équations différentielles ordinaires
1. Exercices
Solution
- 1
-
Le schéma d’Euler progressif s’écrit
Euler rétrograde:
Pour les deux schémas, on pose \(u_0=v_0=y(0)=1\).
- 2
-
On a:
Soit \(y(t)\) la solution du problème de Cauchy. On nous dit que \(y(t)\) est positive. Puisque \(y^{\prime}=-\arctan (y)<0\) pour \(y>0, y(t)\) est décroissante. Donc \(y(t) \in[0,1\)(\operatorname{car} y(0)=1)], et si l’on pose \(\lambda(t)=-\frac{1}{1+y(t)^2}\), on a
où \(\lambda_{\min }=\frac{1}{1+1}=\frac{1}{2}>0, \lambda_{\max }=1<\infty\); alors, d’après la théorie, la condition de stabilité est la suivante:
- 3
-
On pose \(F(x)=x-u_n+h \arctan (x)\), et on a alors \(F^{\prime}(x)=1+h /\left(1+x^2\right)\); les itérations de Newton s’écrivent
Solution
- 1
-
En utilisant la transformation proposée, le problème peut s’écrire sous la forme équivalente: On aboutit donc au système d’équations différentielles du premier ordre qui suit
\(A\) étant la matrice \(2 \times 2\) à coefficients constants
- 2
-
Les schémas d’Euler progressif et rétrograde s’écrivent respectivement: Euler progressif \(\quad\left\{\begin{array}{l}\mathbf{w}^{n+1}=\mathbf{w}^n+h A \mathbf{w}^n=(I+h A) \mathbf{w}^n \\ \mathbf{w}^0=\mathbf{w}_0=\left(y_0, z_0\right)^T\end{array}\right.\) Euler rétrograde \(\left\{\begin{array}{l}\mathbf{w}^{n+1}=\mathbf{w}^n+h A \mathbf{w}^{n+1} \\ \mathbf{w}^0=\mathbf{w}_0=\left(y_0, z_0\right)^T\end{array} \Longrightarrow\left\{\begin{array}{l}(I-h A) \mathbf{w}^{n+1}=\mathbf{w}^n \\ \mathbf{w}^0=\mathbf{w}_0=\left(y_0, z_0\right)^T\end{array}\right.\right.\) \(h\) étant le pas d’intégration. On note que le deuxième schéma nécessite la résolution d’un système linéaire \(2 \times 2\) à chaque itération.
- 3
-
Schéma d’Euler progressif. Si l’on introduit la transformation \(\mathbf{w}^n=V \mathbf{x}^n\) dans [eq:3], on obtient
En multipliant à gauche tous le termes \(\operatorname{par} V^{-1}\), on obtient
avec la condition initiale \(\mathbf{x}^0=\mathbf{x}_0=V^{-1} \mathbf{w}_0\). Schéma d’Euler rétrograde. En suivant le même raisonnement que pour le schéma explicite, on obtient aisément le système
Notons que [eq:5] contient deux équations qui peuvent se résoudre de façon indépendante l’une de l’autre. Dans le cas où \(c=5\) et \(k=6\) on a
Chaque équation du système [eq:6] correspond à un schéma d’Euler progressif scalaire et donc la condition de stabilité est \(h<2 / 3\) pour la première équation et \(h<1\) pour la deuxième. La condition à retenir c’est la plus restrictive, donc \(h<2 / 3\). Puisque le schéma [eq:6] est équivalent au schéma [eq:3], cette dernière condition est aussi la condition de stabilité pour [eq:3].
Solution
- 1
-
En soustrayant les équations indiquées on trouve
où on a utilisé le fait que \(y_n-u_n=e_n\). Donc l’équation de récurrence pour l’erreur est:
Cette formule découle de l’estimation de l’erreur pour la formule d’intégration des trapèzes |
- 2
-
A partir de (13), en sachant que la fonction \(f\) est lipschitzienne, on trouve
c’est-à-dire
Si \(h<1 / L\), on a \(1-h L / 2>0\), donc on peut diviser par \(1-h L / 2\) et écrire
où
Or, comme \(e_0=y_0-u_0=0\), en appliquant \(n\) fois la dernière inégalité on a:
On peut ensuite remarquer que, si \(h<1 / L\), on a
donc (par la même démarche que l’on a vue au cours pour la convérgence de la méthode d’Euler
et finalement on obtient
où
La méthode de Cranck-Nicholson est donc d’ordre 2 ; en effet, on a \(\tau(h)=\frac{h^2}{12} \max _{t \in[0, T\)}\left|y^{\prime \prime \prime}(t)\right|] et donc l’erreur est majorée par une quantité proportionelle au carré du pas de temps:
- 3
-
On a vu au point précédent que la méthode de Crank-Nicholson est d’ordre 2. Or, si l’on regarde le tableau, on remarque que, en divisant par deux le pas de temps, l’erreur dans la première colonne est (approximativement) divisée par 2, tandis que dans la seconde colonne elle est divisée par 4. On en déduit que la première colonne correspond à une méthode d’ordre 1 (par exemple Euler progressive ou Euler rétrograde) et la seconde à une méthode d’ordre 2 (donc celle de Crank-Nicholson).
- 4
-
On a:
On remarque que \(\lambda^{\prime}(t)=4 \frac{1-t^2}{\left(1+t^2\right)^2}\) : les zéros de cette dérivé sont en \(t= \pm 1\), donc
On peut donc utiliser la condition de stabilité pour la méthode d’Euler progressive que l’on a vue au cours: on a
Solution
- 1
-
Nous avons:
Les valeurs de \(N_h\) requises sont \(N_{h_1}=20 / 0.4=50\) et \(N_{h_2}=20 / 0.1=200\):
Pour la méthode de Heun, utilisons la méthode de Heun pour calculer la solution numérique.
import numpy as np
import plotly.graph_objects as go
from scipy.integrate import solve_ivp
from tan.ode.heun import heun
# Définition de la fonction de l'EDO
def odefun(t, y):
return -5 * y
# Paramètres
tspan = np.array([0, 20])
y0 = [1]
h1 = 0.4 # h_bar
Nh1 = int(20 / h1)
h2 = h1 / 4
Nh2 = int(20 / h2)
# Résolution par la méthode de Heun (à implémenter ou utiliser solve_ivp avec méthode 'Heun')
# Exemple d'utilisation de solve_ivp pour le contexte
t1,sol1 = heun(odefun, tspan, y0, Nh1)
t2,sol2 = heun(odefun, tspan, y0, Nh2)
# Plotting
fig = go.Figure()
fig.add_trace(go.Scatter(x=t1, y=sol1[:,0], mode='lines', name='Heun Nh=50'))
fig.add_trace(go.Scatter(x=t1, y=sol2[:,0], mode='lines', name='Heun Nh=200'))
fig.update_layout(title="Solution Numérique par la Méthode de Heun",
xaxis_title="Temps (t)",
yaxis_title="Solution (y)",
legend_title="Méthode")
fig.show()
Results
La Figure 1 affiche les résultats. Pour \(h=\bar{h}\), la solution numérique est constante et ne tend pas vers zéro lorsque \(t \rightarrow \infty\) : en effet, la condition de stabilité sur le pas de discrétisation \(h\) n’est pas satisfaite. Pour \(h=\bar{h} / 4\), c’est-à-dire \(N_h=200\), la condition \(h<\bar{h}\) est satisfaite et la solution numérique tend vers zéro.
- 2
-
Méthodes de Euler Progressive et Rétrograde. Appliquons la même démarche avec les méthodes Euler progressive et rétrograde. (Le code Python pour ces méthodes serait similaire, en ajustant la méthode de résolution dans
solve_ivp
ou en implémentant les méthodes spécifiquement.)
# Définition de la fonction de l'EDO
def odefun(t, y):
return -5 *y
from tan.ode.feuler import feuler
from tan.ode.beuler import beuler
# Résolution par la méthode d'Euler progressive
t1,sol1 = feuler(odefun, tspan, y0, Nh1)
t2,sol2 = feuler(odefun, tspan, y0, Nh2)
# Résolution par la méthode d'Euler rétrograde
t3,sol3 = beuler(odefun, tspan, y0, Nh1)
t4,sol4 = beuler(odefun, tspan, y0, Nh2)
# Plotting
fig = go.Figure()
fig.add_trace(go.Scatter(x=t1, y=sol1[:,0], mode='lines', name='Euler Progressif Nh=50'))
fig.add_trace(go.Scatter(x=t2, y=sol2[:,0], mode='lines', name='Euler Progressif Nh=200'))
fig.add_trace(go.Scatter(x=t3, y=sol3[:,0], mode='lines', name='Euler Rétrograde Nh=50'))
fig.add_trace(go.Scatter(x=t4, y=sol4[:,0], mode='lines', name='Euler Rétrograde Nh=200'))
fig.update_layout(title="Solution Numérique par les Méthodes d'Euler",
xaxis_title="Temps (t)",
yaxis_title="Solution (y)",
legend_title="Méthode")
fig.show()
Results
- 3
-
Euler Modifié, voici le code
import numpy as np
import plotly.graph_objects as go
tspan = np.array([0, 20])
y0 = [1]
h1 = 0.4 # h_bar
Nh1 = int(20 / h1)
h2 = h1 / 4
Nh2 = int(20 / h2)
def meuler(f, tspan, y0, Nh):
h = (tspan[1] - tspan[0]) / Nh # le pas de temps
t = np.linspace(tspan[0], tspan[1], Nh + 1) # le vecteur des temps tn
y = np.zeros((Nh + 1, len(y0)))
y[0] = y0
for i in range(Nh):
y_mid = y[i] + h / 2 * f(t[i], y[i]) # Calcul du point milieu
y[i + 1] = y[i] + h * f(t[i] + 0.5 * h, y_mid) # Mise à jour avec la pente au point milieu
return t, y
# Exemple d'utilisation avec une fonction d'EDO simple
def odefun(t, y):
return -5*y
t1, y1 = meuler(odefun, tspan, y0, Nh1)
t2, y2 = meuler(odefun, tspan, y0, Nh2)
# Tracer avec Plotly
fig = go.Figure()
fig.add_trace(go.Scatter(x=t1, y=y1[:, 0], mode=f'lines+markers', name='Méthode Euler Modifiée h={h1}'))
fig.add_trace(go.Scatter(x=t2, y=y2[:, 0], mode=f'lines+markers', name='Méthode Euler Modifiée h={h2}'))
fig.update_layout(title='Solution Numérique par la Méthode d\'Euler Modifiée',
xaxis_title='Temps',
yaxis_title='Solution y(t)',
legend_title='Méthode')
fig.show()
Results
Soit $h_1=0.1, h_2=0.01 ;$ comme $h_1 / h_2=10$, on aura $p \simeq \log {10}\left(E{h_1} / E_{h_2}\right)$. Alors on calcule les solutions numériques et les erreurs dans les deux cas:
import numpy as np
yexact = lambda t: np.exp(-5 * t)
# Calcul des solutions numériques
t1, y1 = meuler(odefun, tspan, y0, Nh1)
t2, y2 = meuler(odefun, tspan, y0, Nh2)
# Calcul des erreurs
Eh1 = np.max(np.abs(y1[:,0] - yexact(t1)))
Eh2 = np.max(np.abs(y2[:,0] - yexact(t2)))
print(f"Erreur pour h1={h1} : {Eh1}")
print(f"Erreur pour h2={h2} : {Eh2}")
# Calcul de l'ordre de la méthode
p = np.log10(Eh1 / Eh2) / np.log10(h1 / h2)
print(f"ordre de Eurler Modifié: {p}")
Results
La méthode d’Euler modifiée est d’ordre 2.
Solution
== Réponse à l’Exercice sur les Équations Différentielles Ordinaires
- 1
-
On a :
donc
mais comme \(y(30) \simeq 0\), on déduit que \(\bar{h} \simeq 2\).
- 2
-
Soit \(h = 1.5 \bar{h} = 3\); avec les commandes qui suivent on calcule la solution numérique par la méthode d’Euler progressive.
import numpy as np
import plotly.graph_objects as go
# Définition de la fonction dérivée
def dydt(t, y):
return -1 / (1 + y**2)
# Paramètres
h_large = 3 # h = 1.5 bar{h}
h_petit = 0.5 # h = 0.25 bar{h}
tspan = np.array([0, 30])
y0 = [0] # Condition initiale
# Solution numérique pour h_large
t_large = np.arange(tspan[0], tspan[1] + h_large, h_large)
t_large, y_large = feuler(dydt, tspan, y0, int(30 / h_large))
# Solution numérique pour h_petit
t_petit = np.arange(tspan[0], tspan[1] + h_petit, h_petit)
t_petit, y_petit = feuler(dydt, tspan, y0, int(30 / h_petit))
# Tracer les solutions numériques
fig = go.Figure()
fig.add_trace(go.Scatter(x=t_large, y=np.squeeze(y_large), mode='lines', name='h=1.5bar{h}'))
fig.add_trace(go.Scatter(x=t_petit, y=np.squeeze(y_petit), mode='lines', name='h=0.25bar{h}'))
fig.update_layout(title="Méthode d'Euler Progressive pour h=1.5bar{h} et h=0.25bar{h}",
xaxis_title="Temps",
yaxis_title="y(t)",
legend_title="Valeur de h")
fig.show()
Results
Les graphes que l’on obtient sont montrés. D’après le graphique, on peut vérifier que la méthode d’Euler progressive n’est pas stable pour tout choix de \(h\): si on utilise la méthode avec \(h=1.5\bar{h}\), on obtient des solutions avec des oscillations non-amorties. Par contre, avec \(h = 0.25 \bar{h}\) on est dans la région de stabilité absolue de la méthode : les perturbations sont contrôlées, en particulier on n’a pas d’oscillations.
- 3
-
On veut maintenant résoudre l’équation différentielle avec la méthode de Heun (commande heun) pour \(h=1.5 \cdot \bar{h}\) et \(h=0.25 \cdot \bar{h}\).
from tan.ode.heun import heun
# Résolution par la méthode de Heun
t_large, y_large = heun(dydt, tspan, y0, int(30 / h_large))
t_petit, y_petit = heun(dydt, tspan, y0, int(30 / h_petit))
# Tracer les solutions numériques
fig = go.Figure()
fig.add_trace(go.Scatter(x=t_large, y=np.squeeze(y_large), mode='lines', name='h=1.5bar{h}'))
fig.add_trace(go.Scatter(x=t_petit, y=np.squeeze(y_petit), mode='lines', name='h=0.25bar{h}'))
fig.update_layout(title="Méthode de Heun pour h=1.5bar{h} et h=0.25bar{h}",
xaxis_title="Temps",
yaxis_title="y(t)",
legend_title="Valeur de h")
fig.show()
Results
- 4
-
On veut maintenant résoudre l’équation différentielle avec la méthode de Runge-Kutta d’ordre 4 (commande rk4) pour \(h=1.5 \cdot \bar{h}\) et \(h=0.25 \cdot \bar{h}\).
from tan.ode.rk4 import rk4
# Résolution par la méthode de Runge-Kutta d'ordre 4
t_large, y_large = rk4(dydt, tspan, y0, int(30 / h_large))
t_petit, y_petit = rk4(dydt, tspan, y0, int(30 / h_petit))
# Tracer les solutions numériques
fig = go.Figure()
fig.add_trace(go.Scatter(x=t_large, y=np.squeeze(y_large), mode='lines', name='h=1.5bar{h}'))
fig.add_trace(go.Scatter(x=t_petit, y=np.squeeze(y_petit), mode='lines', name='h=0.25bar{h}'))
fig.update_layout(title="Méthode de Runge-Kutta d'ordre 4 pour h=1.5bar{h} et h=0.25bar{h}",
xaxis_title="Temps",
yaxis_title="y(t)",
legend_title="Valeur de h")
fig.show()
Results
Solution
- 1
-
On a une équation différentielle décrivant la dynamique de la population :
import numpy as np
import plotly.graph_objects as go
from tan.ode.feuler import feuler
# Définition de la fonction dérivée
def odefun(t, y):
return y * (y * (1 - y / 5) - 0.1)
y0 = np.array([1])
T = 10.
# Premier cas avec h = 0.5
h = 0.5
Nh = int(T / h)
t = np.linspace(0, T, Nh+1)
t, y = feuler(odefun, [0, T], y0, Nh)
print(y)
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=y[:,0], mode='lines', name='h=0.5', line=dict(dash='dot')))
# Deuxième cas avec h = 0.1
h = 0.1
Nh = int(T / h)
t1 = np.linspace(0, T, Nh+1)
t1, y1 = feuler(odefun, [0, T], y0, Nh)
fig.add_trace(go.Scatter(x=t1, y=y1[:,0], mode='lines', name='h=0.1'))
fig.update_layout(title="Dynamique de la Population pour Différents Pas de Temps",
xaxis_title="Temps",
yaxis_title="Population",
legend_title="Valeur de h")
fig.show()
Results
La première remarque est qu’il existe une condition de stabilité à respecter : pour \(h=0.5\), la méthode n’est pas stable tandis que pour \(h=0.1\) elle l’est. Ensuite, la population atteint un équilibre à \(y_\infty = 5\).
- 2
-
On considère maintenant un système de deux équations différentielles représentant les interactions entre les bactéries et les phagocytes.
def odefun_system(t, y):
return np.array([y[0] * (y[0] * (1 - y[0] / 5) - y[1]), y[1] * (y[0] - 1)])
y0 = np.array([0.5, 0.1])
h = 0.1
Nh = int(T / h)
t = np.linspace(0, T, Nh+1)
t,y=feuler(odefun_system, [0, T], y0, Nh)
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=y[:, 0], mode='lines', name='Bactéries'))
fig.add_trace(go.Scatter(x=t, y=y[:, 1], mode='lines', name='Phagocytes'))
fig.update_layout(title="Dynamique des Populations de Bactéries et Phagocytes",
xaxis_title="Temps",
yaxis_title="Population",
legend_title="Espèce")
fig.show()
Results
Selon le nouveau modèle, les phagocytes réussissent à éliminer les bactéries.
- 3
-
voici le code pour le modèle de bactéries et phagocytes, pour étudier les différentes valeurs des paramètres. Nous implémentons le modèle dans une fonction
modele
qui prend en entrée les paramètres et retourne les solutions numériques afin de pouvoir explorer les différentes valeurs des paramètres.
import numpy as np
import plotly.graph_objects as go
from tan.ode.feuler import feuler
def modele(h,T,r1, r2, K, alpha):
def odefun_system_logistic(t, y, r1, r2, K, alpha):
return np.array([y[0] * (r1*((1 - y[0] / K) - alpha*y[1])), r2*y[1] * ((1 - y[1] / y[0]))])
T=100
y0 = np.array([1e5, 1e2])
h = 0.1
Nh = int(T / h)
t = np.linspace(0, T, Nh+1)
t,y=feuler(odefun_system_logistic, [0, T], y0, Nh, r1, r2, K, alpha)
return t,y
r1=1 # taux de croissance des bactéries en l'absence de phagocytes (1/jour) : entre 0.1 et 2
r2=0.1 # taux de croissance des phagocytes en l'absence de bactéries (1/jour) : entre 1e-3 et 1
K=1e6 # capacité de charge de l'environnement (nombre de bactéries) : entre 1e3 et 1e9
alpha=1e-2 # taux de prédation des phagocytes sur les bactéries (1/jour) : entre 1e-3 et 0.1
t,y=modele(0.1,100,r1, r2, K, alpha)
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=y[:, 0], mode='lines', name='Bactéries'))
fig.add_trace(go.Scatter(x=t, y=y[:, 1], mode='lines', name='Phagocytes'))
fig.update_layout(title="Dynamique des Populations de Bactéries et Phagocytes avec Régulation Logistique",
xaxis_title="Temps",
yaxis_title="Population",
legend_title="Espèce")
# semi-logarithmic plot
fig.update_layout(yaxis_type="log")
fig.show()
Results
Nous pouvons maintenant explorer les différentes valeurs des paramètres pour le modèle de bactéries et phagocytes. Par exemple, pour \(r_1=1\), \(r_2=0.1\), \(K=10^6\) et \(\alpha=10^{-2}\), nous obtenons une dynamique des populations de bactéries et phagocytes comme montré ci-dessous
fig = go.Figure()
for alpha in [1e-3, 1e-2, 1e-1]:
t,y=modele(0.05,100,np.array([1e5, 1e2]),r1, r2, K, alpha)
fig.add_trace(go.Scatter(x=t, y=y[:, 0], mode='lines', name=f'Bactéries alpha={alpha}'))
fig.add_trace(go.Scatter(x=t, y=y[:, 1], mode='lines', name=f'Phagocytes alpha={alpha}'))
fig.update_layout(title=f"Dynamique des Populations de Bactéries et Phagocytes avec alpha={alpha}",
xaxis_title="Temps",
yaxis_title="Population",
legend_title="Espèce")
# semi-logarithmic plot
fig.update_layout(yaxis_type="log")
fig.show()
Results
Nous pouvons voir que pour des valeurs plus élevées de \(\alpha\), la population de bactéries diminue plus rapidement, tandis que la population de phagocytes augmente plus rapidement. Cela suggère que les phagocytes sont plus efficaces pour éliminer les bactéries lorsque \(\alpha\) est plus élevé.
Explorons maintenant les effets de la capacité de charge \(K\) sur la dynamique des populations de bactéries et phagocytes. Nous démarrons avec des populations initiales de bactéries et de phagocytes de \(10^3\) et \(10^1\) respectivement, et nous explorons différentes valeurs de \(K\) dans une échelle semi-logarithmique.
fig = go.Figure()
for K in [1e3, 1e6, 1e9]:
t,y=modele(0.05,100,np.array([1e3, 1e1]),r1, r2, K, alpha)
fig.add_trace(go.Scatter
(x=t, y=y[:, 0], mode='lines', name=f'Bactéries K={K}'))
fig.add_trace(go.Scatter(x=t, y=y[:, 1], mode='lines', name=f'Phagocytes K={K}'))
fig.update_layout(title=f"Dynamique des Populations de Bactéries et Phagocytes avec K={K}",
xaxis_title="Temps",
yaxis_title="Population",
legend_title="Espèce")
# semi-logarithmic plot
fig.update_layout(yaxis_type="log")
fig.show()
Results
Nous pouvons voir que pour des valeurs plus élevées de \(K\), la population de bactéries atteint des niveaux plus élevés et diminue plus lentement, tandis que la population de phagocytes atteint des niveaux légèrement plus élevés et augmente plus rapidement. Cela suggère que les phagocytes sont plus efficaces pour éliminer les bactéries lorsque \(K\) est plus élevé.
Dans les graphes ci-dessus, nous avons utilisé une échelle semi-logarithmique pour visualiser les populations de bactéries et de phagocytes, car les populations peuvent varier sur plusieurs ordres de grandeur. Cela nous permet de mieux visualiser les dynamiques des populations sur une échelle plus large. |
en Master CSMI nous avons un cours de traitement des incertitudes et de l’optimisation, qui permet de faire des analyses de sensibilité et d’optimisation des paramètres. Ces analyses peuvent être utilisées pour explorer les effets des paramètres sur la dynamique du système et pour identifier les paramètres les plus influents sur les résultats du modèle et ceci de manière systématique. |