• Aucun résultat trouvé

La documentation se trouve ici6 Pour intégrer en Python, on utilise généralement la méthode quad du module

scipy.integrate. On a en prime une estimation de l’erreur commise.

>>> int_rec_gauche(lambda x: x*x,0,1,20) 0.30875000000000014 >>> int_rec_milieu(lambda x: x*x,0,1,20) 0.3331250000000001 >>> int_trapezes(lambda x: x*x,0,1,20) 0.3337500000000001 >>> int_simpson(lambda x: x*x,0,1,20) 0.3333333333333335

>>> import scipy.integrate as sci ; sci.quad(lambda x: x*x,0,1) (0.33333333333333337, 3.700743415417189e-15)

La méthode utilisée est assez ésotérique (c’est l’aide Python qui le dit !) et utilise pas mal de méthodes différentes suivant ce qu’on lui donne en paramètre, bien plus complexes que celles présentées ici. Remarquez que notre méthode de Simpson est bien exacte sur la fonction x 7→ x2.

Étant donné deux tableaux de pointsX et Y, avec Y le tableau des valeurs prises par une fonction f en les points deX, on peut explicitement utiliser la méthode des trapèzes ou la méthode de Simpson :

>>> X=[1,3,4] ; Y=[x*x for x in X] >>> sci.trapz(x=X,y=Y) 22.5 >>> sci.simps(x=X,y=Y) 21.0 >>> int_simpson(lambda x:x*x,1,4,1) 21.0

On retrouve d’ailleurs bien l’exactitude de la méthode de Simpson sur les fonctions polynomiales de degré 2. Remarquez que Python se débrouille même si les points ne sont pas régulièrement espacés : en fait il prend les points par groupe de trois et calcule (l’unique) parabole passant par ces points et en déduit une méthode d’intégration.

Plus généralement, il peut faire pareil avec les méthodes de Newton-Cotes (il y a une méthodenewton_cotes dans le modulescipy.optimize).

Chapitre 8

Résolution approchée d’équations

différentielles

8.1

Introduction

8.1.1

Motivation

De même qu’il n’est pas possible d’exprimer les primitives de certaines fonctions à l’aide des fonctions usuelles, il n’est en général pas possible de déterminer la solution formelle d’un système différentiel. C’est le cas par exemple de l’équation du pendule amorti :

..

θ(t) + k1sin(θ(t)) + k2 .

θ(t) = 0

De tels systèmes non linéaires interviennent par exemple en chimie ou en mécanique des fluides. La résolution formelle étant impossible, il est nécessaire de résoudre numériquement le système pour visualiser la solution.

8.1.2

Reformulation

Une équation différentielle ordinaire s’écrit sous la forme1 :

(?) 

x(t0) = x0 (condition initiale)

x0(t) = f (x(t), t) pour t au voisinage de t0.

où la fonction f est une fonction continue, a priori quelconque. Notez que l’inconnue x peut aussi bien être une fonction à valeurs dans R qu’une fonction à valeurs dans Rk, avec k > 1 : cette formulation2 permet de traiter aussi bien des équations scalaires d’ordre supérieur à 1 que des systèmes d’équations différentielles.

Par exemple, l’équation du système du pendule amorti se reformule en l’équation vectorielle d’ordre 1 :  x(t0) = x0 x0(t) = f (x(t), t) où x0=  θ(t 0) . θ(t0)  et f :  a b  , t 7→  b −k1sin(a) − k2b 

Notez que dans ce cas, l’équation est autonome : la fonction f ne dépend pas du temps. Mais on sera obligé de la déclarer sous cette forme en Python lorsqu’on utiliseraodeint. Un autre exemple serait l’équation régissant la tension uC aux bornes d’un condensateur d’un circuit RLC :

uC(t) + RC

duC

dt (t) + LC d2uC

dt2 (t) = E(t)

Dans ce cas, l’équation différentielle se reformule en  x(0) = x0 x0(t) = f (x(t), t) où x0=  uC(0) . uC(0)  et f :  a b  , t 7→  b 1 LC[E(t) − a − RCb] 

L’équation différentielle n’est pas autonome si la force électromotrice du générateur n’est pas constante.

1. On suit ici la même formulation que la syntaxe de la fonction odeint en Python, bien que la convention mathématique soit plutôt d’écrire l’équation différentielle sous la forme x0(t) = f (t, x(t)). Cela ne change pas grand chose en définitive, mais attention à ne pas faire de confusion en Python !

