Objectif de la fiche : utiliser un langage de programmation pour résoudre une équation différentielle grâce à la méthode d'Euler, à travers l'exemple de la charge d'un condensateur, puis comparer le résultat numérique à la solution analytique exacte pour juger de la qualité de la méthode.
Cette fiche est autonome : tu n'as besoin d'aucun autre document. Chaque étape contient un script incomplet à compléter, puis à copier-coller dans Thonny (ou dans la console Basthon tout en bas de la page si tu n'as pas Thonny sous la main).
Contexte : simuler une charge sans la résoudre à la main
- On charge un condensateur à travers une résistance, à l'aide d'un générateur idéal de tension
E. La tensionUaux bornes du condensateur vérifie l'équation différentielledU/dt = (E - U)/τ, oùτ = RCest la constante de temps du circuit. - Cette équation possède une solution mathématique exacte (une exponentielle qui tend vers
E), mais on va ici la retrouver numériquement, sans la résoudre à la main : c'est l'intérêt de la méthode d'Euler. On comparera ensuite les deux pour juger de la qualité de la méthode. - Le principe : on écrit l'équation différentielle sous la forme
dU/dt = f(U, t), puis on avance pas à pas depuis la condition initiale, en approchant la courbe solution par une suite de petits segments de droite. Plus le pas est petit, plus l'approximation est fine — mais plus le calcul est long.
La fonction f(U,t) et les paramètres
- On simule un condensateur initialement déchargé (
U(0) = 0 V), avec un générateur de force électromotriceE = 10 Vet une constante de tempsτ = 1 s(notéeTaudans le code, car Python n'accepte pas les lettres grecques comme nom de variable usuel). - La fonction
f(U, t)renvoie la valeur dedU/dtà un instant donné : c'est elle qui encode l'équation différentielle. - Le script ci-dessous est déjà complet : copie-le tel quel dans Thonny (ou dans Basthon) pour commencer.
import numpy as np
import matplotlib.pyplot as plt
# bornes de l'intervalle de temps et nombre de pas pour la résolution
t0, tf = 0., 10.
N = 1000
t = np.linspace(t0, tf, N+1) # liste des dates pour le calcul des solutions approchées
# condition initiale et constantes du circuit
x0 = 0 # tension initiale U(0), en V : le condensateur est déchargé
E = 10 # force électromotrice du générateur, en V
Tau = 1 # constante de temps du circuit RC, en s
def f(U, t):
return (E-U)/Tau
Indice
Le paramètre t de f(U, t) n'est pas utilisé dans le return ici (l'équation ne dépend pas explicitement du temps), mais il faut tout de même le prévoir dans la signature de la fonction : la méthode d'Euler, plus générale, doit pouvoir fonctionner aussi avec des équations où t intervient.
Écrire la fonction euler
- La méthode d'Euler calcule
U(i+1)à partir deU(i)grâce à la relationU(i+1) = U(i) + h·f(U(i), t(i)), oùhest le pas (la durée entre deux instants successifs). - Comme les instants de
tsont régulièrement espacés (grâce ànp.linspace),hest constant : il suffit de le calculer une seule fois, comme la différence entre les deux premières valeurs det. - La fonction doit fonctionner pour n'importe quelle équation différentielle : c'est pour cela qu'on lui passe la fonction
g(qui jouera le rôle def), la condition initialex0, et le tableau des instantst, sans jamais écriref,EouTauà l'intérieur.
- Complète la fonction
eulerci-dessous, qui renvoie un tableau numpyxcontenant la solution approchée à chaque instant det. - N'oublie pas d'initialiser
xavecnp.zeros(N)avant la boucle : un tableau numpy a une taille fixée dès sa création, contrairement à une liste, on ne peut pas lui ajouter des éléments avec.append(...)au fur et à mesure.
def euler(g, x0, t): # méthode d'Euler
h = t[_] - t[_] # TODO : pas constant, distance entre les deux premières valeurs de t
N = len(_) # on recalcule N en local pour le cas général
x = np.zeros(_) # TODO : initialisation obligatoire du tableau numpy
x[_] = __ # TODO : condition initiale
for i in range(N-_) :
x[i+_] = x[_] + h*g(x[_], t[_]) # TODO : définit x[i+1] par Euler
return x
Indice
range(N-1) permet à i de parcourir 0, 1, ..., N-2 : à chaque tour, on calcule x[i+1], ce qui remplit progressivement le tableau jusqu'au dernier indice N-1, sans jamais chercher à écrire au-delà.
Cette fonction ne produit aucun affichage à elle seule (elle se termine par return x) : le résultat se vérifie à l'étape suivante, en l'appliquant au problème de la charge du condensateur.
Résoudre avec N = 1000 pas et comparer à la théorie
- Cette équation différentielle a une solution théorique connue :
U(t) = E·(1 - e-t/τ). On peut donc la tracer sur le même graphe que la solution d'Euler, pour vérifier que la méthode numérique donne bien le bon résultat.
- Complète la fonction
Uthéo(t), qui renvoie la solution théoriqueE·(1 - e-t/τ)(utilisenp.exppour l'exponentielle). - Appelle ta fonction
euleravec la fonctionf, la condition initialex0et le tableautdéfinis à l'étape 0, et stocke le résultat danssolE. - Trace
solEen fonction det, avec des ronds bleus ('bo'), et superposeUthéo(t)en rouge trait plein ('r-'), chacune avec une légende adaptée.
# TODO : fonction qui renvoie la solution théorique de l'équation
def Uthéo(t):
return E*(_-np.exp(-t/___))
# TODO : résous l'équation avec ta fonction euler
solE = euler(_, __, _)
plt.figure()
# TODO : trace solE en fonction de t, en ronds bleus, avec une légende indiquant N
plt.plot(_, ____, '__', label='________________'+str(N))
# TODO : superpose Uthéo(t) en rouge trait plein
plt.plot(_, Uthéo(_), '__', label='__________________')
plt.legend(loc='best')
plt.xlabel('t') ; plt.ylabel('$tension$')
plt.grid()
plt.show()
Indice
Le nombre 1 tout seul, sans unité, joue le rôle du 1 dans (1 - e-t/τ) : ce n'est pas x0 qu'on réutilise ici, mais bien la constante mathématique de la formule.
Une courbe qui part de 0 V et remonte rapidement vers 10 V (la tension du générateur), en s'aplatissant progressivement — et les points bleus doivent quasiment se confondre avec la courbe rouge théorique. Pour vérifier ton calcul : solE[1] doit valoir 0,1 (le premier petit pas, de taille h = 0,01).
Comparer avec seulement 15 pas
- Ta fonction
eulerest générique : elle fonctionne avec n'importe quel tableau d'instantst, quelle que soit sa taille. Il suffit de lui fournir un nouveau tableau, construit avec un nombre de pas beaucoup plus petit.
- Crée un second tableau d'instants
t2, sur le même intervalle[0, 10], mais avec seulementM = 15pas. - Appelle
eulerune seconde fois avec ce nouveau tableau, et stocke le résultat danssolE2. - Trace
solE2en fonction det2avec des ronds bleus ('bo'), et superposeUthéo(t2)en rouge trait plein, chacune avec une légende indiquant son nombre de pas.
# TODO : construis un tableau de 15 pas sur le même intervalle de temps
M = __
t2 = np.linspace(__, __, ____)
# TODO : résous l'équation avec ce nouveau tableau d'instants
solE2 = euler(_, __, __)
plt.figure()
# TODO : trace solE2 en fonction de t2, en ronds bleus, avec une légende indiquant M
plt.plot(__, ______, '__', label='________________'+str(M))
# TODO : superpose Uthéo(t2) en rouge trait plein
plt.plot(__, Uthéo(__), '__', label='__________________')
plt.legend(loc='best')
plt.xlabel('t') ; plt.ylabel('$tension$')
plt.grid()
plt.show()
Indice
Il n'est pas nécessaire de réécrire toute la fonction euler : elle a été conçue pour accepter n'importe quel tableau t en argument, c'est tout l'intérêt de l'avoir rendue générale à l'étape 1.
Les points bleus (15 pas) suivent grossièrement la courbe rouge théorique mais s'en écartent nettement au début : avec un pas h beaucoup plus grand (h ≈ 0,67 s au lieu de 0,01 s), chaque segment de droite est une moins bonne approximation de la vraie courbe. Pour vérifier : solE2[1] doit valoir environ 6,7.
Conclusion
- La méthode d'Euler permet de résoudre numériquement une équation différentielle, sans avoir besoin d'en connaître la solution mathématique exacte, en avançant pas à pas depuis la condition initiale.
- Quand on connaît la solution théorique, comme ici, on peut la superposer à la solution numérique pour juger de la qualité de la méthode : avec suffisamment de pas, les deux courbes se confondent.
- Le choix du pas (ou, de façon équivalente, du nombre de pas
N) est déterminant : un pas trop grand donne une solution approchée grossière, qui peut s'écarter significativement de la réalité, en particulier là où la fonction varie vite. C'est une des limites de la méthode d'Euler. - Écrire une fonction générique comme
euler(g, x0, t), qui ne mentionne jamais explicitement le problème traité, permet de la réutiliser telle quelle pour n'importe quelle équation différentielle du premier ordre, et pour n'importe quel découpage temporel.
Pense-bête — méthode d'Euler
| dU/dt=f(U,t) | forme générale d'une équation différentielle du 1er ordre |
| h = t[1]-t[0] | pas constant, déduit du tableau des instants |
| U(i+1)=U(i)+h·f(U(i),t(i)) | relation d'Euler : avancer d'un pas h |
| np.zeros(N) | crée un tableau numpy de N zéros, à remplir ensuite |
| np.linspace(a,b,N+1) | engendre N+1 instants régulièrement espacés entre a et b |
| 'bo' / 'r-' | ronds bleus / ligne rouge continue (couleur + marqueur) |
Pas de Thonny sous la main ? Utilise Basthon
Ouvrir dans un nouvel onglet ↗Ctrl+V ou Cmd+V) le code copié avec les boutons « Copier le code » ci-dessus, puis appuie sur Entrée pour l'exécuter.