Table des matières Python

Équation de Poisson 1D : méthode des éléments finis

1. Introduction

Ce document montre la résolution numérique de l'équation de Poisson à une dimension par la méthode des éléments finis. Bien que cette méthode ait peu d'intérêt dans le cas d'un problème unidimensionnel (la méthode des différences finies convient parfaitement), ce cas constitue une introduction à la méthode des éléments finis pour un problème bidimensionnel (avec maillage triangulaire).

Soit u(x) une fonction définie sur l'intervalle [0,L] et deux fois dérivable vérifiant l'équation différentielle suivante :

d2udx2=-f(x)(1)

f(x) est une fonction donnée sur l'intervalle [0,L]. Par exemple pour un problème de thermique en régime stationnaire, u(x) est la température et f(x) la source thermique divisée par la conductivité du milieu. Pour un problème d'électrostatique, u(x) et le potentiel et f(x) la densité de charge divisée par la permittivité du milieu.

La solution de cette équation est unique si une condition limite est donnée sur chaque borne de l'intervalle. Considérons la condition limite en x=0. La conditon limite de type Dirichlet consiste à fixer la valeur de u, soit u(0)=u0 . Pour un problème de thermique, cela correspond à une température imposée à une extrémité. Pour un problème d'électrostatique, il s'agit d'un potentiel électrostatique imposé. La condition limite de Neumann consiste à fixer la dérivée première de u, soit dudx(0)=g0 (lettre g comme gradient). Pour un problème de thermique, cela correspond à un flux thermique imposé. Pour un problème d'électrostatique, cela correspond à un champ électrique imposé (sur la surface d'un corps isolant chargé). Une condition limite plus générale, appelée condition de Robin, s'écrit sous la forme :

dudx(0)=K0(u(0)-u0)+g0(2)

Lorsque K0=0, on retrouve la condition de Neumann. Lorsque K0 on retrouve la condition de Dirichlet. Lorsque g0=0, on obtient une condition limite souvent utilisé dans les problèmes de thermique, consistant en un flux thermique proportionnel à la différence de température entre l'extrémité et une température constante. Remarquons que certaines conditions limites ne permettent pas d'obtenir une solution unique : par exemple si f=0 il est impossible d'imposer une condition de Neumann sur les deux bords puisque dans ce cas la dérivée de u est constante.

2. Méthode des éléments finis

2.a. Formulation variationnelle

Soit v(x) une fonction continue et dérivable par morceaux sur [0,L], appelée fonction test. On considère l'intégrale suivante :

0Lddx(v(x)dudx)dx=v(L)dudx(L)-v(0)dudx(0)(3)

Cette intégrale s'écrit aussi :

0Lddx(v(x)dudx)dx= 0Lv(x)d2udx2dx+0Ldvdxdudxdx=-0Lv(x)f(x)+0Ldvdxdudxdx(4)

Au moyen de la condition limite de Robin ((2)), on obtient l'égalité suivante :

0Lv(x)f(x)dx=0Ldvdxdudxdx+v(0)(K0(u(0)-u0)+g0)-v(L)(KL(u(L)-uL)+gL)(5)

K0,u0,g0,KL,uL,gL sont des constantes qui définissent les conditions limites.

Soit V l'ensemble des fonctions de carré sommable sur l'intervalle [0,L] et dont la dérivée est aussi de carré sommable. La formulation variationnelle du problème de Poisson [1][2] consiste à écrire que l'égalité (5) doit être vraie pour toute fonction test v appartenant à V. La fonction recherchée u, appelée aussi fonction d'essai, doit aussi appartenir à V. Si u(x) est solution de l'équation (1) avec les conditions limites (2) alors elle satisfait l'énoncé variationnel mais la réciproque n'est pas vraie.

2.b. Maillage de l'intervalle - Fonctions affines par morceaux

L'intervalle [0,L] est subdivisé en N sous-intervalles. On obtient ainsi un maillage (unidimensionnel) dont les N+1 nœuds sont :

x0=0,x1,x2,xN-1,xN=L(6)

Remarquons que les sous-intervalles n'ont pas nécessairement la même largeur. On définit la largeur maximale :

h=max(|xi+1-xi|)(7)

Une solution u(x) du problème variationnel est recherchée sous la forme d'une fonction continue et affine par morceaux, c'est-à-dire affine sur chaque sous-intervalle [xi,xi+1]. La figure suivante en donne l'illustration dans le cas N=5.

affine-fig.svgFigure pleine page

Cette fonction est notée uh. Bien que ses valeurs aux nœuds du maillage soient censées être des approximations de la fonction u(x) solution de l'équation de Poisson, elle n'est pas dérivable aux nœuds et sa dérivée seconde sur chaque sous-intervalle est nulle.

On note Vh l'ensemble des fonctions affines par morceaux pour le maillage choisi, qui est un sous-ensemble de V. Une fonction de Vh est entièrement définie par ses valeurs aux nœuds du maillage.

La formulation variationnelle dans Vh consiste à recherche une fonction uhVh telle que pour toute fonction test vVh on ait :

0Lv(x)f(x)dx=0Ldvdxduhdxdx+v(0)(K0(uh(0)-u0)+g0)-v(L)(KL(uh(L)-uL)+gL)(8)

On introduit les N+1 fonctions de Vh notées ϕn et définies par :

ϕn(xi)=1sii=n(9)ϕn(xi)=0siin(10)

Les fonctions ϕ0 et ϕ3 sont tracées sur la figure ci-dessus. Ces fonctions sont appelées fonctions chapeaux en raison de leur forme. Le support de la fonction ϕn est [xn-1,xn+1] pour n=1,N-1 . Le support de ϕ0 est [x0,x1], celui de ϕN est [xN-1,xN].

Il est facile de démontrer que ces N+1 fonctions chapeaux constituent une base de Vh. La fonction uh peut donc s'écrire sous la forme suivante :

uh(x)=j=0Nξjϕj(x)(11)

Le nombre réel ξj est la valeur de uh au nœud j, c'est-à-dire l'approximation de u(xj). La fonction test v se décompose de la même manière puisqu'elle appartient aussi à Vh.

La formulation variationnelle dans le cas où u et v appartiennent à Vh (équation (8)) se traduit par N+1 équations :

0Lϕi(x)f(x)dx=0Ldϕidxduhdxdx+ϕi(0)(K0(uh(0)-u0)+g0)-ϕi(L)(KL(uh(L)-uL)+gL),i=0,1N(12)

La formulation variationnelle pour les fonctions de Vh implique évidemment ces N+1 équations puisque les fonctions chapeaux sont dans Vh. Réciproquement, cette condition vérifiée pour i=0,1,N-1 implique l'identité (5) pour toute fonction v de Vh.

2.c. Équations algébriques

Le report de l'expression (11) dans l'égalité (12) conduit à :

0Lϕi(x)f(x)dx=j=0Nξj0Ldϕidxdϕjdxdx +ϕi(0)(K0(ξ0-u0)+g0)-ϕi(L)(KL(ξN-uL)+gL),i=0,1N(13)

On obtient N+1 équations algébriques linéaires pour les n+1 nombres ξi .

Posons :

Aij=0Ldϕidxdϕjdxdx(14) bi=0Lϕi(x)f(x)dx(15)

Les équations s'écrivent :

j=0NAijξj+ϕi(0)K0ξ0-ϕi(L)KKξN=bi+ϕi(0)(K0u0-g0)-ϕi(L)(KLuL-gL),i=0,1N(16)

Ce système d'équations peut être écrit en isolant la première et la dernière équations, qui font intervenir les conditions limites :

j=0NA0jξj+K0ξ0=b0+K0u0-g0(17) j=0NAijξj=bi,i=1,N-1(18) j=0NANjξj-KLξN=bN-(KLuL-gL) (19)

La condition limite de Dirichlet en x=0 s'obtient avec K0 , ce qui revient à remplacer la première équation par ξ0=u0 (comme on pouvait s'y attendre). De même, une condition de Dirichlet en x=L consiste à remplacer la dernière équation par ξN=uL . Une condition de Neumann en x=0 s'obtient en posant K0=0.

Explicitons les coefficients Aij . Seuls les coefficients Ai,i,Ai-1i,Aii+1 sont non nuls. On pose hi=xi-xi-1 pour i=1,2,N .

A00=0L(dϕ0dx)2dx =x0x1dxh12=1h1(20) ANN=0L(dϕNdx)2dx =xN-1xNdxhN2=1hN(21) Aii=0L(dϕidx)2dx=xi-1xidxhi2+xixi+1dxhi+12=1hi+1hi+1,i=1,N-1(22) Aii+1=0Ldϕidxϕi+1dxdx=-xixi+1dxhi+12=-1hi+1(23) Ai-1i=-1hi(24)

Les coefficients bi sont évalués avec la méthode des trapèzes. Pour i=1,N-1 :

bi=xi-1xi+1ϕi(x)f(x)dx(25) (ϕi(xi-1)f(xi-1)+ϕi(xi)f(xi))hi2+ (ϕi(xi)f(xi)+ϕi(xi+1)f(xi+1))hi+12(26) =f(xi)hi+hi+12(27) b0=f(x0)h12(28) bN=f(xN)hN2(29)

Pour i=1,N-1 , l'équation s'écrit :

-ξi-1hi+(1hi+1hi+1)ξi-ξi+1hi+1=f(xi)hi+hi+12(30)

Dans le cas d'un maillage uniforme hi=hi et l'équation devient :

ξi-1-2ξi+ξi+1h2=-f(xi)(31)

Il s'agit de l'équation qu'on obtient par discrétisation de la dérivée seconde (laplacien) par différence finie. Si le maillage n'est pas uniforme, l'équation de Poisson peut être discrétisée de la manière suivante :

2hi+hi+1(ξi+1-ξihi+1-ξi+1-ξihi)=-f(xi)(32)

ce qui conduit à l'équation (30). Pour un problème de Poisson unidimensionnel, la méthode des éléments finis conduit donc aux mêmes équations algébriques que la méthode des différences finies. Bien entendu, on ne peut pas en dire autant pour un problème à deux dimensions puisque dans ce cas le maillage est triangulaire et la méthode des différences finies est tout simplement impossible à mettre en œuvre avec un maillage triangulaire.

Pour résumer, écrivons le système linéaire sous forme matricielle : CX=D X est une matrice colonne contenant les ξi . La matrice carrée C est tridiagonale :

C00=1h1+K0(33) C01=-1h1(34) Cii = 1hi+1hi+1,i=1,N-1(35) Cii+1=-1hi+1,i=1,N-1(36) Cii-1=-1hi,i=1,N-1(37) CNN-1=-1hN(38) CNN=1hN-KL (39)

Voici les coefficients de la matrice colonne D :

D0 = f(x0)h12+K0u0-g0(40) Di = f(xi)hi+hi+12i=1,N-1(41) DN= f(xN)hN2-KLuL+gL (42)

2.d. Raffinement du maillage

Pour une fonction v de carré sommable sur l'intervale I=[0,L], on définit la norme L2(I) :

vL2(I) = 0Lv2(x)dx(43)

Pour une solution uh, la norme de la dérivée de l'erreur satisfait l'inégalité suivante [1] :

(u-uh)'L2(I)2Ci=1Nηi2(uh)(44)

ηi est le résidu sur l'intervalle Ii=[xi,xi+1] , défini par :

ηi(uh)=hif+u''hL2(Ii)=hifL2(Ii)=hixixi+1f2(x)dx(45)

Le raffinement du maillage consiste à réduire certains hi , ceux des sous-intervalles où la norme de f est grande, afin de réduire globalement l'erreur sans augmenter de manière excessive le temps de calcul. Ce résultat se comprend aisément : les régions où la norme de f est grande sont les régions où la courbure de u est grande, donc les régions où la largeur hi doit être la plus petite. Un maillage donné peut être raffiné au moyen de la stratégie suivante [1]. On fixe un paramètre α compris entre 0 et 1 puis on subdivise en deux parts égales tous les intervalles tels que :

ηi>αmaxηi(46)

La possibilité de réduire la taille des mailles à certains endroits seulement est une propriété intéressante de la méthode des éléments finis. Avec la méthode des différences finies, on peut seulement réduire la taille de toutes les mailles.

2.e. Résolution du système linéaire

Il existe une méthode directe très efficace pour résoudre un système tridiagonal (voir Équation de diffusion à une dimension) qui conviendrait tout à fait pour le problème de Poisson unidimensionnel. Cependant, le problème bidimensionnel ne conduit évidemment pas à une matrice tridiagonale. Nous optons donc pour la méthode plus générale consistant à résoudre le système par une méthode itérative.

3. Implémentation en Python

3.a. Fonction

Pour résoudre le système linéaire, nous utilisons la méthode itérative du gradient conjugué pour les matrices symétriques définies positives (fonction scipy.sparse.linalg.cg). La matrice D étant creuse, il est intéressant de la stocker sous la forme d'un type de matrice creuse de scipy.sparse (bien que cela ne s'impose pas pour un problème à une dimension). Nous optons pour le type Block Sparse Row, qui s'obtient à partir d'un tableau numpy.ndarray avec la fonction bsr_array.

import numpy as np
import scipy.sparse as sparse
import scipy.sparse.linalg as linalg
from scipy.linalg import solve

            

La fonction suivante effectue le calcul complet, pour des xi et une fonction f donnés, avec les paramètres définissant les conditions limites. Le tableau des ξi est renvoyé.

def poisson(f,x,K0,u0,g0,KL,uL,gL,solver="iterative"):
    # f : fonction
    # x : tableau des x_i
    N = len(x)-1
    h = x-np.roll(x,1)
    C = np.zeros((N+1,N+1),dtype=np.float64)
    C[0,0] = 1/h[1]+K0
    C[0,1] = -1/h[1]
    C[N,N-1] = -1/h[N]
    C[N,N] = 1/h[N]-KL
    for i in range(1,N):
        C[i,i] = 1/h[i]+1/h[i+1]
        C[i,i+1] = -1/h[i+1]
        C[i,i-1] = -1/h[i]
    D = np.zeros((N+1),dtype=np.float64)
    D[0] = f(x[0])*h[1]*0.5+K0*u0-g0
    D[N] = f(x[N])*h[N]*0.5-KL*uL+gL
    for i in range(1,N):
        D[i] = f(x[i])*(h[i]+h[i+1])*0.5
    if solver=="iterative":
        C = sparse.bsr_array(C) # facultatif
        xi, exitCode = linalg.cg(C, D, atol=1e-5)
        print("exitCode : ",exitCode)
    else:
        xi = solve(C,D)
    
    return xi
            

La fonction suivante effectue un raffinement du maillage :

def raffinement(f,x,alpha):
    N = len(x)-1
    eta = np.zeros(N)
    for i in range(N):
        h = x[i+1]-x[i]
        eta[i] = h*np.sqrt((f(x[i])**2+f(x[i+1])**2)*h/2)
    mx = np.max(eta)
    xx = []
    for i in range(N):
        xx.append(x[i])
        if eta[i] > alpha*mx:
            xx.append((x[i]+x[i+1])/2)
    xx.append(x[N])
    return np.array(xx)
            

3.b. Exemples

Pour tester la fonction, on considère le cas f(x)=1 sur l'intervalle [0,1], avec les conditions limites de Dirichlet u(0)=u(1)=0 , dont la solution exacte est :

u(x)=-12x2+12x(47)
x = np.linspace(0,1,300)
def f(x):
    return 1.0
K0 = KL = 1e3
xi = poisson(f,x,K0,0,0,KL,0,0)
def u(x):
    return -0.5*x*x+0.5*x
from matplotlib.pyplot import *
figure(figsize=(12,6))
plot(x,xi,"r")
plot(x,u(x),"k--")
grid()
xlabel("x")
ylabel("u")
                
fig1fig1.pdf

Avec la condition de Neumann u'(0)=0 et de Dirichlet u(1)=0 la solution est :

u(x)=-12x2+12(48)
x = np.linspace(0,1,300)
def f(x):
    return 1.0
K0 = 0
KL = 1e3
xi = poisson(f,x,K0,0,0,KL,0,0)
def u(x):
    return -0.5*x*x+0.5
figure(figsize=(12,6))
plot(x,xi,"r")
plot(x,u(x),"k--")
grid()
xlabel("x")
ylabel("u")
                
fig2fig2.pdf

Voici un autre exemple, correspondant à un problème thermique avec une source localisée au centre ou bien un problème d'électrostatique avec une charge au centre :

x = np.linspace(0,1,100)
def f(x):
    return np.exp(-200*(x-0.5)**2)
K0 = KL = 1e3
xi = poisson(f,x,K0,0,0,KL,0,0)
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig3fig3.pdf

Comme prévu, u(x) est une fonction affine dans les deux sous-intervalles où f(x)=0. Voici le même problème résolu avec un maillage moins fin :

x = np.linspace(0,1,20)
xi = poisson(f,x,K0,0,0,KL,0,0)
figure(figsize=(12,6))
plot(x,xi,"r.")
plot(x,xi,"r-")
grid()
xlabel("x")
ylabel("u")
                
fig4fig4.pdf

Le résultat est globalement bon mais la courbe manque de précision dans la zone centrale, là où la fonction f a des grandes valeurs. On procède à un double raffinement du maillage :

x = raffinement(f,x,0.9)
x = raffinement(f,x,0.9)
xi = poisson(f,x,K0,0,0,KL,0,0)
figure(figsize=(12,6))
plot(x,xi,"r.")
plot(x,xi,"r-")
grid()
xlabel("x")
ylabel("u")
                
fig5fig5.pdf

Voici un exemple qui permet de tester la condition limite de Neumann : la conduction dans un milieu sans source thermique avec un température nulle en 0 et un flux imposé en L :

x = np.linspace(0,1,300)
def f(x):
    return 0
xi = poisson(f,x,1e9,0,0,0,0,1)
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig6fig6.pdf

