Adloun

Résolution d'équations différentielles

Cours · Python (école d'ingénieur), chapitre 13 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun

L'évolution temporelle de nombreux systèmes physiques, biologiques, ou chimiques est régie par des équations différentielles. S'il est parfois possible de trouver des solutions analytiques (comme la décharge simple d'un condensateur dans un circuit ), la grande majorité des modèles réels (tels que le mouvement gravitationnel à trois corps ou le modèle épidémiologique SIR) ne possède aucune solution exacte sous forme de fonctions usuelles. Nous devons alors recourir aux schémas de résolution numérique. Ce chapitre présente les méthodes d'intégration à un pas pour les problèmes de Cauchy (méthodes d'Euler explicite et implicite, méthodes de Runge-Kutta d'ordres 2 et 4) et montre comment modéliser et simuler des systèmes d'équations différentielles d'ordres supérieurs en Python.

13.1 Problème de Cauchy et Champ de Vecteurs

On s'intéresse à la résolution du problème de Cauchy suivant sur un intervalle :

où est une fonction continue de deux variables et est la condition initiale.

13.1.1 Visualisation : Champ de Vecteurs (Direction Field)

Graphiquement, l'équation attribue à chaque point un vecteur de pente . Une solution est une courbe tangente en tout point à ce champ de vecteurs.

Exemple 13.1Champ de vecteurs avec quiver

import numpy as np
import matplotlib.pyplot as plt

def champ_vecteurs(f, t_range, x_range, norm=True):
    """Affiche le champ de vecteurs de l'équation x' = f(x, t)."""
    t = np.linspace(t_range[0], t_range[1], 20)
    x = np.linspace(x_range[0], x_range[1], 20)
    T, X = np.meshgrid(t, x)

    # Composantes du champ (dt=1, dx=f(x,t))
    U = np.ones_like(T)
    V = f(X, T)

    if norm:
        N = np.sqrt(U**2 + V**2)
        U, V = U/N, V/N

    plt.quiver(T, X, U, V, color="gray", alpha=0.7)
    plt.xlabel("t")
    plt.ylabel("x")
    plt.title("Champ de vecteurs")
    plt.grid(True, alpha=0.3)

# Exemple : x' = -x/tau (décroissance exponentielle)
tau = 2.0
f = lambda x, t: -x / tau

champ_vecteurs(f, (0, 10), (-2, 2))
plt.show()

13.2 Méthodes d'Euler

Pour approcher les valeurs de sur une subdivision régulière de pas définie par , nous pouvons intégrer l'équation entre et :

13.2.1 Méthode d'Euler Explicite

En approchant l'intégrale par le rectangle à gauche (valeur en ), on obtient le schéma explicite :

◆Théorème 13.2Consistance d'Euler

Lorsque la fonction est de classe , le schéma d'Euler explicite est consistant d'ordre 1.


def euler_explicite(f, x0, t0, tf, n):
    """Méthode d'Euler explicite."""
    h = (tf - t0) / n
    t = np.linspace(t0, tf, n+1)
    x = np.zeros(n+1)
    x[0] = x0

    for k in range(n):
        x[k+1] = x[k] + h * f(x[k], t[k])

    return t, x

13.2.2 Méthode d'Euler Implicite

En utilisant le rectangle à droite (valeur en ), on obtient le schéma implicite :

Pour une équation non linéaire, on doit résoudre une équation à chaque pas. Pour l'équation linéaire , la solution implicite s'écrit :


def euler_implicite_linear(f, x0, t0, tf, n, lambd):
    """Euler implicite pour x' = -lambda x."""
    h = (tf - t0) / n
    t = np.linspace(t0, tf, n+1)
    x = np.zeros(n+1)
    x[0] = x0

    for k in range(n):
        x[k+1] = x[k] / (1 + lambd * h)

    return t, x

13.2.3 Stabilité des méthodes d'Euler

Exemple 13.3Comparaison de la stabilité des méthodes d'Euler

Pour l'équation test (), la solution exacte décroît exponentiellement vers 0.

  • Euler explicite : . Stabilité si , soit .
  • Euler implicite : . Toujours stable ( pour tout ).

def comparer_euler(lambd=5.0, t_max=2.0):
    """Compare Euler explicite et implicite pour x' = -lambda x."""
    x0 = 1.0
    t_exact = np.linspace(0, t_max, 200)
    x_exact = x0 * np.exp(-lambd * t_exact)

    plt.figure(figsize=(10, 6))
    plt.plot(t_exact, x_exact, 'k-', linewidth=2, label='Solution exacte')

    for h in [0.3, 0.5, 0.7]:
        n = int(t_max / h)

        # Euler explicite
        t_exp, x_exp = euler_explicite(lambda x, t: -lambd*x, x0, 0, t_max, n)
        plt.plot(t_exp, x_exp, '--', label=f'Euler expo (h={h})')

        # Euler implicite
        t_imp, x_imp = euler_implicite_linear(lambda x, t: -lambd*x, x0, 0, t_max, n, lambd)
        plt.plot(t_imp, x_imp, ':', label=f'Euler impli (h={h})')

    plt.xlabel('t')
    plt.ylabel('x(t)')
    plt.title('Comparaison Euler explicite vs implicite')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

13.3 Méthodes de Runge-Kutta

Les méthodes de Runge-Kutta (RK) évaluent la fonction en plusieurs points intermédiaires pour approcher la pente moyenne avec un ordre de précision supérieur.

13.3.1 Méthode du Point Milieu (RK2 - ordre 2)

13.3.2 Méthode de Heun (RK2 alternative)

13.3.3 Méthode RK4 classique (ordre 4)


def rk4(f, x0, t0, tf, n):
    """Méthode de Runge-Kutta d'ordre 4."""
    h = (tf - t0) / n
    t = np.linspace(t0, tf, n+1)
    x = np.zeros(n+1)
    x[0] = x0

    for k in range(n):
        k1 = f(x[k], t[k])
        k2 = f(x[k] + 0.5 * h * k1, t[k] + 0.5 * h)
        k3 = f(x[k] + 0.5 * h * k2, t[k] + 0.5 * h)
        k4 = f(x[k] + h * k3, t[k] + h)
        x[k+1] = x[k] + (h / 6.0) * (k1 + 2.0*k2 + 2.0*k3 + k4)

    return t, x

13.4 Systèmes Différentiels et Équations d'Ordre Supérieur

Pour simuler des systèmes dynamiques complexes ou des équations différentielles d'ordre , on les récrit sous la forme d'un système vectoriel de premier ordre.

13.4.1 Principe de Réduction

Soit l'équation du second ordre . En posant le vecteur d'état , sa dérivée s'écrit :

Exemple 13.4Oscillateur harmonique amorti

L'équation se récrit en système :


def oscillateur(X, t, gamma=0.5, omega0=2.0):
    """Système pour oscillateur harmonique amorti."""
    y, v = X
    return np.array([v, -gamma * v - omega0**2 * y])

def rk4_systeme(F, X0, t0, tf, n, args=()):
    """RK4 pour système vectoriel."""
    h = (tf - t0) / n
    t = np.linspace(t0, tf, n+1)
    X = np.zeros((n+1, len(X0)))
    X[0] = X0

    for k in range(n):
        k1 = F(X[k], t[k], *args)
        k2 = F(X[k] + 0.5 * h * k1, t[k] + 0.5 * h, *args)
        k3 = F(X[k] + 0.5 * h * k2, t[k] + 0.5 * h, *args)
        k4 = F(X[k] + h * k3, t[k] + h, *args)
        X[k+1] = X[k] + (h / 6.0) * (k1 + 2.0*k2 + 2.0*k3 + k4)

    return t, X

# Simulation
t, X = rk4_systeme(oscillateur, [1.0, 0.0], 0, 20, 1000, args=(0.5, 2.0))

plt.figure(figsize=(10, 6))
plt.plot(t, X[:, 0], 'b-', label='Position y(t)')
plt.plot(t, X[:, 1], 'r--', label='Vitesse v(t)')
plt.xlabel('t')
plt.ylabel('y, v')
plt.title('Oscillateur harmonique amorti (RK4)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

13.5 Modèle SIR (Épidémiologie)

Le modèle SIR (Susceptible-Infectious-Removed) est un classique de l'épidémiologie :

où est la proportion de susceptibles, d'infectieux, de guéris.


def sir_model(X, t, beta, gamma):
    """Modèle SIR."""
    S, I, R = X
    dS = -beta * S * I
    dI = beta * S * I - gamma * I
    dR = gamma * I
    return np.array([dS, dI, dR])

# Paramètres
beta, gamma = 0.3, 0.1
S0, I0, R0 = 0.99, 0.01, 0.0
t, X = rk4_systeme(sir_model, [S0, I0, R0], 0, 100, 1000, args=(beta, gamma))

plt.figure(figsize=(10, 6))
plt.plot(t, X[:, 0], 'b-', label='Susceptibles S(t)')
plt.plot(t, X[:, 1], 'r-', label='Infectieux I(t)')
plt.plot(t, X[:, 2], 'g-', label='Guéris R(t)')
plt.xlabel('t')
plt.ylabel('Population')
plt.title('Modèle SIR')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

13.6 Utilisation de Solveurs Professionnels

La bibliothèque scipy.integrate fournit la fonction solve_ivp (Initial Value Problem) avec plusieurs méthodes intégrées.


from scipy.integrate import solve_ivp

def oscillateur_scipy(t, X, gamma, omega0):
    y, v = X
    return [v, -gamma * v - omega0**2 * y]

# Résolution avec solve_ivp
solution = solve_ivp(oscillateur_scipy, [0, 20], [1.0, 0.0],
                     args=(0.5, 2.0), method='RK45', dense_output=True)

t_scipy = np.linspace(0, 20, 500)
X_scipy = solution.sol(t_scipy)

plt.figure(figsize=(10, 6))
plt.plot(t_scipy, X_scipy[0], 'b-', label='solve_ivp (RK45)')
plt.xlabel('t')
plt.ylabel('y(t)')
plt.title('Oscillateur avec solve_ivp')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

13.7 Exercices résolus

Niveau 1 : Méthode d'Euler explicite

Exercice 1 - Décroissance exponentielle

Soit l'équation différentielle avec .

  • Donner la solution analytique.
  • Implémenter la méthode d'Euler explicite avec sur .
  • Comparer avec la solution exacte.
Démonstration (Solution)
  • La solution analytique est .
  • Implémentation et comparaison en Python :

import numpy as np
import matplotlib.pyplot as plt

# Solution analytique
def x_exact(t):
    return np.exp(-2 * t)

# Euler explicite
def euler_explicite(f, x0, t0, tf, n):
    h = (tf - t0) / n
    t = np.linspace(t0, tf, n+1)
    x = np.zeros(n+1)
    x[0] = x0

    for k in range(n):
        x[k+1] = x[k] + h * f(x[k], t[k])

    return t, x

# Paramètres
f = lambda x, t: -2 * x
x0 = 1.0
t0, tf = 0.0, 3.0
n = 30 # h = 0.1

t_euler, x_euler = euler_explicite(f, x0, t0, tf, n)
t_exact = np.linspace(t0, tf, 300)
x_exact_vals = x_exact(t_exact)

# Erreur
erreur = np.abs(x_euler[-1] - x_exact(t_euler[-1]))
print(f"Valeur exacte à t=3 : {x_exact(3):.6f}")
print(f"Euler explicite à t=3 : {x_euler[-1]:.6f}")
print(f"Erreur absolue : {erreur:.6f}")

# Visualisation
plt.figure(figsize=(10, 6))
plt.plot(t_exact, x_exact_vals, 'k-', linewidth=2, label='Solution exacte')
plt.plot(t_euler, x_euler, 'ro-', markersize=4, label='Euler explicite (h=0.1)')
plt.xlabel('t')
plt.ylabel('x(t)')
plt.title('Décroissance exponentielle')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

Illustration

Figure : Décroissance exponentielle résolue par la méthode d'Euler explicite ().

Résultat dans la console :


Valeur exacte à t=3 : 0.002479
Euler explicite à t=3 : 0.002478
Erreur absolue : 0.000001

L'erreur diminue avec le pas (théoriquement en ).

Exercice 2 - Étude de l'erreur d'Euler

Pour l'équation , étudier l'évolution de l'erreur en fonction du pas .

La suite du cours est sur Adloun

Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.

Lire la suite du cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →