FEniCS

“The vision of FEniCS is to set a new standard in Computational Mathematical Modeling (CMM), which is the Automation of CMM (ACMM), towards the goals of generality, efficiency, and simplicity, concerning mathematical methodology, implementation, and application.

FEniCS comes packed with features for the computational scientist. Partial differential equations can be specified in near-mathematical notation (as finite element variational problems) and solved automatically. FEniCS also provides a large library of important tools for the numerial analyst who wishes to explore and develop new methods.

The basic ingredients for the Automation of CMM are :

  • Automated solution of variational problems
  • Automated error control and adaptivity
  • An extensive library of finite elements (Lagrange, DG methods, BDM, RT and Nedelec)
  • High performance linear algebra (include PETSc, Trilinos/Epetra, uBLAS and MTL4)
  • Computational meshes (mesh iterators, mesh partitioning, …)
  • Postprocessing (live plotting, advanced postprocessing, …)
  • C++ and Python interface”

Full features can be found here:http://fenicsproject.org/about/features.html#features.

Version installée

* For the recent version 2017

1.6.0 (séquentielle)

module load fenics/1.6.0

Exemples

\begin{equation}
  \begin{split}
   \tag{A}
    - \Delta u &= f \,\,\, \quad \mbox{in } \Omega,
    \\
    u &= u_0 \quad \mbox{on } \partial \Omega.
  \end{split}
\end{equation}

Avec : $u = u(x)$ et la fonction inconnue, $f = f(x)$ est une fonction pré-définie, $\Delta$ l'opérateur laplacien, $\Omega$ est le domaine spatial, $\partial\Omega$ est la limite de $\Omega$.

(1) Représente une EDP stationnaire, avec un ensemble de conditions aux limites.

Pour résoudre un problème physique avec fenics, généralement, on suit les étapes suivantes :

  • Identifier l'EDP et ses conditions aux limites
  • Reformuler l'EDP sous forme d'un problème variationnel discret
  • Écrire le programme, python/C++, ou les formes variationelles sont codées ainsi que les données d'entrée comme $f$ et $u_0$ et le mesh $omega$
  • Rajouter des instructions dans le programme pour résoudre le problème variationnel, calcul des quantités dérivées telles que $\nabla u $, et la visualisation des résultats.

L'équation (A) est transformée sous forme variationelle :

\begin{equation} 
\int_\Omega\nabla u\cdot\nabla v\dx = \int_\Omega fvdx 
\tag{B}
\end{equation}

\begin{equation}
  a(u, v) = L(v)
   \tag{C}
\end{equation}