Voici une conduction sans source avec deux conditions de Dirichlet :

x = np.linspace(0,1,300)
def f(x):
    return 0
xi = poisson(f,x,1e3,0,0,1e3,1,0)
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig7fig7.pdf

Les conditions de Dirichlet sont obtenus avec des coefficients K0 et KL assez grands. Voici pourtant ce qu'il arrive si ces coefficients sont trop grands :

x = np.linspace(0,1,300)
def f(x):
    return 0
xi = poisson(f,x,1e6,0,0,1e6,1,0)
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig8fig8.pdf

Ce problème est lié à la méthode de résolution itérative puisque l'utilisation de la fonction scipy.linalg.solve (résolution par méthode directe) conduit à la bonne solution :

xi = poisson(f,x,1e6,0,0,1e6,1,0,solver="direct")
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig9fig9.pdf

3.c. Conditions limites de Dirichlet

Dans cette partie, on considère le cas où une condition de Dirichlet est appliquée en 0 et en L.

Dans le cas général de la condition de Robin, la condition limite de Dirichlet est introduite au moyen d'une valeur de K0 ou KL assez grande. Cependant, cette méthode pose problème (en tout cas pour la méthode itérative du gradient conjugué) car elle introduit des coefficients trop grands dans la matrice du système. Par ailleurs, le fait d'introduire commme a priori inconnues les valeurs ξ0 et ξN alors qu'elles sont connues est certainement une mauvaise idée lorsqu'une méthode de résolution itérative est utilisée. Il est donc préférable de définir un système d'équations pour les nœuds intérieurs seulement, c'est-à-dire en excluant les nœuds affectés par la condition de Dirichlet, les nœuds d'indices 0 et N.

La formulation variationnelle avec des conditions de Dirichlet consiste à considérer les fonctions test v dans l'ensemble des fonctions affines par morceaux (sur le maillage considéré) mais qui s'annulent en x=0 et en x=L. Cet ensemble est noté Vh,0. On a donc, pour toute fonction vVh,0 :

0Lv(x)f(x)dx=0Ldvdxduhdxdx(49)

La fonction recherchée uh peut être écrite comme la somme d'une fonction de Vh,0 et d'une fonction appartenant à l'ensemble des fonctions affines par morceaux qui vérifient les conditions de Dirichlet : uh,d(0)=u0 et uh,d(L)=uL, que l'on note Vh,d. On écrit donc :

uh=uh,0+uh,d(50)

La fonction uh,0 se décompose sur la base des fonctions chapeaux de la manière suivante :

uh,0(x)=j=1N-1ξvϕj(x)(51)

puisque les fonctions ϕ1,ϕN-1 constituent une base de Vh,0. La formulation variationnelle s'écrit donc :

0Lϕi(x)f(x)dx=0Ldϕidxduk,0dxdx+0Ldϕidxduk,ddxdx,i=1,N-1(52)

La fonction uh,0 obtenue est indépendante de la fonction hh,d [1]. On peut donc choisir une fonction particulière : celle qui vérifie les conditions de Dirichlet et qui est nulle sur tous les nœuds intérieurs. Cette fonction est représentée sur la figure suivante, avec les fonctions chapeaux qui interviennent dans l'intégrale ci-dessus :

affine2-fig.svgFigure pleine page

On a :

0Ldϕ1dxduk,ddxdx=-u0h11h1h1=-u0h1(53) 0LdϕN-1dxduk,ddxdx=-uLhN1hNhN=-uLhN(54) 0Ldϕidxduk,ddxdx = 0,i=2,N-2 (55)

On obtient finalement le système d'équations suivant, pour les inconnues ξ1,N-1 :

j=1N-1A1jξj=b1+u0h1(56) j=1N-1Ai,jξj=bi,i=2,N-2(57) j=1N-1AN-1jξj=b1+uLhN(58) (59)

Ce système est identique aux équations i=1,N-1 du système établi plus haut (Équations algébrique) avec ξ0=u0 et ξN=uL . Autrement dit, il s'agit du même système mais auquel on a enlevé les nœuds des bords dans les inconnues.

Les expressions de Ai,j et bi établies plus haut sont inchangées. Afin de pourvoir transcrire directemenrt ces relations dans le code Python (sans changer les indices), on commence par créer une matrice A de taille (N+1)x(N+1) puis on extrait de celle-ci la matrice de taille (N-1)x(N-1) qui correspond au système précédent. On procède de manière similaire pour la matrice colonne B, à laquelle il faut ajouter les deux termes correpondant aux conditions limites.

