Ce document traite de la résolution de l'équation de Poisson bidimensionnelle par la méthode des éléments finis. La page Équation de Poisson 1D : méthode des éléments finis développe le cas unidimensionel.
Soit un domaine de de frontière . Soient deux fonctions et . On recherche une fonction deux fois dérivable vérifiant l'équation différentielle
Par exemple dans un problème de diffusion thermique, u désigne la température, a est la conductivité thermique et s est la source thermique. Dans un problème d'électrostatique u est le potentiel, a la permittivité électrique et s la densité volumique de charge. Cette équation aux dérivées partielles est plus générale que l'équation de Poisson mais se ramène à celle-ci dans un milieu homogène, c'est-à-dire lorsque a est uniforme :
On se place dans le cas de coordonnées cartésiennes, qui correspond à un problème invariant par translation dans la direction Z. On a donc :
Pour que l'équation admette une unique solution, il faut définir des conditions limites sur .
Pour une fonction de classe C1, on a :
où désigne le vecteur unitaire orthogonal à et dirigé dans le sens sortant par rapport à . La même égalité est évidemment valable pour la coordonnée y. Appliquons ce théorème à :
On obtient :
et de même :
La somme de ces deux égalités conduit à :
qui devient, en utilisant l'équation :
Cette égalité est écrite dans le cas bidimensionnel, où est une surface plane et le contour de cette surface. est donc l'élément de surface et l'élément du contour. Le même résultat est bien sûr valable en tridimensionnel ( est alors un volume et la surface qui le délimite).
Soit V l'ensemble des fonctions de carré sommable et dont le gradient est aussi de carré sommable sur .
La formulation variationnelle du problème (la recherche de u) s'énonce ainsi [1] : on recherche une fonction telle que pour toute fonction on ait l'égalité . Cet énoncé est souvent appelé forme faible du problème de Poisson car les exigences sur la fonction u sont moindres que pour l'équation (elle n'est plus nécessairement deux fois dérivable).
Séparons la frontière du domaine en deux parties : la partie sur laquelle une condition limite de Dirichlet est imposée, la partie sur laquelle une condition limite de Neumann est imposée. L'égalité s'écrit :
Comme il est montré dans Équation de Poisson 1D : méthode des éléments finis, la formulation variationnelle doit être modifiée en considérant les fonctions de V qui s'annulent sur . Notons V0 l'ensemble de ces fonctions. La formulation variationnelle devient : obtenir une fonction u telle que pour toute fonction :
La condition de Neumann consiste à fixer la valeur de sur . Une variante de la condition de Neumann est la condition de Robin, qui consiste à écrire sur la frontière :
où K0, u0 et g0 sont des constantes. Pour K0=0, on retrouve la condition de Neumann avec g0 la composante normale du gradient sur la frontière. Plus généralement, ces trois constantes peuvent dépendre du point de la frontière (auquel cas il ne s'agit plus de constantes). Dans ce document, on nommera condition de Neumann la condition définie par l'équation .
Finalement, l'égalité de la formulation variationnelle s'écrit :
Le domaine est subdivisé en triangles : chaque maille est un triangle et comporte donc trois nœuds. Une maille partage avec une maille voisine deux nœuds. La figure suivante montre un maillage triangulaire très simple pour un domaine rectangulaire (celui que nous utiliserons pour les tests) :
Figure pleine pageLe maillage comporte P nœuds. Le nœud Ni a pour coordonnées (xi,yi). En général, les nœuds ne sont évidemment pas disposés sur une grille.
Les fonctions u et v du problème variationnel sont considérées dans l'ensemble, noté Vh, des fonctions continues et affines par morceaux sur le maillage (l'indice h désigne le maillage). Pour on a sur chaque maille : . Si les mailles sont assez petites, les valeurs de uh aux nœuds sont censées être de bonnes approximations de u, la solution de l'équation avec les conditions limites. La fonction uh est une solution du problème variationnel mais elle n'est évidemment pas une solution de .
Si l'on note et les coefficients de uh pour deux mailles voisines, on a sur le côté commun à ces deux mailles, par continuité :
ce qui est l'équation de la droite passant par les deux nœuds communs. On a bien évidemment :
où sont les coefficients d'un des trois triangles qui partagent le nœud Ni.
On définit les fonctions affines par morceaux suivantes, pour :
Autrement dit, la fonction vaut 1 sur le nœud Ni et 0 sur tous les autres nœuds. Ces fonctions constituent une base Vh. Pour toute fonction , on a en effet :
Le support de est constitué des triangles qui ont le nœud Ni en commun. La figure suivante montre le support de et de :
Figure pleine pagePour un noeud Ni, la fonction s'annule sur le côté opposé de chaque triangle auxquels ce nœud appartient.
Considérons un nœud Ni et ses nœuds voisins. Le support de est constitué des triangles définis avec le nœud Ni et ces voisins. La figure suivante montre ces triangles :
Figure pleine pageConsidérons un de ces triangles, dont les sommets sont notés 1,2,3, le sommet 1 étant le nœud Ni. Les coefficients de sur ce triangle sont solutions des trois équations suivantes :
Si detT désigne le déterminant de ce système, il est facile de démontrer que, si les sommets N1,N2,N3 sont ordonnés dans le sens direct, l'aire du triangle s'écrit :
Si les trois points ne sont pas alignés, ce système a un déterminant non nul. L'unique solulion peut être obtenue sans difficulté avec les règles de Cramer :
Soit Vh,0 l'ensemble des fonctions affines par morceaux sur le maillage et qui s'annulent sur les nœuds qui appartiennent à la frontière (la partie de la frontière où une condition de Dirichlet est appliquée). Notons les indices de tous les nœuds à l'exception de ces nœuds (indices des nœuds intérieurs). Pour toute fonction on a :
La formulation variationnelle s'énonce comme suit. Trouver une fonction telle que pour toute fonction on ait :
Un énoncé équivalent est : trouver une fonction telle que :
Introduisons les composantes de uh sur la base :
Les sont les valeurs de uh aux nœuds, autrement dit les approximations de la solution u. Le report de cette décomposition dans conduit à un système d'équations linéaires pour les composantes :
Ce système comporte plus d'inconnues que d'équations : le nombre d'équations est égal aux nombres de nœuds à l'exception de ceux de la frontière où une condition de Dirichlet est appliquée alors que le nombre d'inconnues est égal aux nombre total de nœuds. Il faut donc enlever du système les des nœuds qui sont sur . Pour cela, on écrit uh comme la somme d'une fonction de Vh,0 (qui s'annule sur la frontière de Dirichlet) et d'une fonction de Vh,d, l'ensemble des fonctions affines par morceaux qui vérifient précisément la condition de Dirichlet choisie :
Il peut être démontré que uh est indépendant du choix de uh,d [1]. On choisit donc la fonction la plus simple : celle qui s'annule sur tous les nœuds à l'exception de ceux où la condition de Dirichlet est appliquée. Notons Nd les indices des nœuds où une condition de Dirichlet est appliquée. On a :
Les dans la seconde somme sont connues puisqu'il s'agit des valeurs de u sur les nœuds appartenant à la frontière où une condition de Dirichlet est appliquée.
On obtient un système d'équations pour les inconnues :
Réécrivons ces équations en mettant à gauche la combinaison linéaire des inconnues :
Posons :
Le système d'équations linéaires s'écrit :
La matrice du système d'équations est M=A+R. La matrice A est indépendante des conditions limites. La matrice R est non nulle seulement si une condition de Robin () est appliquée sur une partie de la frontière .
Pour , le coefficient Aij est non nul si les nœuds i et j ont une partie de leur support en commun, autrement dit si ce sont deux nœuds voisins (rappelons que les nœuds où une condition de Dirichlet est appliquée sont exclus). Pour évaluer approximativement l'intégrale sur un triangle T dont les sommets sont N1,N2,N3, on utilise la formule de quadrature du barycentre :
où ST est l'aire du triangle. Considérons , l'intégrale sur un des deux triangles dont un côté est constitué des nœuds i et j, le troisième étant noté Nk. Soient les coefficients qui définissent sur ce triangle et les coefficients qui définissent sur le même triangle.
Figure pleine pageOn a :
La formule du barycentre donne :
En pratique, il sera plus simple d'attribuer une valeur de a à chaque nœud et on écrira donc :
Il faut aussi déterminer le coefficient sur la diagonale :
Le support de la fonction à intégrer est constitué des triangles auquel le nœud Ni appartient. Pour un de ces triangles, l'intégrale vaut approximativement :
Le remplissage de la matrice A se fait en parcourant tous les triangles du maillage. Pour chaque triangle :
Pour , Rij est non nul si les deux nœuds Ni et Nj sont voisins et s'ils appartiennent à une frontière où une condition de Robin est appliquée.
Figure pleine pageLe produit est non nul sur le triangle constitué de ces deux nœuds et du nœud Nk. Rij est l'intégrale sur le segment NiNj de . Les coefficients a et K0 seront dans la plupart des cas constants sur le segment ou peuvent être considérés comme tel. Si est l'abscisse sur le segment, on a :
où dij est la distance entre les deux nœuds. On a donc :
aK0 est la moyenne des valeurs sur le côté. Une valeur de K0 est attribuée au côté et la valeur moyenne de a est évaluée à partir des valeurs sur les deux nœuds. On a donc :
Il faut aussi déterminer le coefficient sur la diagonale :
La fonction à intégrer est non nulle sur les deux segments (notés 1 et 2) de la frontière auquel le nœud Ni appartient :
Figure pleine pageNotons a1,K1 et a2,K2 les valeurs des coefficients aux milieux des segments 1 et 2. On a :
Pour un triangle donné, le terme à considérer est une intégrale sur le côté du triangle situé sur la frontière :
Le remplissage de la matrice R se fait en parcourant tous les triangles du maillage. Pour chaque triangle :
Les matrices A et R sont symétriques. La ligne i de la matrice A comporte un coefficient diagonal et des coefficients non diagonaux correspondant aux nœuds voisins du nœud Ni. La ligne i de la matrice R comporte un coefficient diagonal et des coefficients non diagonaux correspondant aux nœuds voisins de Ni situés sur le bord .
Rappelons que tous les nœuds qui interviennent dans le calcul de ces deux matrices sont des nœuds intérieurs, c'est-à-dire qu'il faut exclure les nœuds où une condition de Dirichlet et appliquée.
Il faut expliciter les coefficients de la matrice colonne B. Le premier terme de Bi est :
Pour (nœud n'appartenant pas à ), il faut considérer les nœuds Nk appartenant à tels que le support de ait une partie commune avec celui de . est non nul si et seulement si Ni est voisin d'un nœud de la frontière de Dirichlet. La figure suivante montre un nœud de ce type et les triangles qui constituent la partie commune des supports.
Figure pleine pagePour chaque triangle du maillage, on recherche les paires de nœuds contituées d'un nœud Ni n'appartenant pas à et d'un nœud Nk appartenant à . La contribution de cette paire à B1,i est :
Le deuxième terme de Bi est :
Il s'agit du terme qui contient la source de l'équation de Poisson. Pour , il faut évaluer l'intégrale sur le support de , qui est constitué des triangles auxquels ce nœud appartient. Pour un de ces triangles, dont les sommets sont N1,N2,N3, la formule de quadrature du barycentre donne :
La fonction vaut 1/3 au barycentre. Pour la source, on considère plutôt la moyenne des valeurs aux sommets du triangle :
Pour chaque triangle du maillage et pour chaque nœuds du triangle (d'indice i) n'appartenant pas à , on ajoute B2,i à Bi.
Le troisième terme de Bi est :
Ce coefficient peut être non nul s'il existe un nœud Nk de la frontière de Dirichlet tel que soit non nul sur au moins un segment de la frontière de Neumann. Cela ne peut se rencontrer qu'à la jonction entre ces deux frontières, comme le montre la figure suivante :
Figure pleine pageL'intégrale se fait sur le segment NkNi. Le calcul est similaire à celui du coefficient Rij. On obtient :
où d est la distance NkNi.
Le quatrième terme de Bi est :
Ce terme contient la condition de Neumann (gradient g0) et le terme constant de la condition de Robin. Il est non nul si et seulement si le nœud Ni appartient à la frontière . Pour chaque triangle du maillage et pour chaque paire de nœuds de ce triangle appartenant à , la contribution de cette paire est, pour chacun des deux nœuds :
où est la composante normale du gradient sur la frontière définie par les nœuds Ni et son voisin Nj, et les coefficients relatifs à la condition de Robin sur la même frontière.
Chaque nœud Ni est un objet qui contient les informations suivantes :
Un côté de triangle défini par un nœud de Dirichlet et un nœud de Neumann peut se voir affecté une condition de Neumann (s'il est sur ). Celle-ci est définie dans le triangle correspondant.
import numpy as np
import scipy.sparse as sparse
import scipy.sparse.linalg as linalg
from scipy.linalg import solve
DIR = 1
NEU = 2
class Noeud:
def __init__(self,x,y,a,s,lim=0,ksi=0):
self.xy = np.array([x,y])
self.x = x
self.y = y
self.a = a
self.s = s
self.lim = lim
self.ksi = ksi
self.indice = 0 # indice dans le maillage
Chaque triangle est défini par trois nœuds. Les arguments du constructeur sont :
Dans le constructeur, l'aire du triangle et les coefficients des fonctions pour les trois sommets sont calculés.
class Triangle:
def __init__(self,n1,n2,n3,bords=[False,False,False],g=[0,0,0],K0=[0,0,0],u0=[0,0,0]):
self.noeud = [n1,n2,n3]
self.detT = n2.x*n3.y-n2.x*n1.y-n1.x*n3.y-n2.y*n3.x+n2.y*n1.x+n1.y*n3.x
self.ST = 0.5*abs(self.detT)
if self.detT<0:
(n2,n3) = (n3,n2)
self.noeud = [n1,n2,n3]
self.detT = n2.x*n3.y-n2.x*n1.y-n1.x*n3.y-n2.y*n3.x+n2.y*n1.x+n1.y*n3.x
if self.detT ==0 :
print("Erreur de définition du triangle")
print(n1.xy)
print(n2.xy)
print(n3.xy)
self.phi = []
self.phi.append(self.fonctionPhi(n1,n2,n3))
self.phi.append(self.fonctionPhi(n2,n3,n1))
self.phi.append(self.fonctionPhi(n3,n1,n2))
self.a = (n1.a+n2.a+n3.a)/3
self.s = (n1.s+n2.s+n3.s)/3
self.paires = [(0,1,self.distance(n1,n2),bords[0],g[0],K0[0],u0[0]),(0,2,self.distance(n1,n3),bords[2],g[2],K0[2],u0[2]),(1,2,self.distance(n2,n3),bords[1],g[1],K0[1],u0[1])]
self.xc = (n1.x+n2.x+n3.x)/3
self.yc = (n1.y+n2.y+n3.y)/3
def distance(self,ni,nj):
return np.sqrt((ni.x-nj.x)**2+(ni.y-nj.y)**2)
def fonctionPhi(self,n1,n2,n3):
# fonction Phi relative au sommet n1
c0 = (n2.y-n3.y)/self.detT
c1 = (n3.x-n2.x)/self.detT
c2 = (n2.x*n3.y-n3.x*n2.y)/self.detT
return [c0,c1,c2]
def interpol(self,x,y):
phi1 = self.phi[0]
phi2 = self.phi[1]
phi3 = self.phi[2]
u1 = self.noeud[0].ksi*(phi1[0]*x+phi1[1]*y+phi1[2])
u2 = self.noeud[1].ksi*(phi2[0]*x+phi2[1]*y+phi2[2])
u3 = self.noeud[2].ksi*(phi3[0]*x+phi3[1]*y+phi3[2])
return u1+u2+u3
def cotePoint(self,i,j,x,y):
# détermine de quel côté d'un côté le point P se trouve
xi = self.noeud[i].x
yi = self.noeud[i].y
xj = self.noeud[j].x
yj = self.noeud[j].y
return (y-yi)*(xj-xi)-(x-xi)*(yj-yi)
def contientPoint(self,x,y):
# détermine si un point est dans le triangle
return self.cotePoint(0,1,x,y)>=0 and self.cotePoint(1,2,x,y)>=0 and self.cotePoint(2,0,x,y)>=0
def rectangle(self):
#rectangle minimal contenant le triangle
n1 = self.noeud[0]
n2 = self.noeud[1]
n3 = self.noeud[2]
xmin = min(n1.x,n2.x,n3.x)
xmax = max(n1.x,n2.x,n3.x)
ymin = min(n1.y,n2.y,n3.y)
ymax = max(n1.y,n2.y,n3.y)
return [xmin,xmax,ymin,ymax]
def matriceA(self,M):
for i in range(3):
ni = self.noeud[i]
if ni.lim!=DIR:
phi = self.phi[i]
M[ni.indice,ni.indice] += self.a*(phi[0]*phi[0]+phi[1]*phi[1])*self.ST
for p in self.paires:
(i,j,d,b,g,K0,u0) = p
ni = self.noeud[i]
nj = self.noeud[j]
if ni.lim!=DIR and nj.lim!=DIR:
phi_i = self.phi[i]
phi_j = self.phi[j]
A = self.a*(phi_i[0]*phi_j[0]+phi_i[1]*phi_j[1])*self.ST
M[ni.indice,nj.indice] += A
M[nj.indice,ni.indice] += A
def matriceR(self,M):
for p in self.paires:
(i,j,d,b,g,K0,u0) = p
ni = self.noeud[i]
nj = self.noeud[j]
if ni.lim==NEU and nj.lim==NEU and b:
mii = -(ni.a+nj.a)*K0*d/6
M[ni.indice,ni.indice] += mii
M[nj.indice,nj.indice] += mii
mij = -1/6*(ni.a+nj.a)*K0/2*d
M[ni.indice,nj.indice] += mij
M[nj.indice,ni.indice] += mij
def matriceB1(self,B):
for p in self.paires:
(i,j,d,b,g,K0,u0) = p
ni = self.noeud[i]
nj = self.noeud[j]
phi_i = self.phi[i]
phi_j = self.phi[j]
if ni.lim==DIR and nj.lim!=DIR:
B[nj.indice] += -ni.ksi*self.a*(phi_i[0]*phi_j[0]+phi_i[1]*phi_j[1])*self.ST
if ni.lim!=DIR and nj.lim==DIR:
B[ni.indice] += -nj.ksi*self.a*(phi_i[0]*phi_j[0]+phi_i[1]*phi_j[1])*self.ST
def matriceB2(self,B):
for i in range(3):
ni = self.noeud[i]
if ni.lim!=DIR:
B[ni.indice] += self.s*self.ST/3
def matriceB3(self,B):
for p in self.paires:
(i,j,d,b,g,K0,u0) = p
ni = self.noeud[i]
nj = self.noeud[j]
if ni.lim==NEU and nj.lim==DIR and b:
B[ni.indice] += nj.ksi*(ni.a+nj.a)*K0*d/12
if ni.lim==DIR and nj.lim==NEU and b:
B[nj.indice] += ni.ksi*(ni.a+nj.a)*K0*d/12
def matriceB4(self,B):
for p in self.paires:
(i,j,d,b,g,K0,u0) = p
ni = self.noeud[i]
nj = self.noeud[j]
if ni.lim==NEU and nj.lim==NEU and b:
B4 = 0.5*(ni.a+nj.a)*(g-K0*u0)*0.5*d
B[ni.indice] += B4
B[nj.indice] += B4
def completerMatrices(self,M,B):
self.matriceA(M)
self.matriceR(M)
self.matriceB1(B)
self.matriceB2(B)
self.matriceB3(B)
self.matriceB4(B)
Les fonctions matriceA et matriceR complètent la matrice M (par addition). Les fonctions matriceB1,matriceB2,matriceB3,matriceB4 complètent la matrice B. La fonction completerMatrices permet de générer la contibution d'un triangle aux deux matrices.
La classe ElementsFinis2D permet d'effectuer la résolution. Son constructeur prend en argument la liste des nœuds et la liste des triangles. La fonction solve construit les matrices par bouclage sur les triangles puis effectue la résolution du système, par défaut par la méthode itérative du gradient conjugué. Les valeurs de obtenues sont affectées aux nœuds.
class ElementsFinis2D:
def __init__(self,noeuds,triangles):
self.triangles = triangles
self.noeuds = []
self.noeuds_dirichlet = []
indice = 0
indice_dirichlet = 0
for n in noeuds:
if n.lim!=DIR:
n.indice = indice
self.noeuds.append(n)
indice += 1
else:
n.indice = indice_dirichlet
self.noeuds_dirichlet.append(n)
indice_dirichlet += 1
def creerMatrices(self):
self.Nnoeuds = len(self.noeuds)
self.M = np.zeros((self.Nnoeuds,self.Nnoeuds),dtype=np.float64)
self.B = np.zeros(self.Nnoeuds,dtype=np.float64)
def solve(self,solver="iterative"):
self.creerMatrices()
for t in self.triangles:
t.completerMatrices(self.M,self.B)
if solver=="iterative":
M = sparse.bsr_array(self.M)
ksi,exitCode = linalg.cg(M, self.B, atol=1e-5)
print("exitCode : ",exitCode)
else:
ksi = solve(self.M,self.B)
for i in range(self.Nnoeuds):
self.noeuds[i].ksi = ksi[i]
On considère le domaine carré . La source est nulle (s=0) et le coefficient a est uniforme (le même partout). En x=0 et x=1 on applique des conditions limites de Dirichlet, avec respectivement les valeurs 0 et 1. En y=0 et y=1 on applique des conditions de Neumann avec g0=0. La solution est : u(x,y)=x (il s'agit en fait d'un problème unidimensionnel).
La structure du maillage est montré sur la figure suivante :
Figure pleine pageLes 4 nœuds des coins sont affectés de la condition de Dirichlet. Chaque nœud non situé sur un bord a 6 nœuds voisins.
On commence par définir les noeuds :
N = 50
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 0
for j in range(N):
for i in range(N):
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=1)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
noeuds.append(n)
puis les triangles :
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
if i==N-2 :
bords=[False,True,False]
g = [0,0,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g))
bords = [False,False,False]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g))
Pour le maillage considéré, il est aisé de tracer u(x,y) car les nœuds sont disposés sur une grille. Dans le cas général, il faudra procéder à une interpolation dans les triangles (par la fonction affine par morceaux) pour obtenir les valeurs de u sur une grille.
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig1.pdf
figure()
plot(x,U[N//2,:])
grid()
fig2.pdf
La même solution peut être obtenue en remplaçant la condition de Dirichlet en x=1 par la condition de Neumann u'(1)=1 :
N = 51
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 0
for j in range(N):
for i in range(N):
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
noeuds.append(n)
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
if i==N-2 :
bords=[False,True,False]
g = [0,1,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,1,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g))
bords = [False,False,False]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g))
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig3.pdf
figure()
plot(x,U[N//2,:])
grid()
fig4.pdf
On reprend l'exemple A avec une source uniforme et des conditions de Dirichlet nulle en x=0 et x=1. La solution est .
N = 50
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 1
for j in range(N):
for i in range(N):
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
noeuds.append(n)
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
if i==N-2 :
bords=[False,True,False]
g = [0,0,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g))
bords = [False,False,False]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g))
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig5.pdf
figure()
plot(x,U[N//2,:])
plot(x,s/2*(x-x*x),"k--")
grid()
fig6.pdf
On reprend l'exemple A avec une source uniforme et des conditions de Dirichlet nulle sur les quatre bords :
N = 50
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 1
for j in range(N):
for i in range(N):
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
noeuds.append(n)
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
if i==N-2 :
bords=[False,True,False]
g = [0,0,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g))
bords = [False,False,False]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g))
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig7.pdf
figure()
plot(x,U[N//2,:])
grid()
fig8.pdf
On reprend l'exemple A avec deux milieux différents : la moitié à droite du domaine a une valeur de a deux fois plus grande.
N = 50
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 0
a1 = 1
a2 = 2
for j in range(N):
for i in range(N):
if i < N//2 : a=a1
else : a=a2
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=1)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
noeuds.append(n)
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
if i==N-2 :
bords=[False,True,False]
g = [0,0,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g))
bords = [False,False,False]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g))
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig9.pdf
figure()
plot(x,U[N//2,:])
grid()
fig10.pdf
Un objet de forme carrée est introduit dans le domaine, avec une condition de Dirichlet sur ses bords. Les quatres bords du domaine ont une condition de Dirichlet. La figure suivante montre comment l'objet est défini :
Figure pleine page
N = 50
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 0
# bords de l'objet
e = 5
i1 = N//2-e
i2 = N//2+e
j1 = N//2-e
j2 = N//2+e
for j in range(N):
for i in range(N):
if (i==i1 and j1<=j<=j2) or (i==i2 and j1<=j<=j2) or (j==j1 and i1<=i<=i2) or (j==j2 and i1<=i<=i2):
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=1)
else:
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
noeuds.append(n)
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
if i==N-2 :
bords=[False,True,False]
g = [0,0,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g))
bords = [False,False,False]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g))
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig11.pdf
figure()
plot(x,U[N//2,:])
grid()
fig12.pdf
On reprend l'exemple A mais avec une condition de Robin sur les bords en y=0 et y=1 :
N = 50
x = np.linspace(0,1,N)
y = np.linspace(0,1,N)
noeuds = []
a = 1
s = 0
for j in range(N):
for i in range(N):
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
n = Noeud(x[i],y[j],a,s)
else:
if i==0:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=0)
elif i==N-1:
n = Noeud(x[i],y[j],a,s,lim=DIR,ksi=1)
if j==0 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
elif j==N-1 and i!=0 and i!=N-1:
n = Noeud(x[i],y[j],a,s,lim=NEU)
noeuds.append(n)
triangles = []
for j in range(N-1):
for i in range(N-1):
k = i+j*N
bords = [False,False,False]
K0 = [0,0,0]
u0 = [0,0,0]
if i==N-2 :
bords=[False,True,False]
g = [0,0,0]
if j==0:
bords=[True,False,False]
g = [0,0,0]
K0 = [-2,0,0]
if j==0 and i==N-2:
bords=[True,True,False]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+1],noeuds[k+1+N],bords=bords,g=g,K0=K0,u0=u0))
bords = [False,False,False]
K0 = [0,0,0]
u0 = [0,0,0]
if i==0:
bords = [False,False,True]
g = [0,0,0]
if j==N-2 :
bords = [False,True,False]
g = [0,0,0]
K0 = [0,-2,0]
if i==0 and j==N-2:
bords = [False,True,True]
g = [0,0,0]
triangles.append(Triangle(noeuds[k],noeuds[k+N+1],noeuds[k+N],bords=bords,g=g,K0=K0,u0=u0))
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
U = np.zeros((N,N),dtype=np.float64)
indice = 0
for j in range(N):
for i in range(N):
n = noeuds[indice]
U[j,i] = n.ksi
indice+= 1
from matplotlib.pyplot import *
figure()
contour(U,10,extent=(0,1,0,1))
fig13.pdf
figure()
plot(x,U[N//2,:])
grid()
fig14.pdf
Pour un ensemble de points, il existe au moins une triangulation, appelée triangulation de Delaunay, qui vérifie la propriété suivante : pour chaque triangle, le disque ouvert défini par le cercle qui passe par les trois sommets ne contient aucun point de l'ensemble. La triangulation de Delaunay est unique si l'ensemble ne contient pas quatre points situés sur un même cercle. Il existe un algorithme qui permet d'obtenir une triangulation de Delaunay. La fonction scipy.spatial.Delaunay implémente cet algorithme.
Une méthode simple de triangulation consiste à définir des points sur le domaine , avec en particulier des points sur les frontières du domaine et sur les frontières des objets à l'intérieur du domaine. Ces points devront être espacés convenablement afin que les triangles obtenus par triangulation de Delaunay n'aient pas des angles trop petits (idéalement supérieurs à 30 degrés).
On traite comme exemple un domaine circulaire avec une objet de forme carré au milieu :
from scipy.spatial import Delaunay
theta = np.linspace(0,2*np.pi,20)
points = [[0,0]]
for R in [10,8,6]:
for i in range(len(theta)):
points.append([R*np.cos(theta[i]),R*np.sin(theta[i])])
a = 3
N = 5
xx = np.linspace(-a,a,N)
yy = np.linspace(-a,a,N)
for x in xx:
points.append([x,-a])
points.append([x,a])
for y in yy:
points.append([-a,y])
points.append([a,y])
points = np.array(points)
tri = Delaunay(points)
figure(figsize=(8,8))
triplot(points[:,0], points[:,1], tri.simplices)
fig15.pdf
Les triangles sont donnés par tri.simplices, dont voici un échantillon :
print(tri.simplices[0:10])
--> array([[74, 59, 41],
[63, 65, 0],
[66, 45, 46],
[42, 78, 41],
[47, 48, 79],
[48, 49, 79],
[47, 64, 46],
[64, 66, 46],
[64, 47, 79],
[66, 64, 0]], dtype=int32)
Chaque triangle est défini par les trois indices de ses sommets dans la liste des points. Nous pouvons facilement définir un objet de la classe Noeud pour chaque point. Un objet de la classe Triangle doit être défini pour chaque triangle. Il y a cependant une difficulté : il faut identifier les triangles dont au moins un côté se trouve sur une frontière et attribuer la condition limite de Neumann s'il s'agit de . Pour la condition de Dirichlet, il n'y a aucune difficulté puisque celle-ci est définie sur chaque nœud de .
Les nœuds situés sur une frontière sont facilement identifiables puisque l'appartenance à cette frontière est définie dès le début, mais il faut en plus définir les segments de . Considérons le cas du contour du domaine et supposons qu'une condition de Neumann soit adoptée sur ce contour. Afin de repérer les points de ce contour dans les triangles, il suffit d'ajouter dans la classe Noeud un objet indiquant la frontière auquel le nœud appartient et l'indice du nœud dans cette frontière, les nœuds étant numérotés dans l'ordre de parcours de la frontière. Au moment de la création d'un triangle, on doit repérer les points du triangle qui appartiennent à chaque frontière et, pour une frontière donnée, repérer ceux qui sont consécutifs sur cet frontière. Ces deux points constituent un côté du triangle situé sur la frontière.
On définit une classe pour représenter une frontière de Neumann :
class Frontiere:
def __init__(self,nom,g0,K0,u0):
self.nom = nom
self.g0 = g0
self.K0 = K0
self.u0 = u0
self.nbpoints = 0
def nombrePoints(self,n):
self.nbpoints = n
Voici la nouvelle définition de la classe Noeud :
class Noeud:
def __init__(self,x,y,a,s,lim=0,ksi=0,frontiere=None,indiceFrontiere=0):
self.xy = np.array([x,y])
self.x = x
self.y = y
self.a = a
self.s = s
self.lim = lim
self.ksi = ksi
self.indice = 0 # indice dans le maillage
self.frontiere = frontiere
self.indiceFrontiere = indiceFrontiere
Lorsque lim==NEU, il faudra attribuer au nœud un objet de la classe Frontiere et l'indice du nœud sur cette frontière.
La fonction suivante permet de savoir si deux nœuds sont les deux sommets d'un segment appartenant à une frontière donnée :
def segment(n1,n2,frontiere):
if n1.lim==NEU and n1.frontiere.nom == frontiere.nom and n2.lim==NEU and n2.frontiere.nom == frontiere.nom:
if abs(n1.indiceFrontiere-n2.indiceFrontiere)==1:
return (frontiere.g0,frontiere.K0,frontiere.u0)
if abs(n1.indiceFrontiere-n2.indiceFrontiere)==frontiere.nbpoints-1:
return (frontiere.g0,frontiere.K0,frontiere.u0)
return None
La fonction suivante génère les triangles à partir de la liste tri.simplices :
def creerTriangles(noeuds,simplices,frontieres):
# noeuds : liste de noeuds
# simplices : liste de triangles
# frontieres : liste de frontieres
triangles = []
for t in simplices:
n1 = noeuds[t[0]]
n2 = noeuds[t[1]]
n3 = noeuds[t[2]]
f1 = f2 = f3 = (0,0,0)
b1 = b2 = b3 = False
for front in frontieres:
s1 = segment(n1,n2,front)
if s1!=None:
f1 = s1
b1 = True
s2 = segment(n2,n3,front)
if s2!=None:
f2 = s2
b2 = True
s3 = segment(n3,n1,front)
if s3!=None:
f3 = s3
b3 = True
tr = Triangle(n1,n2,n3,bords=[b1,b2,b3],g=[f1[0],f2[0],f3[0]],K0=[f1[1],f2[1],f3[1]],u0=[f1[2],f2[2],f3[2]])
triangles.append(tr)
return triangles
Lorsque la résolution est terminée, il peut être nécessaire de déterminer les valeurs de u sur une grille rectangulaire. Pour cela, on parcourt les triangles et, pour chacun, on repère les points de la grille qui sont dans ce triangle. La valeur de u sur chacun de ces points est calculée au moyen des trois fonctions relatives aux trois sommets du triangle (fonction interpol de la classe Triangle).
La figure suivante montre un triangle et une grille rectangulaire :
Figure pleine pageLe rectangle représenté en trait pointillé est le plus petit rectangle (de côtés parallèles aux axes de la grille) qui contient le triangle. On commence par identifier les points de la grille qui sont dans ce rectangle. La grille est définie par les points suivants :
Les indices des points de la grille contenus dans le rectangle sont définis par :
Pour savoir si un point P de la grille est dans le triangle, on doit déterminer de quel côté il se trouve par rapport à chaque droite définie par deux sommets. Les sommets du triangle sont ordonnés dans le sens direct. Pour la droite NiNj, le point P se trouve à gauche de la droite parcourue dans le sens Ni vers Nj si :
Le point P est à l'intérieur du triangle si et seulement si cette condition est vérifiée pour les trois côtés du triangle. La fonction contientPoint de la classe Triangle effectue ce test. Le rectangle contenant le triangle est donné par la fonction rectangle, sous la forme [xmin,xmax,ymin,ymax].
La fonction suivante effectue l'interpolation sur une grille :
def interpolGrille(triangles,x0,x1,y0,y1,Nx,Ny):
U = np.zeros((Nx,Ny),dtype=np.float64)
Delta_x = (x1-x0)/(Nx-1)
Delta_y = (y1-y0)/(Ny-1)
for tr in triangles:
rect = tr.rectangle()
pmin = np.floor((rect[0]-x0)/Delta_x)
pmax = np.floor((rect[1]-x0)/Delta_x)+1
qmin = np.floor((rect[2]-y0)/Delta_y)
qmax = np.floor((rect[3]-y0)/Delta_y)+1
if pmin<pmax and qmin<qmax:
for p in range(int(pmin),int(pmax)+1):
for q in range(int(qmin),int(qmax)+1):
x = x0+p*Delta_x
y = y0+q*Delta_y
if tr.contientPoint(x,y):
U[q,p] = tr.interpol(x,y)
return U
Une source uniforme est présente sur tout le domaine et une condition de Dirichlet avec une valeur nulle est appliquée sur le contour. La liste des objets de la classe Noeud est générée en même temps que la liste des points.
points = [[0,0]]
noeuds = []
s = 1
a = 1
noeuds.append(Noeud(0,0,a,s))
theta = np.linspace(0,2*np.pi,40)
R = 10
for i in range(len(theta)-1):
x = R*np.cos(theta[i])
y = R*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=0))
for R in [9,8,7,6,5,4,2,1]:
for i in range(len(theta)-1):
x = R*np.cos(theta[i])
y = R*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s))
L = 3
N = 10
xx = np.linspace(-L,L,N)
yy = np.linspace(-L,L,N)
for x in xx:
points.append([x,-L])
noeuds.append(Noeud(x,-L,a,s))
points.append([x,L])
noeuds.append(Noeud(x,L,a,s))
for y in yy:
points.append([-L,y])
noeuds.append(Noeud(-L,y,a,s))
points.append([L,y])
noeuds.append(Noeud(L,y,a,s))
points = np.array(points)
tri = Delaunay(points)
figure(figsize=(8,8))
triplot(points[:,0], points[:,1], tri.simplices)
fig16.pdf
Voici le calcul puis le tracé des lignes d'isovaleurs de u au moyen de la fonction matplotlib.pyplot.tricontour :
triangles = creerTriangles(noeuds,tri.simplices,[])
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
figure(figsize=(8,8))
U = np.zeros(len(noeuds))
for i in range(len(noeuds)):
U[i] = noeuds[i].ksi
figure(figsize=(8,8))
tricontour(points[:,0], points[:,1], tri.simplices,U,levels=10)
fig17.pdf
Voici l'utilisation de la fonction interpolGrille :
Nx,Ny = 100,100
x0,y0 = -10,-10
x1,y1 = 10,10
U = interpolGrille(triangles,x0,x1,y0,y1,Nx,Ny)
figure(figsize=(8,8))
contour(U,levels=10,extent=(-10,10,-10,10))
fig18.pdf
Le fait de disposer des valeurs dur une grille permet de tracer un profil sur une droite parallèle à un des axes :
figure()
plot(np.linspace(-10,10,Nx),U[Ny//2,:])
xlabel("x")
ylabel("u(x,0)")
fig19.pdf
La source est nulle mais une condition de Dirichlet de valeur 1 est mise sur l'objet carré situé au centre :
points = [[0,0]]
noeuds = []
s = 0
a = 1
noeuds.append(Noeud(0,0,a,s))
theta = np.linspace(0,2*np.pi,30)
R = 10
for i in range(len(theta)-1):
x = R*np.cos(theta[i])
y = R*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=0))
for R in [9,8,7,6,5,4,2,1]:
for i in range(len(theta)-1):
x = R*np.cos(theta[i])
y = R*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s))
L = 3
N = 10
xx = np.linspace(-L,L,N)
yy = np.linspace(-L,L,N)
for x in xx:
points.append([x,-L])
noeuds.append(Noeud(x,-L,a,s,lim=DIR,ksi=1))
points.append([x,L])
noeuds.append(Noeud(x,L,a,s,lim=DIR,ksi=1))
for y in yy:
points.append([-L,y])
noeuds.append(Noeud(-L,y,a,s,lim=DIR,ksi=1))
points.append([L,y])
noeuds.append(Noeud(L,y,a,s,lim=DIR,ksi=1))
points = np.array(points)
tri = Delaunay(points)
triangles = creerTriangles(noeuds,tri.simplices,[])
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
figure(figsize=(8,8))
U = np.zeros(len(noeuds))
for i in range(len(noeuds)):
U[i] = noeuds[i].ksi
figure(figsize=(8,8))
tricontour(points[:,0], points[:,1], tri.simplices,U,levels=10)
fig20.pdf
Une condition de Neumann avec un gradient -1 est placée sur le contour. Une condition de Dirichlet de valeur 1 est mise sur l'objet carré situé au centre.
bord = Frontiere("bord",-1,0,0)
frontieres = [bord]
points = [[0,0]]
noeuds = []
s = 0
a = 1
noeuds.append(Noeud(0,0,a,s))
theta = np.linspace(0,2*np.pi,30)
R = 10
for i in range(len(theta)-1):
x = R*np.cos(theta[i])
y = R*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=NEU,frontiere=bord,indiceFrontiere=i))
bord.nombrePoints(len(theta)-1)
for R in [9,8,7,6,5,4,2,1]:
for i in range(len(theta)-1):
x = R*np.cos(theta[i])
y = R*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s))
L = 3
N = 10
xx = np.linspace(-L,L,N)
yy = np.linspace(-L,L,N)
for x in xx:
points.append([x,-L])
noeuds.append(Noeud(x,-L,a,s,lim=DIR,ksi=1))
points.append([x,L])
noeuds.append(Noeud(x,L,a,s,lim=DIR,ksi=1))
for y in yy:
points.append([-L,y])
noeuds.append(Noeud(-L,y,a,s,lim=DIR,ksi=1))
points.append([L,y])
noeuds.append(Noeud(L,y,a,s,lim=DIR,ksi=1))
points = np.array(points)
tri = Delaunay(points)
triangles = creerTriangles(noeuds,tri.simplices,frontieres)
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
figure(figsize=(8,8))
U = np.zeros(len(noeuds))
for i in range(len(noeuds)):
U[i] = noeuds[i].ksi
figure(figsize=(8,8))
tricontour(points[:,0], points[:,1], tri.simplices,U,levels=10)
fig21.pdf
Voici l'utilisation de la fonction interpolGrille :
Nx,Ny = 100,100
x0,y0 = -10,-10
x1,y1 = 10,10
U = interpolGrille(triangles,x0,x1,y0,y1,Nx,Ny)
figure(figsize=(8,8))
contour(U,levels=10,extent=(-10,10,-10,10))
fig22.pdf
figure()
plot(np.linspace(-10,10,Nx),U[Ny//2,:],".")
xlabel("x")
ylabel("u(x,0)")
fig23.pdf
figure()
plot(np.linspace(-10,10,Ny),U[:,Nx//2],".")
xlabel("y")
ylabel("u(0,y)")
fig24.pdf
La valeur sur le bord du domaine n'est pas nulle mais elle reste évidemment nulle dans les parties de la grille hors du maillage. Pour les deux courbes ci-dessus, les deux points extrêmes ne sont pas dans le maillage.
Le domaine est rectangulaire et les points sont disposés sur un grille. Deux objets circulaires sont placés avec une condition de Dirichlet sur chacun d'eux. Une condition de Dirichlet est appliquée sur les bords. Il s'agit du problème d'électrostatique comportant deux conducteurs cylindriques parallèles avec une différence de potentiel entre eux.
points = []
noeuds = []
s = 0
a = 1
x1,y1 = 0,3
x2,y2 = 0,-3
R1 = 1
N = 51
xx = np.linspace(-10,10,N)
yy = np.linspace(-10,10,N)
for i in range(N):
for j in range(N):
x = xx[i]
y = yy[j]
points.append([x,y])
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
noeuds.append(Noeud(x,y,a,s))
else:
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=0))
theta = np.linspace(0,2*np.pi,20)
for i in range(len(theta)-1):
x = x1+R1*np.cos(theta[i])
y = y1+R1*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=1))
for i in range(len(theta)-1):
x = x2+R1*np.cos(theta[i])
y = y2+R1*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=-1))
points = np.array(points)
tri = Delaunay(points)
figure(figsize=(8,8))
triplot(points[:,0], points[:,1], tri.simplices)
fig25.pdf
triangles = creerTriangles(noeuds,tri.simplices,frontieres)
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
figure(figsize=(8,8))
U = np.zeros(len(noeuds))
for i in range(len(noeuds)):
U[i] = noeuds[i].ksi
figure(figsize=(8,8))
tricontour(points[:,0], points[:,1], tri.simplices,U,levels=10)
fig26.pdf
Nx,Ny = 100,100
x0,y0 = -10,-10
x1,y1 = 10,10
U = interpolGrille(triangles,x0,x1,y0,y1,Nx,Ny)
figure(figsize=(8,8))
contour(U,levels=10,extent=(-10,10,-10,10))
fig27.pdf
figure()
plot(np.linspace(-10,10,Ny),U[:,Nx//2])
xlabel("y")
ylabel("u(0,y)")
fig28.pdf
Le maillage n'est pas optimal : certains triangles au voisinage des cercles ont un angle trop petit. Pourtant, le résultat semble satisfaisant.
Si l'on réduit la distance entre les deux cercles, il faut aussi réduire la grille principale car la variation de u entre les deux cercles augmente :
points = []
noeuds = []
s = 0
a = 1
x1,y1 = 0,1.5
x2,y2 = 0,-1.5
R1 = 1
N = 71
xx = np.linspace(-10,10,N)
yy = np.linspace(-10,10,N)
for i in range(N):
for j in range(N):
x = xx[i]
y = yy[j]
points.append([x,y])
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
noeuds.append(Noeud(x,y,a,s))
else:
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=0))
theta = np.linspace(0,2*np.pi,20)
for i in range(len(theta)-1):
x = x1+R1*np.cos(theta[i])
y = y1+R1*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=1))
for i in range(len(theta)-1):
x = x2+R1*np.cos(theta[i])
y = y2+R1*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=-1))
points = np.array(points)
tri = Delaunay(points)
figure(figsize=(8,8))
triplot(points[:,0], points[:,1], tri.simplices)
fig29.pdf
triangles = creerTriangles(noeuds,tri.simplices,frontieres)
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
figure(figsize=(8,8))
U = np.zeros(len(noeuds))
for i in range(len(noeuds)):
U[i] = noeuds[i].ksi
figure(figsize=(8,8))
tricontour(points[:,0], points[:,1], tri.simplices,U,levels=10)
fig30.pdf
Nx,Ny = 100,100
x0,y0 = -10,-10
x1,y1 = 10,10
U = interpolGrille(triangles,x0,x1,y0,y1,Nx,Ny)
figure(figsize=(8,8))
contour(U,levels=10,extent=(-10,10,-10,10))
fig31.pdf
figure()
plot(np.linspace(-10,10,Ny),U[:,Nx//2])
xlabel("y")
ylabel("u(0,y)")
fig32.pdf
On reprend la configuration précédente mais avec une condition de Neumann sur chaque cercle. Pour un problème d'électrostatique, cela correspond à deux corps isolants cylindriques avec une densité de charge uniforme sur leur surface.
cercle1 = Frontiere("cercle1",-1,0,0)
cercle2 = Frontiere("cercle2",1,0,0)
frontieres = [cercle1,cercle2]
points = []
noeuds = []
s = 0
a = 1
x1,y1 = 0,3
x2,y2 = 0,-3
R1 = 1
N = 51
xx = np.linspace(-10,10,N)
yy = np.linspace(-10,10,N)
for i in range(N):
for j in range(N):
x = xx[i]
y = yy[j]
points.append([x,y])
if i!=0 and i!=N-1 and j!=0 and j!=N-1:
noeuds.append(Noeud(x,y,a,s))
else:
noeuds.append(Noeud(x,y,a,s,lim=DIR,ksi=0))
theta = np.linspace(0,2*np.pi,20)
for i in range(len(theta)-1):
x = x1+R1*np.cos(theta[i])
y = y1+R1*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=NEU,frontiere=cercle1,indiceFrontiere=i))
cercle1.nombrePoints(len(theta)-1)
for i in range(len(theta)-1):
x = x2+R1*np.cos(theta[i])
y = y2+R1*np.sin(theta[i])
points.append([x,y])
noeuds.append(Noeud(x,y,a,s,lim=NEU,frontiere=cercle2,indiceFrontiere=i))
cercle2.nombrePoints(len(theta)-1)
points = np.array(points)
tri = Delaunay(points)
triangles = creerTriangles(noeuds,tri.simplices,frontieres)
elem = ElementsFinis2D(noeuds,triangles)
elem.solve()
figure(figsize=(8,8))
U = np.zeros(len(noeuds))
for i in range(len(noeuds)):
U[i] = noeuds[i].ksi
Nx,Ny = 100,100
x0,y0 = -10,-10
x1,y1 = 10,10
U = interpolGrille(triangles,x0,x1,y0,y1,Nx,Ny)
figure(figsize=(8,8))
contour(U,levels=10,extent=(-10,10,-10,10))
fig33.pdf
figure()
plot(np.linspace(-10,10,Ny),U[:,Nx//2])
xlabel("y")
ylabel("u(0,y)")
fig34.pdf