Méthode implicite : s’il existe un terme \(a_{ij}\neq0\) pour \(j> i\), alors la méthode est dite implicite. Pour calculer les \(K_i\), il faut résoudre un système d’équations non linéaires.
Méthode semi-implicite : si \(a_{ij}=0\) pour \(j>i\) (la matrice \(\mathbb{A}\) est triangulaire inférieure mais au moins un terme de la diagonale est non nul), alors la méthode est dite semi-implicite. Pour calculer les \(K_i\), il suffit de résoudre l’une après l’autre les équations non linéaires (et non pas un système).
Méthode explicite : si \(a_{ij}=0\) pour \(j\ge i\) (la matrice \(\mathbb{A}\) est triangulaire inférieure stricte), alors la méthode est dite explicite. Le calcul des \(K_i\) est alors direct.
Théorème de Lax-Richtmyer
Soit \(\omega\ge1\). Le schéma est convergent (d’ordre \(\omega\)) ssi il est zéro-stable et consistant (d’ordre \(\omega\ge1\)).
Zéro-stabilité
Les méthodes de Runge-Kutta sont zéro-stables.
Consistance (et ordre de consistance \(\omega\))
Une méthode de Runge-Kutta est consistante d’ordre \(\omega\ge1\) si elle satisfait les conditions suivantes :
L’ordre \(\omega\ge1\) d’une méthode de Runge-Kutta à \(s\ge1\) étapes convergente satisfait les inégalités suivantes :
\(
\omega \leq
\begin{cases}
2s, & \text{si la méthode est implicite}, \\
s, & \text{si la méthode est explicite et } s < 5, \\
s - 1, & \text{si la méthode est explicite et } s \geq 5.
\end{cases}
\)
A-stabilité
Soit \(\beta>0\) un nombre réel positif et considérons le problème de Cauchy
Sa solution est \(y(t)=e^{-\beta t}\) et l’on a \(y(t)\xrightarrow[t\to+\infty]{}0.\)
Une méthode de Runge-Kutta à \(s\ge1\) étages pour \(y'(t)=-\beta y(t)\) s’écrit: \( \begin{cases} u_0=y_0, \\ u_{n+1}=u_{n}+h \displaystyle\sum_{i=1}^sb_iK_i& n=0,1,\dots N-1\\ K_i=\displaystyle -\beta \left( u_n+h\sum_{j=1}^{s}a_{ij}K_j \right) & i=1,\dots s. \end{cases} \)
Si, sous d’éventuelles conditions sur \(h\), on a \(|u_n|\xrightarrow[n\to+\infty]{}0\) alors on dit que le schéma est A-stable.
Le calcul des conditions de consistance est un peu long et fastidieux dès que \(s\) est grand ou \(\omega\) est élevé. On peut utiliser un logiciel de calcul formel pour les obtenir, par exemple le module sympy.
On doit simplement indiquer le nombre d’étages \(s\) et le type de méthode (implicite, explicite, semi-implicite) pour obtenir les conditions de consistance. Indiquer le type de méthode permet de simplifier les calculs car de nombreux coefficients de la matrice sont nuls.
Code
# =============================================================================# Calcul des conditions de consistance pour un schéma RK# On doit indiquer le nombre d'étages s et le type de schéma implicite, semi-implicite ou explicite# =============================================================================s =2type="semi-implicite"# implicite, semi-implicite, explicite# =============================================================================# Fonctions pour calculer les conditions de consistance pour un schéma RK.## La fonction ordre_RK calcule les conditions pour les ordres 1, 2, 3 et 4 (même si théoriquement cela n'est pas possible) # - affiche la matrice de Butcher# - affiche les conditions de consistance obtenues pour chaque ordre par la fonction ordre_i# - renvoie une liste d'équations pour une éventuelle résolution## Chaque fonction ordre_i calcule les conditions pour être d'ordre i# - peut afficher les conditions de consistance [actuellement en commentaire]# - renvoie la liste d'équations## =============================================================================import sympy sympy.init_printing()from IPython.display import display, Math, Markdownprlat =lambda*args : display(Math(''.join( sympy.latex(arg) for arg in args )))def ordre_RK(s, type="implicite", A=None, b=None, c=None): j = sympy.symbols('j')if A isNone:iftype=="implicite": A = sympy.MatrixSymbol('a', s, s)eliftype=="semi-implicite": A = sympy.Matrix(s, s, lambda i,j: sympy.Symbol('a{}{}'.format(i, j)) if i >= j else0)eliftype=="explicite": A = sympy.Matrix(s, s, lambda i,j: sympy.Symbol('a{}{}'.format(i, j)) if i > j else0)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance (= pour être d'ordre 1)**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(*Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2]) display(Eq_2) display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**")) Eq_3 = ordre_3(s, A, b, c, j) Eqs.append(Eq_3) display(*Eq_3) display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**")) Eq_4 = ordre_4(s, A, b, c, j) Eqs.append(Eq_4) display(*Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1"# display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i])# display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}"# display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}"# display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}"# display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}"# display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}"# display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}"# display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}"# display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]# =================================================================================== # Affichage des conditions de consistance : attention aux indices qui vont de 0 à s-1# ===================================================================================display(Markdown(f"### Conditions pour un schéma {</span><span class="bu">type</span><span class="sc">} avec s = {</span>s<span class="sc">} étages "))Eqs = ordre_RK(s, type)
1.0.1 Conditions pour un schéma semi-implicite avec s = 2 étages
D’après la barrière de Butcher, un schéma RK à \(s=2\) étages explicite ne peut pas être d’ordre supérieur à \(s=2\).
On peut bien-sûr écrire les formules à la main, mais on peut aussi utiliser sympy comme suit :
Code
def ordre_RK(s, A=None, b=None, c=None): j = sympy.symbols('j')if A isNone: A = sympy.MatrixSymbol('a', s, s)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance = pour être d'ordre 1**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2])# display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**"))# Eq_3 = ordre_3(s, A, b, c, j) # Eqs.append(Eq_3) # display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**"))# Eq_4 = ordre_4(s, A, b, c, j)# Eqs.append(Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1" display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i]) display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}" display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}" display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}" display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}" display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}" display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}" display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}" display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]################################################### Conditions avec s étages##################################################s =2# paramètres dans le cas générale# b_0, b_1 = sympy.symbols('b_0 b_1', real=True)# c_0, c_1 = sympy.symbols('c_0 c_1', real=True)# a_00, a_01, a_10, a_11 = sympy.symbols('a_00 a_01 a_10 a_11', real=True)# matrice dans le cas général# b = [b_0, b_1]# c = [c_0, c_1]# A = [[a_00, a_01], [a_10, a_11]]# matrice dans notre cas particulierb = [ sympy.Rational(1,4), sympy.Rational(3,4) ]c = [ 0, sympy.Rational(2,3) ]A = [ [ 0, 0] , [ sympy.Rational(2,3), 0] ]Eqs = ordre_RK(s, A, b, c)
La suite vérifie \(u_n \xrightarrow[n\to \infty]{} 0\) ssi \(\left|q(x)\right|<1\). En étudiant brièvement la fonction \(q(x)\), on trouve que \(q(x)<1\) ssi \(x<6\) donné ci-dessous:.
Code
sympy.plot(q, (x,0,2.5), ylim=(-1.5,1.5), ylabel='q(x)', xlabel='x', title='q(x) en fonction de x')sympy.solve(abs(q)<1)
\(\displaystyle x < 2\)
Correction point 4 : test de l’ordre
Code
################################################### EDO##################################################t0 =0tfinal =2y0 =1phi =lambda t,y: t*(1-t**2)-y################################################### solution exacte##################################################t = sympy.Symbol('t')y = sympy.Function('y')edo = sympy.Eq( (y(t)).diff() , phi(t,y(t)) )solpar = sympy.dsolve(edo , ics={y(t0):y0})display(solpar)sol_exacte = sympy.lambdify(t,solpar.rhs,'numpy')################################################### schéma ################################################### Paramètres du schéma recuperés depuis la matrice de Butcher obtenue précédemmentcc = np.array(c)bb = np.array(b)AA = np.array(A)s =len(cc)# Schéma de Runge-Kutta implicite à s étagesdef RK(phi, tt, y0): h = tt[1] - tt[0] uu = np.zeros_like(tt) uu[0] = y0 for n inrange(len(tt) -1): k_0 = phi(tt[n], uu[n]) k_1 = phi(tt[n]+cc[1]*h, uu[n]+h*AA[1,0]*k_0) KK = [k_0, k_1] uu[n+1] = uu[n] + h*sum([KK[j]*b[j] for j inrange(s)])return uu################################################### ordre##################################################H = []err = []plt.figure(figsize=(16,5))ax1 = plt.subplot(1,2,1)plt.xlabel('$t$')plt.ylabel('$y$')ax1.grid(True)N =10for k inrange(6): N +=10 tt = np.linspace(t0, tfinal, N +1) H.append( tt[1] - tt[0] ) yy = sol_exacte(tt) uu = RK(phi, tt, y0) err.append( np.linalg.norm(uu-yy,np.inf) ) ax1.plot(tt,uu,label=f'Approchée avec N={</span>N<span class="sc">}')ax1.plot(tt,yy,label='Exacte') # sur la grille la plus fineax1.legend()ax2 = plt.subplot(1,2,2)ax2.plot( np.log(H), np.log(err), 'r-o')plt.xlabel(r'$\ln(h)$')plt.ylabel(r'$\ln(e)$')plt.title(f'Le schéma a ordre {</span>np<span class="sc">.</span>polyfit(np.log(H),np.log(err),<span class="dv">1</span>)[<span class="dv">0</span>]<span class="sc">:1.2f}')ax2.grid(True)
D’après la barrière de Butcher, un schéma RK à \(s=2\) étages implicite ne peut pas être d’ordre supérieur à \(s=4\).
On peut bien-sûr écrire les formules à la main, mais on peut aussi utiliser sympy comme suit :
Code
def ordre_RK(s, A=None, b=None, c=None): j = sympy.symbols('j')if A isNone: A = sympy.MatrixSymbol('a', s, s)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance = pour être d'ordre 1**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2]) display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**")) Eq_3 = ordre_3(s, A, b, c, j) Eqs.append(Eq_3) display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**")) Eq_4 = ordre_4(s, A, b, c, j) Eqs.append(Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1" display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i]) display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}" display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}" display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}" display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}" display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}" display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}" display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}" display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]################################################### Conditions avec s étages##################################################s =2# paramètres dans le cas générale# b_0, b_1 = sympy.symbols('b_0 b_1', real=True)# c_0, c_1 = sympy.symbols('c_0 c_1', real=True)# a_00, a_01, a_10, a_11 = sympy.symbols('a_00 a_01 a_10 a_11', real=True)# matrice dans le cas général# b = [b_0, b_1]# c = [c_0, c_1]# A = [[a_00, a_01], [a_10, a_11]]# matrice dans notre cas particulierb = [ sympy.Rational(1,4), sympy.Rational(3,4) ]c = [ 0, sympy.Rational(2,3) ]A = [ [ sympy.Rational(1,4), -sympy.Rational(1,4)] , [ sympy.Rational(1,4), sympy.Rational(5,12) ] ]Eqs = ordre_RK(s, A, b, c)
La suite vérifie \(u_n \xrightarrow[n\to \infty]{} 0\) ssi \(\left|q(x)\right|<1\). En étudiant brièvement la fonction \(q(x)\), on trouve que \(q(x)<1\) pour tout \(x>0\) :
Code
sympy.plot(q, (x,0,2.5), ylim=(-1.5,1.5), ylabel='q(x)', xlabel='x', title='q(x) en fonction de x')sympy.solve(abs(q)<1)
\(\displaystyle \text{True}\)
Correction point 4 : test de l’ordre
Code
################################################### EDO##################################################t0 =0tfinal =2y0 =1phi =lambda t,y: t*(1-t**2)-y################################################### solution exacte##################################################t = sympy.Symbol('t')y = sympy.Function('y')edo = sympy.Eq( (y(t)).diff() , phi(t,y(t)) )solpar = sympy.dsolve(edo , ics={y(t0):y0})display(solpar)sol_exacte = sympy.lambdify(t,solpar.rhs,'numpy')################################################### schéma ################################################### Paramètres du schéma recuperés depuis la matrice de Butcher obtenue précédemmentcc = np.array(c)bb = np.array(b)AA = np.array(A)s =len(cc)# Schéma de Runge-Kutta implicite à s étagesdef RK(phi, tt, y0): h = tt[1] - tt[0] uu = np.zeros_like(tt) uu[0] = y0 for n inrange(len(tt) -1): k_init = phi(tt[n], uu[n]) eqs =lambda KK: [ -KK[0] + phi(tt[n] , uu[n] + h/4*( KK[0] - KK[1]) ) ,-KK[1] + phi(tt[n] +2/3*h, uu[n] + h/12*( 3*KK[0] +5*KK[1]) ) ] KK = fsolve( eqs, [k_init,k_init] ) uu[n+1] = uu[n] + h/4*(KK[0]+3*KK[1])# eqs = lambda KK: [ -KK[i] + phi(tt[n] + cc[i]*h, uu[n] + h*sum([AA[i,j]*KK[j] for j in range(s)])) for i in range(s)]# KK = fsolve( eqs , k_init*np.ones(s) )# uu[n+1] = uu[n] + h*sum([KK[j]*b[j] for j in range(s)])return uu################################################### ordre##################################################H = []err = []plt.figure(figsize=(16,5))ax1 = plt.subplot(1,2,1)plt.xlabel('$t$')plt.ylabel('$y$')ax1.grid(True)N =10for k inrange(6): N +=10 tt = np.linspace(t0, tfinal, N +1) yy = sol_exacte(tt) uu = RK(phi, tt, y0) H.append( tt[1] - tt[0] ) err.append( np.linalg.norm(uu-yy,np.inf) ) ax1.plot(tt,uu,label=f'Approchée avec N={</span>N<span class="sc">}')ax1.plot(tt,yy,label='Exacte') # sur la grille la plus fineax1.legend()ax2 = plt.subplot(1,2,2)ax2.plot( np.log(H), np.log(err), 'r-o')plt.xlabel(r'$\ln(h)$')plt.ylabel(r'$\ln(e)$')plt.title(f'Le schéma a ordre {</span>np<span class="sc">.</span>polyfit(np.log(H),np.log(err),<span class="dv">1</span>)[<span class="dv">0</span>]<span class="sc">:1.2f}')ax2.grid(True)
D’après la barrière de Butcher, un schéma RK à \(s=2\) étages implicite ne peut pas être d’ordre supérieur à \(s=4\).
On peut bien-sûr écrire les formules à la main, mais on peut aussi utiliser sympy comme suit :
Code
def ordre_RK(s, A=None, b=None, c=None): j = sympy.symbols('j')if A isNone: A = sympy.MatrixSymbol('a', s, s)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance = pour être d'ordre 1**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2]) display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**")) Eq_3 = ordre_3(s, A, b, c, j) Eqs.append(Eq_3) display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**")) Eq_4 = ordre_4(s, A, b, c, j) Eqs.append(Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1" display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i]) display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}" display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}" display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}" display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}" display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}" display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}" display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}" display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]################################################### Conditions avec s étages##################################################s =2# paramètres dans le cas générale# b_0, b_1 = sympy.symbols('b_0 b_1', real=True)# c_0, c_1 = sympy.symbols('c_0 c_1', real=True)# a_00, a_01, a_10, a_11 = sympy.symbols('a_00 a_01 a_10 a_11', real=True)# matrice dans le cas général# b = [b_0, b_1]# c = [c_0, c_1]# A = [[a_00, a_01], [a_10, a_11]]# matrice dans notre cas particulierb = [ sympy.Rational(3,4), sympy.Rational(1,4) ]c = [ sympy.Rational(1,3) , 1 ]A = [ [ sympy.Rational(5,12), -sympy.Rational(1,12)] , [ sympy.Rational(3,4), sympy.Rational(1,4) ] ]Eqs = ordre_RK(s, A, b, c)
La suite vérifie \(u_n \xrightarrow[n\to \infty]{} 0\) ssi \(\left|q(x)\right|<1\). En étudiant brièvement la fonction \(q(x)\), on trouve que \(q(x)<1\) pour tout \(x>0\) :
Code
sympy.plot(q, (x,0,2.5), ylim=(-1.5,1.5), ylabel='q(x)', xlabel='x', title='q(x) en fonction de x')sympy.solve(abs(q)<1)
\(\displaystyle \text{True}\)
Correction point 4 : test de l’ordre
Code
################################################### EDO##################################################t0 =0tfinal =2y0 =1phi =lambda t,y: t*(1-t**2)-y################################################### solution exacte##################################################t = sympy.Symbol('t')y = sympy.Function('y')edo = sympy.Eq( (y(t)).diff() , phi(t,y(t)) )solpar = sympy.dsolve(edo , ics={y(t0):y0})display(solpar)sol_exacte = sympy.lambdify(t,solpar.rhs,'numpy')################################################### schéma ################################################### Paramètres du schéma recuperés depuis la matrice de Butcher obtenue précédemmentcc = np.array(c)bb = np.array(b)AA = np.array(A)s =len(cc)# Schéma de Runge-Kutta implicite à s étagesdef RK(phi, tt, y0): h = tt[1] - tt[0] uu = np.zeros_like(tt) uu[0] = y0 for n inrange(len(tt) -1): eqs =lambda KK: [ -KK[i] + phi(tt[n] + cc[i]*h, uu[n] + h*sum([AA[i,j]*KK[j] for j inrange(s)])) for i inrange(s)] k_init = phi(tt[n], uu[n]) KK = fsolve( eqs , k_init*np.ones(s) ) uu[n+1] = uu[n] + h*sum([KK[j]*b[j] for j inrange(s)])return uu################################################### ordre##################################################H = []err = []plt.figure(figsize=(16,5))ax1 = plt.subplot(1,2,1)plt.xlabel('$t$')plt.ylabel('$y$')ax1.grid(True)N =10for k inrange(6): N +=10 tt = np.linspace(t0, tfinal, N +1) H.append( tt[1] - tt[0] ) yy = sol_exacte(tt) uu = RK(phi, tt, y0) err.append( np.linalg.norm(uu-yy,np.inf) ) ax1.plot(tt,uu,label=f'Approchée avec N={</span>N<span class="sc">}')ax1.plot(tt,yy,label='Exacte') # sur la grille la plus fineax1.legend()ax2 = plt.subplot(1,2,2)ax2.plot( np.log(H), np.log(err), 'r-o')plt.xlabel(r'$\ln(h)$')plt.ylabel(r'$\ln(e)$')plt.title(f'Le schéma a ordre {</span>np<span class="sc">.</span>polyfit(np.log(H),np.log(err),<span class="dv">1</span>)[<span class="dv">0</span>]<span class="sc">:1.2f}')ax2.grid(True)
D’après la barrière de Butcher, un schéma RK à \(s=3\) étages explicite ne peut pas être d’ordre supérieur à \(s=3\).
On peut bien-sûr écrire les formules à la main, mais on peut aussi utiliser un solveur pour résoudre le système d’équations non-linéaires comme suit :
Code
def ordre_RK(s, A=None, b=None, c=None): j = sympy.symbols('j')if A isNone: A = sympy.MatrixSymbol('a', s, s)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance = pour être d'ordre 1**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2]) display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**")) Eq_3 = ordre_3(s, A, b, c, j) Eqs.append(Eq_3) # display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**"))# Eq_4 = ordre_4(s, A, b, c, j)# Eqs.append(Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1" display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i]) display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}" display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}" display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}" display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}" display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}" display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}" display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}" display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]################################################### Conditions avec s étages##################################################s =3# paramètres dans le cas générale# b_0, b_1, b_2 = sympy.symbols('b_0 b_1 b_2', real=True)# c_0, c_1, c_2 = sympy.symbols('c_0 c_1 c_2', real=True)# a_00, a_01, a_02, a_10, a_11, a_12, a_20, a_21, a_22 = sympy.symbols('a_00 a_01 a_02 a_10 a_11 a_12 a_20 a_21 a_22', real=True)# matrice dans le cas général# b = [b_0, b_1, b_2]# c = [c_0, c_1, c_2]# A = [[a_00, a_01, a_02], [a_10, a_11, a_12], [a_20, a_21, a_22]]# matrice dans notre cas particulierb = [ sympy.Rational(1,3), sympy.Rational(1,3), sympy.Rational(1,3)]c = [ 0, sympy.Rational(1,2), 1 ]A = [[ 0, 0, 0], [ sympy.Rational(1,2), 0, 0], [ 0, 1, 0]]Eqs = ordre_RK(s, A, b, c)
La suite vérifie \(u_n \xrightarrow[n\to \infty]{} 0\) ssi \(\left|q(x)\right|<1\). En étudiant brièvement la fonction \(q(x)\), on trouve que \(q(x)<1\) ssi \(x<6\) donné ci-dessous:.
Code
sympy.plot(q, (x,0,4), ylim=(-1.5,1.5), ylabel='q(x)', xlabel='x', title='q(x) en fonction de x')sol = sympy.solveset(q+1, x, domain=sympy.S.Reals) display(Markdown(f"$\\beta h<{</span>sol<span class="sc">.</span>args[<span class="dv">0</span>]<span class="sc">.</span>evalf()<span class="sc">}$"))
\(\beta h<2.51274532661833\)
Correction point 4 : test de l’ordre
Code
################################################### EDO##################################################t0 =0tfinal =2y0 =1phi =lambda t,y: t*(1-t**2)-y################################################### solution exacte##################################################t = sympy.Symbol('t')y = sympy.Function('y')edo = sympy.Eq( (y(t)).diff() , phi(t,y(t)) )solpar = sympy.dsolve(edo , ics={y(t0):y0})display(solpar)sol_exacte = sympy.lambdify(t,solpar.rhs,'numpy')################################################### schéma ################################################### Paramètres du schéma recuperés depuis la matrice de Butcher obtenue précédemmentcc = np.array(c)bb = np.array(b)AA = np.array(A)s =len(cc)# Schéma de Runge-Kutta implicite à s étagesdef RK(phi, tt, y0): h = tt[1] - tt[0] uu = np.zeros_like(tt) uu[0] = y0 for n inrange(len(tt) -1): k_0 = phi(tt[n], uu[n]) k_1 = phi(tt[n]+cc[1]*h, uu[n]+h*AA[1,0]*k_0) k_2 = phi(tt[n]+cc[2]*h, uu[n]+h*(AA[2,0]*k_0+AA[2,1]*k_1)) KK = [k_0, k_1, k_2] uu[n+1] = uu[n] + h*sum([KK[j]*b[j] for j inrange(s)])return uu################################################### ordre##################################################H = []err = []plt.figure(figsize=(16,5))ax1 = plt.subplot(1,2,1)plt.xlabel('$t$')plt.ylabel('$y$')ax1.grid(True)N =10for k inrange(6): N +=10 tt = np.linspace(t0, tfinal, N +1) H.append( tt[1] - tt[0] ) yy = sol_exacte(tt) uu = RK(phi, tt, y0) err.append( np.linalg.norm(uu-yy,np.inf) ) ax1.plot(tt,uu,label=f'Approchée avec N={</span>N<span class="sc">}')ax1.plot(tt,yy,label='Exacte') # sur la grille la plus fineax1.legend()ax2 = plt.subplot(1,2,2)ax2.plot( np.log(H), np.log(err), 'r-o')plt.xlabel(r'$\ln(h)$')plt.ylabel(r'$\ln(e)$')plt.title(f'Le schéma a ordre {</span>np<span class="sc">.</span>polyfit(np.log(H),np.log(err),<span class="dv">1</span>)[<span class="dv">0</span>]<span class="sc">:1.2f}')ax2.grid(True)
D’après la barrière de Butcher, un schéma RK à \(s=3\) étages implicite ne peut pas être d’ordre supérieur à \(s=6\).
On peut bien-sûr écrire les formules à la main, mais on peut aussi utiliser un solveur pour résoudre le système d’équations non-linéaires comme suit :
Code
def ordre_RK(s, A=None, b=None, c=None): j = sympy.symbols('j')if A isNone: A = sympy.MatrixSymbol('a', s, s)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance = pour être d'ordre 1**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2]) display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**")) Eq_3 = ordre_3(s, A, b, c, j) Eqs.append(Eq_3) display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**")) Eq_4 = ordre_4(s, A, b, c, j) Eqs.append(Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1" display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i]) display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}" display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}" display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}" display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}" display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}" display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}" display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}" display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]################################################### Conditions avec s étages##################################################s =3# paramètres dans le cas générale# b_0, b_1, b_2 = sympy.symbols('b_0 b_1 b_2', real=True)# c_0, c_1, c_2 = sympy.symbols('c_0 c_1 c_2', real=True)# a_00, a_01, a_02, a_10, a_11, a_12, a_20, a_21, a_22 = sympy.symbols('a_00 a_01 a_02 a_10 a_11 a_12 a_20 a_21 a_22', real=True)# matrice dans le cas général# b = [b_0, b_1, b_2]# c = [c_0, c_1, c_2]# A = [[a_00, a_01, a_02], [a_10, a_11, a_12], [a_20, a_21, a_22]]# matrice dans notre cas particulierb = [ sympy.Rational(1,6), sympy.Rational(4,6), sympy.Rational(1,6)]c = [ 0, sympy.Rational(1,2), 1 ]A = [[ 0, 0, 0], [ sympy.Rational(5,24), sympy.Rational(1,3), -sympy.Rational(1,24)], [ 0, 1, 0]]Eqs = ordre_RK(s, A, b, c)
\(\displaystyle q(x) = \frac{- x^{3} + 5 x^{2} - 16 x + 24}{x^{2} + 8 x + 24}\)
La suite vérifie \(u_n \xrightarrow[n\to \infty]{} 0\) ssi \(\left|q(x)\right|<1\). En étudiant brièvement la fonction \(q(x)\), on trouve que \(q(x)<1\) ssi \(x<6\) donné ci-dessous:.
Code
sympy.plot(q, (x,0,10), ylim=(-1.5,1.5), ylabel='q(x)', xlabel='x', title='q(x) en fonction de x')sol = sympy.solveset(q+1, x, domain=sympy.S.Reals) display(Markdown(f"$\\beta h<{</span>sol<span class="sc">.</span>args[<span class="dv">0</span>]<span class="sc">}$"))
\(\beta h<6\)
Correction point 4 : test de l’ordre
Code
################################################### EDO##################################################t0 =0tfinal =10y0 =1phi =lambda t,y: -y################################################### solution exacte##################################################t = sympy.Symbol('t')y = sympy.Function('y')edo = sympy.Eq( (y(t)).diff() , phi(t,y(t)) )solpar = sympy.dsolve(edo , ics={y(t0):y0})display(solpar)sol_exacte = sympy.lambdify(t,solpar.rhs,'numpy')################################################### schéma ################################################### Paramètres du schéma recuperés depuis la matrice de Butcher obtenue précédemmentcc = np.array(c)bb = np.array(b)AA = np.array(A)s =len(cc)# Schéma de Runge-Kutta implicite à s étagesdef RK(phi, tt, y0): h = tt[1] - tt[0] uu = np.zeros_like(tt) uu[0] = y0 for n inrange(len(tt) -1): k_init = phi(tt[n],uu[n]) KK = fsolve( lambda kk: [ -kk[i] + phi( tt[n]+cc[i]*h , uu[n] + h*sum([kk[j]*AA[i,j] for j inrange(s)]) ) for i inrange(s) ], k_init*np.ones((s,1)) ) uu[n+1] = uu[n] + h*sum([KK[j]*b[j] for j inrange(s)])return uu################################################### ordre##################################################H = []err = []plt.figure(figsize=(16,5))ax1 = plt.subplot(1,2,1)plt.xlabel('$t$')plt.ylabel('$y$')ax1.grid(True)N =10for k inrange(6): N +=10 tt = np.linspace(t0, tfinal, N +1) H.append( tt[1] - tt[0] ) yy = sol_exacte(tt) uu = RK(phi, tt, y0) err.append( np.linalg.norm(uu-yy,np.inf) ) ax1.plot(tt,uu,label=f'Approchée avec N={</span>N<span class="sc">}')ax1.plot(tt,yy,label='Exacte') # sur la grille la plus fineax1.legend()ax2 = plt.subplot(1,2,2)ax2.plot( np.log(H), np.log(err), 'r-o')plt.xlabel(r'$\ln(h)$')plt.ylabel(r'$\ln(e)$')plt.title(f'Le schéma a ordre {</span>np<span class="sc">.</span>polyfit(np.log(H),np.log(err),<span class="dv">1</span>)[<span class="dv">0</span>]<span class="sc">:1.2f}')ax2.grid(True)
\(\displaystyle y{\left(t \right)} = e^{- t}\)
2.0.6 🎨 Exercice (\(s=3\), explicite avec paramètres)
D’après la barrière de Butcher, un schéma RK à \(s=3\) étages explicite ne peut pas être d’ordre supérieur à \(s=3\).
On peut bien-sûr écrire les formules à la main, mais on peut aussi utiliser sympy comme suit :
Code
def ordre_RK(s, A=None, b=None, c=None): j = sympy.symbols('j')if A isNone: A = sympy.MatrixSymbol('a', s, s)else: A = sympy.Matrix(A)if c isNone: c = sympy.symbols(f'c_0:{</span>s<span class="sc">}') if b isNone: b = sympy.symbols(f'b_0:{</span>s<span class="sc">}') display(Markdown("**Matrice de Butcher**")) But = matrice_Butcher(s, A, b, c) Eqs = [] display(Markdown(f"**On a {</span>s<span class="op">+</span><span class="dv">1</span><span class="sc">} conditions pour avoir consistance = pour être d'ordre 1**")) Eq_1 = ordre_1(s, A, b, c, j) Eqs.append(Eq_1) display(Markdown("**On doit ajouter 1 condition pour être d'ordre 2**")) Eq_2 = ordre_2(s, A, b, c, j) Eqs.append([Eq_2]) display(Markdown("**On doit ajouter 2 conditions pour être d'ordre 3**")) Eq_3 = ordre_3(s, A, b, c, j) Eqs.append(Eq_3) # display(Markdown("**On doit ajouter 4 conditions pour être d'ordre 4**"))# Eq_4 = ordre_4(s, A, b, c, j)# Eqs.append(Eq_4)return Eqsdef matrice_Butcher(s, A, b, c): But = sympy.Matrix(A) But = But.col_insert(0, sympy.Matrix(c)) last = [sympy.Symbol(" ")] last.extend(b) But = But.row_insert(s,sympy.Matrix(last).T) display(Math(sympy.latex(sympy.Matrix(But))))return Butdef ordre_1(s, A, b, c, j): texte =r"\sum_{j=1}^s b_j ="+f"{</span><span class="bu">sum</span>(b)<span class="sc">.</span>simplify()<span class="sc">}" texte +=r"\text{ doit être égale à }1" display(Math(texte)) Eq_1 = [sympy.Eq( sum(b).simplify(), 1 )]for i inrange(s): somma = sympy.summation(A[i,j],(j,0,s-1)).simplify() texte =r'\sum_{j=1}^s a_{'</span><span class="op">+</span><span class="bu">str</span>(i)<span class="op">+</span><span class="vs">r'j}='+ sympy.latex( somma ) texte +=r"\text{ doit être égale à }"+sympy.latex(c[i]) display(Math( texte )) Eq_1.append(sympy.Eq( somma, c[i] ))return Eq_1 def ordre_2(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j='+sympy.latex(sum([b[i]*c[i] for i inrange(s)]).simplify()) texte +=r"\text{ doit être égale à }\frac{1}{2}" display(Math(texte)) Eq_2 = sympy.Eq( sum([b[i]*c[i] for i inrange(s)]).simplify(), sympy.Rational(1,2) )return Eq_2def ordre_3(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^2=' texte += sympy.latex( sum([b[i]*c[i]**2for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{3}" display(Math(texte)) Eq_3_1 = sympy.Eq( sum([b[i]*c[i]**2for i inrange(s)]).simplify(), sympy.Rational(1,3) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j=' somma =sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{6}" display(Math(texte)) Eq_3_2 = sympy.Eq( sum([b[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,6) )return [Eq_3_1, Eq_3_2]def ordre_4(s, A, b, c, j): texte =r'\sum_{j=1}^s b_j c_j^3=' texte += sympy.latex( sum([b[i]*c[i]**3for i inrange(s)]).simplify() ) texte +=r"\text{ doit être égale à }\frac{1}{4}" display(Math(texte)) Eq_4_1 = sympy.Eq( sum([b[i]*c[i]**3for i inrange(s)]).simplify(), sympy.Rational(1,4) ) texte =r'\sum_{i,j=1}^s b_i c_i a_{ij} c_j=' somma =sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{8}" display(Math(texte)) Eq_4_2 = sympy.Eq( sum([b[i]*c[i]*A[i,j]*c[j] for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,8) ) texte =r'\sum_{i,j=1}^s b_i a_{ij} c_j^2=' somma =sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{12}" display(Math(texte)) Eq_4_3 = sympy.Eq( sum([b[i]*A[i,j]*c[j]**2for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,12) ) texte =r'\sum_{i,j,k=1}^s b_i a_{ij} a_{jk} c_k=' somma =sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify() texte = texte + sympy.latex(somma) texte +=r"\text{ doit être égale à }\frac{1}{24}" display(Math(texte)) Eq_4_4 = sympy.Eq( sum([b[i]*A[i,j]*A[j,k]*c[k] for k inrange(s) for j inrange(s) for i inrange(s)]).simplify(), sympy.Rational(1,24) )return [Eq_4_1, Eq_4_2, Eq_4_3, Eq_4_4]################################################### Conditions avec s étages##################################################s =3# paramètres dans le cas générale# b_0, b_1, b_2 = sympy.symbols('b_0 b_1 b_2', real=True)# c_0, c_1, c_2 = sympy.symbols('c_0 c_1 c_2', real=True)# a_00, a_01, a_02, a_10, a_11, a_12, a_20, a_21, a_22 = sympy.symbols('a_00 a_01 a_02 a_10 a_11 a_12 a_20 a_21 a_22', real=True)# matrice dans le cas général# b = [b_0, b_1, b_2]# c = [c_0, c_1, c_2]# A = [[a_00, a_01, a_02], [a_10, a_11, a_12], [a_20, a_21, a_22]]# matrice dans notre cas particulierb_0, b_1 = sympy.symbols('b_0 b_1', real=True)b = [ b_0, b_1, 0 ]c = [ 0, sympy.Rational(1,3), sympy.Rational(2,3) ]A = [[ 0, 0, 0], [ sympy.Rational(1,3), 0, 0], [ 0, sympy.Rational(2,3), 0]]Eqs = ordre_RK(s, A, b, c)
La suite vérifie \(u_n \xrightarrow[n\to \infty]{} 0\) ssi \(\left|q(x)\right|<1\). En étudiant brièvement la fonction \(q(x)\), on trouve que \(q(x)<1\) ssi \(x<6\) donné ci-dessous:.
Code
sympy.plot(q, (x,0,2.5), ylim=(-1.5,1.5), ylabel='q(x)', xlabel='x', title='q(x) en fonction de x')sympy.solve(abs(q)<1)
\(\displaystyle x < 2\)
Correction point 4 : test de l’ordre
Code
################################################### EDO##################################################t0 =0tfinal =2y0 =2phi =lambda t,y: y*(1-y)*t################################################### solution exacte##################################################t = sympy.Symbol('t')y = sympy.Function('y')edo = sympy.Eq( (y(t)).diff() , phi(t,y(t)) )solpar = sympy.dsolve(edo , ics={y(t0):y0})display(solpar)sol_exacte = sympy.lambdify(t,solpar.rhs,'numpy')################################################### schéma ################################################### Paramètres du schéma recuperés depuis la matrice de Butcher obtenue précédemmentcc = np.array(c)bb = np.array(b)AA = np.array(A)s =len(cc)# Schéma de Runge-Kutta implicite à s étagesdef RK(phi, tt, y0): h = tt[1] - tt[0] uu = np.zeros_like(tt) uu[0] = y0 for n inrange(len(tt) -1): k_0 = phi(tt[n], uu[n]) k_1 = phi(tt[n]+cc[1]*h, uu[n]+h*AA[1,0]*k_0) k_2 = phi(tt[n]+cc[2]*h, uu[n]+h*(AA[2,0]*k_0+AA[2,1]*k_1)) KK = [k_0, k_1, k_2] uu[n+1] = uu[n] + h*sum([KK[j]*b[j] for j inrange(s)])return uu################################################### ordre##################################################H = []err = []plt.figure(figsize=(16,5))ax1 = plt.subplot(1,2,1)plt.xlabel('$t$')plt.ylabel('$y$')ax1.grid(True)N =10for k inrange(6): N +=10 tt = np.linspace(t0, tfinal, N +1) H.append( tt[1] - tt[0] ) yy = sol_exacte(tt) uu = RK(phi, tt, y0) err.append( np.linalg.norm(uu-yy,np.inf) ) ax1.plot(tt,uu,label=f'Approchée avec N={</span>N<span class="sc">}')ax1.plot(tt,yy,label='Exacte') # sur la grille la plus fineax1.legend()ax2 = plt.subplot(1,2,2)ax2.plot( np.log(H), np.log(err), 'r-o')plt.xlabel(r'$\ln(h)$')plt.ylabel(r'$\ln(e)$')plt.title(f'Le schéma a ordre {</span>np<span class="sc">.</span>polyfit(np.log(H),np.log(err),<span class="dv">1</span>)[<span class="dv">0</span>]<span class="sc">:1.2f}')ax2.grid(True)
3 Catalogue de quelques schémas 🎨 Runge-Kutta rencontrés dans la littérature
Dans la littérature on trouve beaucoup de schémas de Runge-Kutta. Ci-dessous une petite liste de ces méthodes. Pour chaque méthode de Runge-Kutta donné à travers sa matrice de Butcher, il est indiqué le nombre d’étages \(s\), son ordre de convergence \(\omega\) comme reporté dans la littérature et si le schéma est explicite, semi-implicite ou implicite.
Exercice : étudier l’ordre théorique de convergence (au plus jusqu’à 4) et vérifier empiriquement si l’ordre annoncé coïncide avec l’ordre observé sur un problème de Cauchy dont on connaît la solution exacte.