Vous êtes sur la page 1sur 6

MAP431.

Analyse variationnelle des équations aux dérivées partielles

PC1 : Initiation à FreeFEM


FreeFEM est un logiciel qui permet de résoudre des équations aux dérivées partielles par la méthode
des éléments finis. C’est un logiciel très intéressant à plusieurs titres. Il s’appuie sur un langage haut
niveau fondé sur le C++ afin d’écrire les formulations variationnelles avec une syntaxe très proche de
l’écriture mathématique. C’est par ailleurs un logiciel open-source, développé par plusieurs chercheurs,
notamment Frédéric Hecht. FreeFEM est disponible librement aux adresses :
Website : https://freefem.org
Latest release : https://github.com/FreeFem/FreeFem-sources/releases
Github : https://github.com/FreeFem/
Documentation : https://doc.freefem.org
Reference manual : https://github.com/FreeFem/FreeFem-doc-pdf/raw/master/freefem%
2B%2Bdoc.pdf
Si vous utilisez FreeFEM, il est important de citer [1] dans votre travail.
Les étapes pour l’écriture d’un script FreeFEM (fichier .edp) sont habituelles, elles correspondent
à la méthodologie développée dans le cours :
— On définit un domaine de calcul que l’on discrétise avec un maillage ;
— Sur ce domaine, on définit l’espace des fonctions associées à l’approximation, ici des fonctions
éléments finis ;
— On décrit les équations à résoudre sous forme variationnelle en indiquant les inconnues, les
fonctions tests et la formulation variationnelle ;
— On résout et on trace la solution numérique calculée.
Nous allons donner quelques exemples de ces différentes étapes dans ce qui suit.

1 Le préambule
Il peut être utile d’organiser votre fichier .edp avec un préambule permettant de préciser le pro-
blème considéré, les constantes physiques du problème, les règles d’affichage des solutions, etc. La
définition d’un tel préambule nous permettra de voir quelques notions utiles. On commence par
cout << " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ n " ;
cout << " PC " << 1 << " -- Laplace solution " << endl ;
cout << " = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = \ n " ;

où cout permet, comme en C++, d’afficher des informations. La concaténation s’effectue avec “«” et
le passage à la ligne par “\n” ou endl. N’oubliez pas les “;” à la fin de chaque déclaration.
Ensuite, on peut préciser quelques paramètres.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Dimension constants
real L = 1;
real l = 1;

// Physical constants
real D = 10;

// Discretization constants
int Nx = 20;
int Ny = 10;
real dt = 0.01;
int NT = 100;

1
où on remarque que // déclare un commentaire, et que le langage est typé (real,int) comme en
C++. On peut finalement préciser comment on souhaite visualiser les résultats, afficher des quantités,
idéalement en écriture scientifique.
// Outputs
load " iovtk " // library for vtk input - outputs
cout . scientific ;
cout . scientific . precision (6) ;

2 Le maillage
On peut maintenant construire la géométrie, par exemple mailler un rectangle. Ceci se fait grâce
aux commandes
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Triangular Mesh with references
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int dirichlet = 1;
int neumann = 0;
int [ int ] labs = [ dirichlet , neumann , dirichlet , neumann ];
mesh Th = square ( Nx , Ny ,[ L *x , l * y ] , label = labs ) ;

Le rectangle est maillé avec Nx segments en x et Ny segments en y. Chaque côté du domaine créé avec
la fonction square possède un label permettant (entre autres) de définir les conditions aux bords. Par
défaut, ces labels sont 1, 2, 3, 4 dans l’ordre, en tournant dans le sens trigonométrique et en commençant
par le côté du bas. Ici nous avons imposé un label fixé à chaque bord du rectangle : 1 sur les bords
horizontaux et 0 sur les bords verticaux.
L’affichage du maillage se fait à l’aide de la commande plot (noter l’ajout de la directive cmm
qui permet d’ajouter une légende au graphique obtenu). Notons que le paramètre wait ici permet de
stopper le programme lors de l’affichage. Il faut appuyer sur la touche Entrée pour le redémarrer.
plot ( Th , wait =1 , cmm = " Mesh " ) ;

Alternativement, on peut construire un maillage en assemblant des bords. Pour le deuxième


maillage, on propose d’utiliser des courbes paramétrées.
border a1 ( t =0 ,1) { x = L * t ; y =0; label = neumann ;}
border a2 ( t =0 ,1) { x = L ; y = l * t ; label = neumann ;}
border a3 ( t =0 ,1) { x = L *(1 - t ) ; y = l +0.5* L * sin ( pi * t ) ; label = dirichlet ;}
border a4 ( t =0 ,1) { x =0; y = l *(1 - t ) ; label = neumann ;}
mesh Th2 = buildmesh ( a1 ( Nx ) + a2 ( Ny ) + a3 ( Nx ) + a4 ( Ny ) ) ;
plot ( Th2 , wait =1 , cmm = " Mesh " ) ;