8.1. INTRODUCTION Lycée Masséna

Les théorèmes mathématiques donnent en général l’existence et l’unicité d’une solution x à l’équation (?), définie au voisinage de t0 (avec quelques hypothèses sur f , qu’on ne détaillera pas ici). il est nécessaire de procéder à une

résolution numérique (approchée) si l’on souhaite visualiser l’allure de cette solution.

8.1.3

Lien avec l’intégration

On souhaite donc résoudre l’équation (?) de manière approchée. On connaît la valeur x(t0) = x0 de la solution au

temps initial t0. L’idée de la résolution approchée que l’on va développer dans ce chapitre est de calculer de proche en

proche des approximations de x aux temps t0+ h, t0+ 2h, t0+ 3h, . . . , avec h un « petit » pas de temps.

Faisons le lien avec le chapitre précédent : connaissant une approximation de x(t), on peut écrire que :

x(t + h) = x(t) + Z t+h t x0(u) du = x(t) + Z t+h t f (x(u), u) du

Pour estimer x(t + h) à partir d’une estimation de x(t), on est donc ramené à approcher l’intégraleRt+h

t f (x(u), u) du,

ce qu’on sait faire depuis le chapitre précédent ! La difficulté ici est que l’on ne connaît (une approximation de) la valeur de la solution x qu’au temps t, et pas sur le reste de l’intervalle [t, t + h]. On peut donc appliquer facilement la méthode des rectangles à gauche, et on verra comment contourner cette difficulté pour appliquer les autres méthodes d’intégration.

8.1.4

Formulation « à la odeint »

La fonctionodeint du module scipy.integrate permet de résoudre des équations différentielles sous la forme (?) déja évoquée :

(?) 

x(t0) = x0 (condition initiale)

x0(t) = f (x(t), t) pour t au voisinage de t0.

La méthode utilisée est plus complexe (et plus efficace) que celles développées dans ce chapitre. odeint prend en paramètres :

— la fonctionf ;

— une valeurx0 (qui peut-être un vecteur représenté par un tableau Numpy) ;

— un tableau de temps[t0, t1, . . . , tn−1] (le premier élément de ce tableau est le temps t0 du système (?)).

et renvoie via l’appelodeint(f, x0, T) une approximation de la solution aux temps ti, sous la forme d’un tableau

Numpy de taille n. Bien sûr, le premier élément de ce tableau est x0. L’idée est de calculer une approximation de x(ti+1) à partir d’une approximation de x(ti).

Donnons par exemple la tension aux bornes d’un condensateur soumis à un échelon de tension « en triangles ». On suppose les constantes déja définies, ainsi que la fonctionE(t) donnant la tension en fonctions du temps. Voici une définition de la fonctionf :

def fRLC(X,t):

u,up=X[0], X[1] #tension, dérivée de la tension.

return np.array([up, (E(t)-R*C*up-u)/(L*C)]) #dérivée et dérivée seconde.

On notep la période du générateur, et on résout en prenant 1000 points sur l’intervalle [0, 8p]. Le code suivant trace conjointement la tension aux bornes du générateur et la tension aux bornes du condensateur, avec condition initiale uC(0) = 0 et

.

uC(0) = 0.

T=np.linspace(0, 8*p, 1000)

X0=np.array([0.,0.]) #initialement, tension et dérivée de la tension sont nulles. Sode=odeint(fRLC,X0, T) #solution odeint

plt.plot(T,[E(t) for t in T], label="E(t)")

plt.plot(T,[u[0] for u in Sode], label="u_C(t)") #on ne veut que la tension, pas sa dérivée. plt.legend(loc="lower left")

plt.show()

Ici, le résultat deodeint, stocké dans Sode, est un tableau Numpy à deux dimensions : 1000 « lignes » (correspondant à la taille deT), et deux « colonnes », puisqu’ici on travaille en dimension 2. Dans cet exemple, on n’est intéressé que par la tension aux bornes du condensateur, et pas sa dérivée. On veut donc garder la première colonne, et la tracer en fonction du temps. Voici le graphe obtenu :

Pour avoir le portrait de phase (uC(t) en abscisse, dudtC(t) en ordonnée), il faut écrire :

plt.plot([u[0] for u in Sode],[u[1] for u in Sode])

Voici le résultat :

La syntaxe utilisée pour le tracé pourra être utilisée avec les fonctions que l’on va écrire, qui suivront la même spécification deodeint.