Pour les conditions aux limites on suppose : $u_0(x, y) = 1 +x^2 + 2y^2$, $\[ f(x,y) = -6$ et $\[ \Omega = [0,1]\times [0,1] \]$ un domaine $2D$.

Programme Python

Avec Python, on peut écrire directement l'équation (A) :

u = TrialFunction(V)
v = TestFunction(V)
 
a = dot(grad(u), grad(v))*dx
L = f*v*dx

On définit les conditions aux limites :

# valeurs d'entrée : u0 et f
u0 = Expression("1 + x[0]*x[0] + 2*x[1]*x[1]")
f = Constant(-6.0)
 
# conditions aux limites
# Cette fonction est appelée par fenics pour tout points aux limites
def u0_boundary (x , on_boundary ):
 return on_boundary
 
bc = DirichletBC (V , u0 , u0_boundary)

Et on peut résoudre automatiquement le problème variationnel ci-dessus on appelant la fonction solve() :

u = Function(V)
solve(a == L, u, bc)

Programme final

Poisson.py
from dolfin import *
 
# Create mesh and define function space
 
mesh = UnitIntervalMesh(50)
V = FunctionSpace(mesh, "CG", 1)
 
 
 
# Define boundary conditions
u0 = Expression("1 + x[0]*x[0] + 2*x[1]*x[1]")
 
def u0_boundary(x, on_boundary):
    return on_boundary
 
bc = DirichletBC(V, u0, u0_boundary)
 
# Define variational problem
u = TrialFunction(V)
v = TestFunction(V)
f = Constant(-6.0)
a = inner(nabla_grad(u), nabla_grad(v))*dx
L = f*v*dx
 
# Compute solution
u = Function(V)
solve(a == L, u, bc)
 
# Plot solution and mesh
plot(u)
plot(mesh)
 
# Dump solution to file in VTK format
file = File("poisson.pvd")
file << u
 
# Hold plot
interactive()

On lance l'exécution :

python poisson.py 

L'affichage automatique des résultats est désactivé. A la fin de l'exécution, utiliser le logiciel paraview (module load tools/paraview) pour afficher les graphiques

Programme C++

L'implémentation se compose de deux fichiers : un fichier contenant la définition des formes variationnelles exprimée en UFL et un fichier C++ contenant la simulation réelle.

# Compile this form with FFC: ffc -l dolfin Poisson.ufl.
 
element = FiniteElement("Lagrange", triangle, 1)
 
u = TrialFunction(element)
v = TestFunction(element)
f = Coefficient(element)
 
 
a = inner(grad(u), grad(v))*dx
L = f*v*dx

Avant que ce fichier soit exploitable dans le programme C++, il doit être compilé en utilisant FFC en exécutant (sur la ligne de commande) :

ffc -l dolfin Poisson.ufl

ffc génère un fichier Poission.h qui sera utilisé dans le programme principal.

main.cpp
#include <dolfin.h>
#include "Poisson.h"
 
using namespace dolfin;
 
 
class Source : public Expression
{
  void eval(Array<double>& values, const Array<double>& x) const
  {
    double dx = x[0] - 0.5;
    double dy = x[1] - 0.5;
    values[0] = 10*exp(-(dx*dx + dy*dy) / 0.02);
  }
};
// Normal derivative (Neumann boundary condition)
 
// Sub domain for Dirichlet boundary condition
class DirichletBoundary : public SubDomain
{
  bool inside(const Array<double>& x, bool on_boundary) const
  {
    return x[0] < DOLFIN_EPS or x[0] > 1.0 - DOLFIN_EPS;
  }
};
 
int main()
{
  // Create mesh and function space
  UnitSquare mesh(32, 32);
  Poisson::FunctionSpace V(mesh);
 
  // Define boundary condition
  Source u0;
  DirichletBoundary boundary;
  DirichletBC bc(V, u0, boundary);
 
  // Define variational forms
  Poisson::BilinearForm a(V, V);
  Poisson::LinearForm L(V);
 
 
  Constant f(-6.0);
 
  L.f = f;
 
  // Compute solution
  Function u(V);
  solve(a == L, u, bc);
 
  // Save solution in VTK format
  File file("poisson.pvd");
  file << u;
 
  // Plot solution
  plot(u);
 
  return 0;
}

Compilation

La manière la plus simple pour compiler des programmes fenics est d'utiliser Makefile. Voic un exemple d'un Makefile pour compiler l'exemple ci-dessus :

CFLAGS=`pkg-config --cflags dolfin`
LIBS=`pkg-config --libs dolfin`
CXX=`pkg-config --variable=compiler dolfin`
 
DEST= poisson
 
OBJECTS = main.o
 
all: $(DEST)
 
install:
 
clean:
        rm -f *.o poisson *.poisson $(OBJECTS) $(DEST)
 
$(DEST): $(OBJECTS)
        $(CXX) -o $@ $(OBJECTS) $(CFLAGS) $(LIBS)
 
.cpp.o:
        $(CXX) $(CFLAGS) -c $<
 make 
 ./poisson

Script SGE

Exemple de script SGE pour lancer la version séquentielle de Fenics.

#!/bin/bash
 
#$ -N test_sge
#$ -o $JOB_NAME.$JOB_ID.out
#$ -e $JOB_NAME.$JOB_ID.err
 
module load fenics/1.6.0 
 
export PATH=$WORK
 
python poisson.py

Les fichiers de sortie (VTK) peuvent être visualisés avec le logiciel Paraview.

#!/bin/bash -l
#$ -q parallel.q
#$ -V
#$ -N fenics_sge
#$ -pe mpi 16
#$ -o $JOB_NAME.$JOB_ID.out
 
#### on charge le module fenics
 
module load fenics/mpi
 
export HOME=$WORK
 
## lancement de l'application
 
mpirun -np $NSLOTS ./appli_fenics
 
## OR
 
mpirun -np $NSLOTS python ./appli_fenics.py

Liens