Sur un maillage donné, on peut réaliser des calculs d’intégrales diverses. Par exemple, l’aire du
domaine est calculable par
real area = int2d ( Th2 ) (1.0) ;
cout << " Area = " << area << endl ;

3 Les éléments finis


Une fois le domaine maillé, on définit un espace d’approximation à partir des fonctions éléments
finis. Par exemple, on pourra sélectionner des éléments de Lagrange P1 ou P2 , etc.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// FE Spaces and functions
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fespace Vh ( Th , P2 ) ;
fespace Vh2 ( Th2 , P1 ) ;

2
Ceci définit des espaces d’éléments finis Vh, (resp. Vh2) de type P2 (resp. P1 ) sur le maillage Th (resp.
Th2) défini précédemment. La commande précédente crée donc un nouveau type Vh (resp. Vh2) dont
on pourra se servir pour déclarer des variables.
Vh wh , uh , vh ;
Vh2 uhh , vhh , uhhold ;

Cette commande crée des variables wh,uh,vh de type Vh et d’autres uhh,vhh,uhhold de type Vh2.
Ces variables sont des fonctions qui dépendent d’un nombre fini de valeurs (les degrés de liberté). On
peut donc les voir alternativement comme des fonctions ou des vecteurs si on décide de retenir leurs
composantes dans la base des fonctions de base. On peut afficher le nombre de degrés de liberté (ddl
en français, dof en anglais pour degrees of freedom).
cout << " Ndof = " << wh []. n << endl ;

On peut accéder aux composantes de wh et en mettre une seule à 1, puis afficher la fonction
correspondante (ainsi que le maillage).
wh [][200] = 1;
plot ( Th , wh , wait =1 , dim =2 , value =10 , cmm = " basis function " ) ;

De façon similaire, on peut définir une fonction f que l’on interpole sur l’espace Vh avant de tracer
son interpolant.
func f = sin ( x ) * cos ( y ) ;
Vh fh = f ; // interpolation in Vh
plot ( fh , wait =1 , fill =1 , dim =2 , value =10 , cmm = " function " ) ;

4 La formulation variationnelle
Vient ensuite la description de la formulation variationnelle. Celle-ci est identique à la formulation
variationnelle continue. Par exemple, si l’on souhaite résoudre le problème

u − D∆u = f, sur Ω


u = x, sur ΓD (1)


 ∂n u = 0, sur ΓN

on écrira la formulation variationnelle :


Trouver u ∈ H 1 (Ω) avec u = x sur ΓD au sens de la trace et tel que pour tout v ∈ H0,Γ
1
D
(Ω) = {v ∈
1
H (Ω) : v = 0 sur ΓD au sens de la trace }, on ait
Z Z Z
D∇u(x) · ∇v(x) dx + u(x)v(x) dx = f (x)v(x) dx.
Ω Ω Ω

En FreeFEM cela donne (avec la fonction f définie ci-dessus) :


problem L a pl ac eAb so rb Di ric hl et ( uh , vh )
= int2d ( Th ) ( D * dx ( uh ) * dx ( vh ) + D * dy ( uh ) * dy ( vh ) + uh * vh ) // bilinear form
- int2d ( Th ) ( f * vh ) // linear form
+ on ( dirichlet , uh = x ) ; // boundary
conditions

Attention, l’ordre est très important :


— On définit d’abord l’inconnue uh et la fonction test vh ;
— On définit ensuite le problème et on lui donne un nom (LaplaceDirichlet). Il faut mettre
l’inconnue et la fonction test en arguments dans cet ordre.
— On donne ensuite la forme bilinéaire (noter l’usage des mots clés dx,dy pour les dérivées), puis
la forme linéaire et enfin les conditions aux limites essentielles (ici Dirichlet).

3
Ensuite, on résout :
L a p l a c e A b so rb Di ric hl et ;