def poissonDirichlet(f,x,u0,uL,solver="iterative"):
    # f : fonction
    # x : tableau des x_i
    N = len(x)-1
    h = x-np.roll(x,1)
    A = np.zeros((N+1,N+1),dtype=np.float64) 
    for i in range(1,N):
        A[i,i] = 1/h[i]+1/h[i+1]
        A[i,i+1] = -1/h[i+1]
        A[i,i-1] = -1/h[i]
    B = np.zeros((N+1),dtype=np.float64)
    for i in range(1,N):
        B[i] = f(x[i])*(h[i]+h[i+1])*0.5
    # extraction des matrice pour les noeuds intérieurs
    A = A[1:N,1:N]
    B = B[1:N]
    B[0] += u0/h[1] # conditions limites de Dirichlet
    B[N-2] += uL/h[N]
    if solver=="iterative":
        A = sparse.bsr_array(A) # facultatif
        xi, exitCode = linalg.cg(A, B, atol=1e-5)
        print("exitCode : ",exitCode)
    else:
        xi = solve(A,B)
        # xi contient les noeuds intérieurs
    xi = np.concatenate((np.array([u0]),xi,np.array([uL])))
    return xi
            
x = np.linspace(0,1,300)
def f(x):
    return 0
xi = poissonDirichlet(f,x,0,1)
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig10fig10.pdf

3.d. Conditions limites de Neumann

Une conditon de Neumann sur un bord se traite sans difficulté avec des fonctions tests dans Vh, c'est-à-dire des fonctions affines par morceaux qui ne s'annulent pas nécessairement sur le bord correspondant. Les équations sont celles établies plus haut (Équations algébriques) avec K0=0 pour le bord x=0 ou bien KL=0 pour le bord x=L.

3.e. Cas général

Nous pouvons à présent traiter correctement les conditions limites dans tous les cas : Dirichlet, Neumann ou Robin. On reprend le système d'équations obtenu dans (Équations algébriques) mais en enlevant les équations correspondant aux bords auquel une condition de Dirichlet est appliquée. Il s'agit d'enlever soit la première équation, soit la dernère, soit les deux.

def poissonGeneral(f,x,type0,K0,u0,g0,typeL,KL,uL,gL,solver="iterative"):
    # f : fonction
    # x : tableau des x_i
    # type0 et typeL : "dir","neu" ou "rob"
    if type0=="neu": K0 = 0
    if typeL=="neu": KL = 0
    N = len(x)-1
    h = x-np.roll(x,1)
    C = np.zeros((N+1,N+1),dtype=np.float64)
    C[0,0] = 1/h[1]+K0
    C[0,1] = -1/h[1]
    C[N,N-1] = -1/h[N]
    C[N,N] = 1/h[N]-KL
    for i in range(1,N):
        C[i,i] = 1/h[i]+1/h[i+1]
        C[i,i+1] = -1/h[i+1]
        C[i,i-1] = -1/h[i]
    D = np.zeros((N+1),dtype=np.float64)
    D[0] = f(x[0])*h[1]*0.5+K0*u0-g0
    D[N] = f(x[N])*h[N]*0.5-KL*uL+gL
    for i in range(1,N):
        D[i] = f(x[i])*(h[i]+h[i+1])*0.5
    if type0=="dir" and typeL=="dir":
        C = C[1:N,1:N]
        D = D[1:N]
        D[0] += u0/h[1] # conditions limites de Dirichlet
        D[N-2] += uL/h[N]
    elif type0=="dir" and typeL!="dir":
        C = C[1:N+1,1:N+1]
        D = D[1:N+1]
        D[0] += u0/h[1]
    elif type0!="dir" and typeL=="dir":
        C = C[0:N,0:N]
        D = D[0:N]
        D[N-1] += uL/h[N]
    if solver=="iterative":
        C = sparse.bsr_array(C) # facultatif
        xi, exitCode = linalg.cg(C, D, atol=1e-5)
        print("exitCode : ",exitCode)
    else:
        xi = solve(C,D)
    if type0=="dir" and typeL=="dir":
        xi = np.concatenate((np.array([u0]),xi,np.array([uL])))
    elif type0=="dir" and typeL!="dir":
        xi = np.concatenate((np.array([u0]),xi))
    elif type0!="dir" and typeL=="dir":
        xi = np.concatenate((xi,np.array([uL])))
    return xi
            

Voici une reprise d'exemples traités plus haut :

x = np.linspace(0,1,300)
def f(x):
    return 1.0