On peut ensuite afficher et sauver les résultats. Soit en format image .eps, soit dans un format de sortie
de calcul .vtk lisible par des logiciels de visualisation tels que Paraview (https://www.paraview.
org) ou VisIt (https://wci.llnl.gov/simulation/computer-codes/visit/), qui lisent des fichiers
.vtk.
// Save
plot ( uh , wait =1 , fill =1 , dim =3 , value =10 , cmm = " solution " , ps = " solution . eps " ) ;
savevtk ( " Result . vtu " , Th , uh , dataname = " solution " ) ;

5 Manipulations plus avancées : Un schéma en temps


Considérons maintenant une équation de la chaleur


∂t u(x, t) − D∆u(x, t) = f (x), (x, t) ∈ Ω × (0, T )

u(x, t) = 12, (x, t) ∈ ΓD × (0, T )


(2)


 ∂n u(x, t) = 0, (x, t) ∈ ΓN × (0, T )

u(x, 0) = 20, x∈Ω

discrétisée pour la partie en temps par un schéma aux différences finies implicite de pas δt

k+1 (x) − uk (x)
u

 − D∆uk+1 (x) = f (x), x ∈ Ω, k ∈ {0, . . . , NT =
T
}
δt δt




 k
u (x) = 12, x ∈ ΓD , k ∈ {0, . . . , NT } (3)
 k



 ∂n u (x) = 0, x ∈ ΓN , k ∈ {0, . . . , NT }

 0

u (x) = 20, x ∈ Ω.

La mise en œuvre de ce schéma nous permet d’utiliser quelques outils avancés de FreeFEM. On
démontre tout d’abord que ce schéma revient à résoudre

U k+1 = A−1 Rk , k ≥ 0,

où A est la matrice associée à la forme bilinéaire


Z Z
a(u, v) = Dδt ∇u(x) · ∇v(x) dx + u(x)v(x) dx,
Ω Ω

et Rk est le vecteur associé à la forme linéaire


Z
 k 
`(v) = u (x)v(x) + δtf (x)v(x) dx.

Commençons par définir les formes variationnelles :


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Heat
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Variationnal formulations
// - - - - - - - - - - - - - - - - - - - - - - - - -

// Macro
macro Grad ( u ) [ dx ( u ) , dy ( u ) ] // EOM

varf HeatOperator ( uhh , vhh )


= int2d ( Th2 ) ( D * dt * ( Grad ( uhh ) ’* Grad ( vhh ) ) + uhh * vhh ) // bilinear form
+ on ( dirichlet , uhh =0) ; // boundary conditions

4
varf HeatRHS ( uhh , vhh ) =
int2d ( Th2 ) ( uhhold * vhh + dt * g * vhh ) // linear form with respect to ( uhh , vhh )
+ on ( dirichlet , uhh = 12) ; // boundary conditions

matrix Op = HeatOperator ( Vh2 , Vh2 , solver = UMFPACK ) ;


real [ int ] Rhs = HeatRHS (0 , Vh2 ) ;

On peut noter ici l’utilisation de la macro Grad(u) pour simplifier l’écriture de la forme bilinéaire. La
définition d’une macro se termine toujours par //EOM et on ne met pas de ; à la fin.
Puis on réalise la boucle en temps, par exemple avec
uhh = 20;
int k = 0;
while (1)
{
cout << " Iteration " << k << " - Temp Max : " << uhh []. max << endl ;
if ( k > NT )
{
break ;
}
else
{
k ++;
uhhold = uhh ;
Rhs = HeatRHS (0 , Vh2 ) ;
uhh [] = Op ^ -1* Rhs ;
}
}

Une remarque fondamentale : la commande x = A^-1*b résout le système linéaire Ax = b, mais


n’inverse pas la matrice A.
Là encore, la visualisation peut-être utile soit en sauvant des scalaires dans un fichier .csv exploi-
table, soit en réalisant des sorties images eps ou calcul vtk. Juste avant la boucle en temps, on ajoute
donc
// Save file
ofstream myfile ( " result . csv " ) ; // file name and format
int nold = myfile . precision (15) ; // precision
myfile . scientific ; // scientific notation
myfile << " time , temperature " << endl ;

Puis dans la boucle en temps juste avant le if, on complète (attention à créer le dossier Results au
préalable dans votre dossier de travail) avec
real Tmean = int2d ( Th2 ) ( abs ( uhh ) ) / area ;
myfile << k * dt << " , " << Tmean << endl ;
plot ( uhh , fill =1 , dim =3 , value =10 , cmm = " solution " , ps = " Results / result_ " + k + " . eps " ) ;
savevtk ( " Results / result_ " + k + " . vtu " , Th2 , uhh , dataname = " Temperature " ) ;

6 Formalisation des résultats


Pour préparer un compte rendu de résultats, vous devez utiliser LaTex et pouvez utiliser le package
Polytechnique disponible ici https://github.com/BinetReseau/polytechnique-LaTeX.
Pour générer les courbes, vous pouvez d’abord créer un .pdf indépendant comportant la courbe.
Par exemple, voici un fichier .tex permettant de produire la courbe en pgfplot
\ documentclass { standalone }

\ usepackage { tikz }
\ usepackage { pgfplots }

5
\ begin { document }

\ begin { tikzpicture }
\ begin { axis }
\ addplot table [ x = time , y = temperature , col sep = comma ] { result . csv };
\ end { axis }
\ end { tikzpicture }

\ end { document }

et un fichier plus complet est disponible sur le moodle.

7 À vous de jouer
On considère l’équation

u(x, y) − ∆u(x, y) = (1 + 2π 2 ) cos(πx) cos(πy), (x, y) ∈ (0, 1) × (0, 1)


(

∂n u(x, y) = 0, (x, y) ∈ {0, 1} × (0, 1) ∪ (0, 1) × {0, 1}.

Réaliser un programme qui illustre le fait que la résolution du problème éléments finis associé
en Pk donne une fonction uh telle que
Z
|u − uh |2 + |∇u − ∇uh |2 dΩ ≤ Ch2k .
 

Références
[1] F. Hecht New development in FreeFem++. Journal of Numerical Mathematics, 20 (3–4), 251–265,
2012

Vous aimerez peut-être aussi