g0 = 0 # neumann en x=0
uL = 0 # dirichlet en x=L
xi = poissonGeneral(f,x,"neu",0,0,g0,"dir",0,uL,0)
def u(x):
    return -0.5*x*x+0.5
figure(figsize=(12,6))
plot(x,xi,"r")
plot(x,u(x),"k--")
grid()
xlabel("x")
ylabel("u")
                
fig11fig11.pdf
x = np.linspace(0,1,300)
def f(x):
    return 0.0
u0 = 0 #dirichlet en x=0
uL = 1 # dirichlet en x=L
xi = poissonGeneral(f,x,"dir",0,u0,0,"dir",0,uL,0)
figure(figsize=(12,6))
plot(x,xi,"r")
grid()
xlabel("x")
ylabel("u")
                
fig12fig12.pdf

4. Changement de milieu

4.a. Définition

Dans un problème de transfert thermique en régime stationnaire, il arrive qu'une frontière sépare deux milieux de conductivités thermiques différentes. L'équation de diffusion thermique dans un domaine où la conductivité thermique λ n'est pas uniforme s'écrit :

dd x(λdTdx)=-p(60)

p est la densité volumique de puissance des sources thermiques.

Dans un problème d'électrostatique, si la permittivité électrique ε n'est pas uniforme, l'équation pour le potentiel s'écrit :

dd x(εdVdx)=-ρ(61)

ρ est la densité volumique de charge.

Considérons une frontière entre deux domaines dont les conductivités thermiques (où les permittivités électriques) sont différentes mais uniforme dans chacun. La dérivée de la conductivité n'est pas déifnie sur la frontière. La manière classique de traiter ce cas est de résoudre l'équation de Poisson dans les deux domaines avec une condition limite qui traduit la conservation du flux :

λ1dTdx(x0-)=λ2dTdx(x0+)(62)

pour une frontière située en x=x0 . Cette équation est appelée relation de passage entre les deux milieux. En électrostatique, une relation de passage similaire provient de la continuité de la composante normale de Dx=εEx.

L'utilisation directe de la relation de passage se fait sans difficulté dans la méthode des différences finis (ou des volumes fini) : voir Équation de Poisson à deux dimensions.

Pour la méthode des éléments finis, il faut considérer l'équation de Poisson sous sa forme plus générale avec une propriété du milieu a(x) :

ddx(a(x)dudx)=-s(x)(63)

Lorsque le milieu est homogène, on a f(x)=s(x)/a .

4.b. Formulation variationnelle

Pour une fonction test v(x), on considère l'intégrale suivante :

0Lddx(v(x)a(x)dudx)dx=v(L)a(L)dudx(L)-v(0)a(0)dudx(0)(64)

Cette intégrale s'écrit aussi :

0Lddx(v(x)a(x)dudx)dx= 0Lv(x)ddx(a(x)dudx)dx+0Ldvdxa(x)dudxdx=-0Lv(x)s(x)+0Ldvdxa(x)dudxdx(65)

Au moyen de la condition limite de Robin (relation (2)), on obtient l'égalité suivante :

0Lv(x)s(x)dx=0Ldvdxa(x)dudxdx+v(0)a(0)(K0(u(0)-u0)+g0)-v(L)a(L)(KL(u(L)-uL)+gL)(66)

La formulation variationnelle pour un fonction uh et pour toute fonction v de Vh s'écrit :

0Lϕi(x)s(x)dx=0Ldϕidxa(x)duhdxdx+ϕi(0)a(0)(K0(uh(0)-u0)+g0)-ϕi(L)a(L)(KL(uh(L)-uL)+gL),i=0,1N(67)

4.c. Équations algébriques

Le report de l'expression (11) dans l'égalité (12) conduit à :

0Lϕi(x)s(x)dx=j=0Nξj0Ldϕidxa(xj)dϕjdxdx +ϕi(0)a(0)(K0(ξ0-u0)+g0)-ϕi(L)a(L)(KL(ξN-uL)+gL),i=0,1N(68)

Nous pouvons ainsi traiter le cas général d'une propriété a(x) (conductivité ou permittivité) qui dépend de x. Le traitement du cas où a(x) présente une disconinuité en x=x0 se fait sans problème à condition qu'il y ait un nœud en x=x0. D'une manière générale, le maillage doit comporter des nœuds sur les frontières de discontinuité des propriétés (qui sont les frontières des différentes objets du problème).

Pour obtenir les matrices du système linéaire, il suffit de remplacer f(x) par s(x) et de multiplier par aj=a(xj-1+xj2) le terme d'intégrale sur Ii dans l'expression de Aij. On obtient :

C00=a1h1+K0a1(69) C01=-a1h1(70) Cii = aihi+ai+1hi+1,i=1,N-1(71) Cii+1=-ai+1hi+1,i=1,N-1(72) Cii-1=-aihi,i=1,N-1(73) CNN-1=-aNhN(74) CNN=aNhN-KLaN (75) D0 = s(x0)h12+K0a1u0-a1g0(76) Di = s(xi)hi+hi+12i=1,N-1(77) DN= s(xN)hN2-KLaNuL+aNgL (78)

Le cas de la condition limite de Dirichlet sur un des bords se traite, comme expliqué plus haut (Conditions limites de Dirichlet), en enlevant du système d'équations le nœud correrspondant.

4.d. Implémentation

On fournit à la fonction de calcul le tableau des ai, qui sont les valeurs de a aux milieux des sous-intervalles. Il y a N sous-intervalles. Il est convenu que le tableau contient N+1 élement (comme le tableau des xi) mais que le premier élément, d'indice 0, n'est pas utilisé. Le coefficient ai est ainsi l'élément d'indice i de ce tableau.

def poissonGeneral2(s,a,x,type0,K0,u0,g0,typeL,KL,uL,gL,solver="iterative"):
    # s : fonction
    # a : tableau des a_i
    # x : tableau des x_i
    # type0 et typeL : "dir","neu" ou "rob"
    if type0=="neu": K0 = 0
    if typeL=="neu": KL = 0
    N = len(x)-1
    h = x-np.roll(x,1)
    C = np.zeros((N+1,N+1),dtype=np.float64)
    C[0,0] = a[1]/h[1]+K0*a[1]
    C[0,1] = -a[1]/h[1]
    C[N,N-1] = -a[N]/h[N]
    C[N,N] = a[N]/h[N]-KL*a[N]
    for i in range(1,N):
        C[i,i] = a[i]/h[i]+a[i+1]/h[i+1]
        C[i,i+1] = -a[i+1]/h[i+1]
        C[i,i-1] = -a[i]/h[i]
    D = np.zeros((N+1),dtype=np.float64)
    D[0] = s(x[0])*h[1]*0.5+(K0*u0-g0)*a[1]
    D[N] = f(x[N])*h[N]*0.5+(gL-KL*uL)*a[N]
    for i in range(1,N):
        D[i] = s(x[i])*(h[i]+h[i+1])*0.5
    if type0=="dir" and typeL=="dir":
        C = C[1:N,1:N]
        D = D[1:N]
        D[0] += u0/h[1]*a[1] # conditions limites de Dirichlet
        D[N-2] += uL/h[N]*a[N]
    elif type0=="dir" and typeL!="dir":
        C = C[1:N+1,1:N+1]
        D = D[1:N+1]
        D[0] += u0/h[1]*a[1]
    elif type0!="dir" and typeL=="dir":
        C = C[0:N,0:N]
        D = D[0:N]
        D[N-1] += uL/h[N]*a[N]
    if solver=="iterative":
        C = sparse.bsr_array(C) # facultatif
        xi, exitCode = linalg.cg(C, D, atol=1e-5)
        print("exitCode : ",exitCode)
    else:
        xi = solve(C,D)
    if type0=="dir" and typeL=="dir":
        xi = np.concatenate((np.array([u0]),xi,np.array([uL])))
    elif type0=="dir" and typeL!="dir":
        xi = np.concatenate((np.array([u0]),xi))
    elif type0!="dir" and typeL=="dir":
        xi = np.concatenate((xi,np.array([uL])))
    return xi
            

4.e. Exemples

N = 300
x = np.linspace(0,1,N+1)
a = np.zeros(N+1)
a[1:N//2] = 1
a[N//2:N+1] = 2
def s(x):
    return 0
u0 = 0
uL = 1
xi = poissonGeneral2(s,a,x,"dir",0,u0,0,"dir",0,uL,0)
figure(figsize=(12,6))
plot(x,xi,"r-")
grid()
xlabel("x")
ylabel("u")
            
fig13fig13.pdf
u0 = 0
gL = 1
xi = poissonGeneral2(s,a,x,"dir",0,u0,0,"neu",0,0,gL)
figure(figsize=(12,6))
plot(x,xi,"r-")
grid()
xlabel("x")
ylabel("u")
            
fig14fig14.pdf
Creative Commons LicenseTextes et figures sont mis à disposition sous contrat Creative Commons.
Références
[1]  M.G. Larson, F. Bengzon,  The finite element method,  (Springer-Verlag, 2013)
[2]  G. Allaire,  Analyse numérique et optimisation,  (Edition de l'école polytechnique, 2006)