0% found this document useful (0 votes)
47 views715 pages

FreeFEM Documentation

The FreeFEM Documentation for Release 4.8, authored by Frederic Hecht, provides comprehensive guidance on the software's features, installation, and usage. It includes sections on learning through examples, detailed documentation, and language references. The document serves as a resource for users to effectively utilize FreeFEM for solving partial differential equations.

Uploaded by

sayahdin.alfat
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
47 views715 pages

FreeFEM Documentation

The FreeFEM Documentation for Release 4.8, authored by Frederic Hecht, provides comprehensive guidance on the software's features, installation, and usage. It includes sections on learning through examples, detailed documentation, and language references. The document serves as a resource for users to effectively utilize FreeFEM for solving partial differential equations.

Uploaded by

sayahdin.alfat
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

FreeFEM Documentation

Release 4.8

Frederic Hecht

Oct 27, 2021


In collaboration with:

i
ii
CONTENTS

1 Introduction 3
1.1 Version 4.5: new features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Installation guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.4 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.5 Citation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.6 Authors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.7 Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

2 Learning by Examples 25
2.1 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.2 Classification of partial differential equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.3 Membrane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.4 Heat Exchanger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.5 Acoustics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.6 Thermal Conduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.7 Irrotational Fan Blade Flow and Thermal effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.8 Pure Convection : The Rotating Hill . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.9 The System of elasticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.10 The System of Stokes for Fluids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.11 A projection algorithm for the Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.12 Newton Method for the Steady Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . 65
2.13 A Large Fluid Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
2.14 An Example with Complex Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
2.15 Optimal Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
2.16 A Flow with Shocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
2.17 Time dependent schema optimization for heat equations . . . . . . . . . . . . . . . . . . . . . . . . 82
2.18 Tutorial to write a transient Stokes solver in matrix form . . . . . . . . . . . . . . . . . . . . . . . . 85
2.19 Wifi Propagation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.20 Plotting in Matlab and Octave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

3 Documentation 99
3.1 Notations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
3.2 Mesh Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
3.3 Finite element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
3.4 Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
3.5 Algorithms & Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
3.6 Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
3.7 PETSc and SLEPc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
3.8 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

iii
3.9 Developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
3.10 ffddm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324

4 Language references 359


4.1 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
4.2 Global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
4.3 Quadrature formulae . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
4.4 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
4.5 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
4.6 I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
4.7 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
4.8 External libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437

5 Mathematical Models 523


5.1 Static problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
5.2 Elasticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
5.3 Non-linear static problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
5.4 Eigen value problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
5.5 Evolution problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
5.6 Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571
5.7 Variational Inequality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
5.8 Domain decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
5.9 Fluid-structure coupled problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591
5.10 Transmission problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
5.11 Free boundary problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
5.12 Non-linear elasticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
5.13 Compressible Neo-Hookean materials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
5.14 Whispering gallery modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615

6 Examples 621
6.1 Misc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
6.2 Mesh Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
6.3 Finite Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
6.4 Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
6.5 Algorithms & Optimizations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
6.6 Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662
6.7 Developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676

Bibliography 707

iv
FreeFEM Documentation, Release 4.8

CONTENTS 1
FreeFEM Documentation, Release 4.8

2 CONTENTS
CHAPTER

ONE

INTRODUCTION

FreeFEM is a partial differential equation solver for non-linear multi-physics systems in 1D, 2D, 3D and 3D border
domains (surface and curve).
Problems involving partial differential equations from several branches of physics, such as fluid-structure interactions,
require interpolations of data on several meshes and their manipulation within one program. FreeFEM includes a fast
interpolation algorithm and a language for the manipulation of data on multiple meshes.
FreeFEM is written in C++ and its language is a C++ idiom.

FreeFEM currently interfaces to the following libraries:

aggedright • METIS • SuperLU


• ParMETIS • TetGen
• ARPACK
• Mmg • PETSc
• BLAS
• mshmet • SLEPc
• OpenBLAS
• MUMPS • HTool
• FFTW 3.3.2
• NLopt 2.2.4 • HPDDM
• Ipopt 3.12.4
• ScaLAPACK • BemTool
• Gmm++ 4.2
• Scotch • ParMmg
• freeYams
• SuiteSparse

3
FreeFEM Documentation, Release 4.8

4 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

1.1 Version 4.5: new features

1.1.1 Release, binaries packages

• Since the version 4.5, the FreeFEM binary packages provides with a compiled PETSc library.
• FreeFEM is now interfaced with ParMmg.

1.1.2 New meshes and FEM border

After Surface FEM, Line FEM is possible with a new mesh type meshL, P0 P1 P2 P1dc FE, basic FEM, mesh generation.
This new development allows to treat a 1d problem, such as a problem described on a 3d curve.

Abstract about Line FEM in FreeFEM.


• new meshL type, refer to the section The type meshL in 3 dimension
– new type of surface mesh: meshL
– the functionalities on the meshL type, it is necessary to load the plugin ”msh3”.
– generator of meshL segment, define multi border and buildmesh function.
– basic transformation are avalaible: movemesh, trunc, extract, checkmesh, change, AddLayers, glue
of meshL.
It is possible to build the underlying meshL from a meshS with the function buildBdMesh:
ThS=buildBdMesh(ThS) builds the boundary domain associated to the meshS ThS and extract it by the
command meshL ThL=ThS. Gamma.
• new finite element space with curve finite element type
• FESpace P0 P1, P2, P1dc Lagrange finite elements and possible to add a custumed finite element with the
classical method (like a plugin).
• as in the standard 2d, 3d, surface 3d case, the variational problem associated to surface PDE can be defined by
using the keywords
– problem
– varf to access to matrix and RHS vector
– available operators are int1d, on and the operator int0d to define a Neumann boundary condition
• visualisation tools
– plot with plot of ffglut, medit meshes meshL and solutions
– 2d or 3d view, with in 3d the option to visualize the elememt Normals at element (touch ‘T’) and the
deformed domain according to it (touch ‘2’).
– loading, saving of meshes and solution at FreeFEM’s format
∗ “.mesh” mesh format file of Medit (P. Frey LJLL)
∗ “.msh” for mesh and “.sol” data solution at freefem format

1.1. Version 4.5: new features 5


FreeFEM Documentation, Release 4.8

∗ “.msh” data file of Gmsh (Mesh generator) (load “gmsh”)


∗ vtk format for meshes and solutions (load “iovtk” and use the “.vtu” extension)

1.1.3 Boundary Element Method

Allows to define and solve a 2d/3d BEM formulation and rebuild the associated potential. The document is in con-
struction.

1.2 Installation guide

To use FreeFEM, two installation methods are available: user access (binary package) and access developers (from the
source code). Follow the section corresponding to your type of installation.

Note: Since the version 4.5, FreeFEM relese provides with the last version of PETSc.

1.2.1 Using binary package

First, open the following web page download page and choose your platform: Linux, MacOS or Windows.

Note: Binary packages are available for Microsoft Windows, MacOS and some Linux distributions. Since the release
4.5, FreeFEM binaries provide with the current version of PETSc.

Install FreeFEM by double-clicking on the appropriate file. Under Linux and MacOS the install directory is one of
the following /usr/local/bin, /usr/local/share/freefem++, /usr/local/lib/ff++

Windows installation

Note: The windows package is build for Window 7 64bits. The support ended for all releases under Windows 32 bits
since the V4.

First download the windows installation executable, then double click to install FreeFEM. Install MSMPI for parallel
version under window64 MS MPI V10.1.2, and install both msmpisdk.msi and MSMpiSetup.exe.
In most cases just answer yes (or type return) to all questions.
Otherwise in the Additional Task windows, check the box “Add application directory to your system path.” This is
required otherwise the program ffglut.exe will not be found.
By now you should have two new icons on your desktop:
• FreeFem++ (VERSION).exe, the freefem++ application.

6 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

• FreeFem++ (VERSION) Examples, a link to the freefem++ examples folder.


where (VERSION) is the version of the files (for example 4.5).
By default, the installed files are in C:\Programs Files\FreeFem++. In this directory, you have all the .dll files
and other applications: FreeFem++-nw.exe, ffglut.exe, . . . The syntax for the command-line tools are the same
as those of FreeFem.exe.
To use FreeFEM binaries under Windows, two methods are possible:
• Use the FreeFEM launcher (launchff++.exe)
Warning: if you launch FreeFEM without filename script by double-clicking, your get a error due (it is bug of usage
GetOpenFileName in win64).
• In shell terminal (cmd, powershell, bash, . . . ):
– To launch sequential version:

1 C:\>"Program Files (x86)\FreeFem++\FreeFem++.exe" <mySequentialScript.edp>

– To launch parallel version:

1 C:\>"Program Files\Microsoft MPI\Bin\mpiexec.exe" -n <nbProcs> C:\>"Program Files␣


˓→(x86)\FreeFem++\FreeFem++-mpi.exe" <myParallelScript.edp>

macOS X installation

Download the macOS X binary version file, extract all the files by double clicking on the icon of the file, go the the
directory and put the FreeFem++.app application in the /Applications directory.
If you want terminal access to FreeFEM just copy the file FreeFem++ in a directory of your $PATH shell environment
variable.

Ubuntu installation

Note: The Debian package is built for Ubuntu 16.04

Beforehand, install the following dependances libraries using the apt tool:

1 sudo apt-get install libgsl-dev libhdf5-dev


2 liblapack-dev libopenmpi-dev freeglut3-dev

Download the package FreeFEM .deb, install it by the command

1 dpkg -i FreeFEM_VERSION_Ubuntu_withPETSc_amd64.deb

FreeFEM is directly available in your terminal by the command “FreeFem++”.

1.2. Installation guide 7


FreeFEM Documentation, Release 4.8

Arch AUR package

An up-to-date package of FreeFEM for Arch is available on the Archlinux user repository.
To install it:

1 git clone https://aur.archlinux.org/freefem++-git.git


2 cd freefem++-git
3 makepkg -si

Note: Thanks to Stephan Husmann

Fedora installation

Packages are available in the Fedora Repositories, and they are managed by the Fedora SciTech special interest group.
The packages are usually recent builds, but may not be the latest released version.
You can install them using the dnf tool, for both the serial and parallel (MPI) versions. :

1 sudo dnf install freefem++


2 sudo dnf install freefem++-openmpi
3 sudo dnf install freefem++-mpich

FreeFEM is directly available in your terminal by the command “FreeFem++”. To use the OpenMPI version, in your
terminal first load the OpenMPI module, for example using

1 module load mpi/openmpi-x86_64

and then the command “FreeFem++-mpi_openmpi” will be available in your terminal. To use the MPICH version, in
your terminal first load the MPICH module using

1 module load mpi/mpich-x86_64

and then the command “FreeFem++-mpi_mpich” will be available in your terminal.

1.2.2 Compiling source code

Various versions of FreeFEM are possible:


• sequential and without plugins (contains in 3rdparty)
• parallel with plugins (and with PETSc).

Note: We advise you to use the package manager for macOS Homebrew to get the different packages
required avalaible here

8 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

Compilation on OSX (>=10.13)

1. Install Xcode, Xcode Command Line tools and Xcode Additional Tools from the Apple website
2. Install gfortran from Homebrew

1 brew cask install gfortran

Note: If you have installed gcc via brew, gfortran comes with it and you do not need this line

3. To use FreeFEM parallel version, install openmpi or mpich

1 # to install openmpi
2 curl -L https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.1.tar.gz --
˓→output openmpi-4.0.1.tar.gz

3 tar xf openmpi-4.0.1
4 cd openmpi-4.0.1/
5 # to install mpich
6 curl -L http://www.mpich.org/static/downloads/3.3.2/mpich-3.3.2.tar.gz --output␣
˓→mpich-3.3.2.tar.gz

7 tar xf mpich-3.3.2
8 cd mpich-3.3.2

4 # with brew gcc gfortran compilers


5 ./configure CC=clang CXX=clang++ FC=gfortran-9 F77=gfortran-9 --prefix=/where/you/
˓→want/to/have/files/installed

7 # with LLVM gcc and brew gfortran compilers


8 ./configure CC=gcc-9 CXX=g++-9 FC=gfortran-9 F77=gfortran-9 --prefix=/where/you/
˓→want/to/have/files/installed

5 make -j<nbProcs>
6 make -j<nbProcs> install

4. Install the minimal libraries for FreeFEM

1 brew install m4 git flex bison

5. If you want build your own configure according your system, install autoconf and automake from Homebrew
(optional, see note in step 10)

1 brew install autoconf automake

6. To use FreeFEM with its plugins, install from Homebrew suitesparse, hdf5, cmake, wget

1 brew install suitesparse hdf5 cmake wget

7. Install gsl

1 curl -O http://mirror.cyberbits.eu/gnu/gsl/gsl-2.5.tar.gz
2 tar zxvf gsl-2.5.tar.gz
3 cd gsl-2.5
4 ./configure
(continues on next page)

1.2. Installation guide 9


FreeFEM Documentation, Release 4.8

(continued from previous page)


5 make -j<nbProcs>
6 make -j<nbProcs> install --prefix=/where/you/want/to/have/files/installed

8. Download the latest Git for Mac installer git and the FreeFEM source from the repository

1 git clone https://github.com/FreeFem/FreeFem-sources.git

9. Configure your source code

1 cd FreeFem-sources
2 autoreconf -i

Note: if your autoreconf version is too old, do tar zxvf AutoGeneratedFile.tar.gz

• following your compilers

3 // with brew gcc gfortran compilers


4 ./configure --enable-download CC=clang CXX=clang++ F77=gfortran-9
5 FC=gfortran-9 --prefix=/where/you/want/to/have/files/installed
6

7 // with LLVM gcc and brew gfortran compilers


8 ./configure --enable-download CC=clang CXX=clang++ F77=gfortran-9
9 FC=gfortran-9 --prefix=/where/you/want/to/have/files/installed

10. Download the 3rd party packages to use FreeFEM plugins

1 ./3rdparty/getall -a

Note: All the third party packages have their own licence

11. If you want use PETSc/SLEPc and HPDDM (High Performance Domain Decomposition Methods)

1 cd 3rdparty/ff-petsc
2 make petsc-slepc // add SUDO=sudo if your installation directory is the␣
˓→default /usr/local

3 cd -
4 ./reconfigure

12. Build your FreeFEM library and executable

1 make -j<nbProcs>
2 make -j<nbProcs> check

Note: make check is optional, but advised to check the validity of your FreeFEM build

13. Install the FreeFEM apllication make install // add SUDO=sudo might be necessary

Note: it isn’t necessary to execute this last command, FreeFEM executable is avalaible here

10 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

your_installation/src/nw/FreeFem++ and mpi executable here your_installation/src/mpi/ff-mpirun.

Compilation on Ubuntu

1. Install the following packages on your system


1 sudo apt-get update && sudo apt-get upgrade
2 sudo apt-get install cpp freeglut3-dev g++ gcc gfortran \
3 m4 make patch pkg-config wget python unzip \
4 liblapack-dev libhdf5-dev libgsl-dev \
5 autoconf automake autotools-dev bison flex gdb git cmake
6

7 # mpich is required for the FreeFEM parallel computing version


8 sudo apt-get install mpich

Warning: In the oldest distribution of Ubuntu, libgsl-dev does not exist, use libgsl2-dev instead

2. Download FreeFEM source from the repository


1 git clone https://github.com/FreeFem/FreeFem-sources.git

3. Autoconf
1 cd FreeFem-sources
2 autoreconf -i

Note: if your autoreconf version is too old, do tar zxvf AutoGeneratedFile.tar.gz

4. Configure
1 ./configure --enable-download --enable-optim
2 --prefix=/where/you/want/to/have/files/installed

Note: To see all the options, type ./configure --help

5. Download the 3rd party packages


1 ./3rdparty/getall -a

Note: All the third party packages have their own licence

6. If you want use PETSc/SLEPc and HPDDM (High Performance Domain Decomposition Methods) for massively
parallel computing

1 cd 3rdparty/ff-petsc
2 make petsc-slepc // add SUDO=sudo if your installation directory is the default /
˓→usr/local
(continues on next page)

1.2. Installation guide 11


FreeFEM Documentation, Release 4.8

(continued from previous page)


3 cd -
4 ./reconfigure

7. Build your FreeFEM library and executable

1 make -j<nbProcs>
2 make -j<nbProcs> check

Note: make check is optional, but advised to check the validity of your FreeFEM build

8. Install the executable

1 make install

Note: it isn’t necessary to execute this last command, FreeFEM executable is avalaible here
your_installation/src/nw/FreeFem++ and mpi executable here your_installation/src/mpi/ff-mpirun

Compilation on Arch Linux

Warning: As Arch is in rolling release, the following information can be quickly outdated !

Warning: FreeFEM fails to compile using the newest version of gcc 8.1.0, use an older one instead.

1. Install the following dependencies:

1 pacman -Syu
2 pacman -S git openmpi gcc-fortran wget python
3 freeglut m4 make patch gmm
4 blas lapack hdf5 gsl fftw arpack suitesparse
5 gnuplot autoconf automake bison flex gdb
6 valgrind cmake texlive-most

2. Download the FreeFEM source from the repository

1 git clone https://github.com/FreeFem/FreeFem-sources.git

3. Autoconf

1 cd FreeFem-sources
2 autoreconf -i

4. Configure

1 ./configure --enable-download --enable-optim

12 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

Note: To see all the options, type ./configure --help

5. Download the packages

1 ./3rdparty/getall -a

Note: All the third party packages have their own licence

6. If you want use HPDDM (High Performance Domain Decomposition Methods) for massively parallel computing,
install PETSc/SLEPc

1 cd 3rdparty/ff-petsc
2 make petsc-slepc SUDO=sudo
3 cd -
4 ./reconfigure

7. Compile the FreeFEM source

1 make

Note: If your computer has many threads, you can run make in parallel using make -j16 for 16 threads, for
example.

Note: Optionally, check the compilation with make check

8. Install the FreeFEM application

1 sudo make install

Compilation on Fedora

1. Install the following packages on your system

1 sudo dnf update


2 sudo dnf install freeglut-devel gcc-gfortran gcc-c++ gcc \
3 m4 make wget python2 python3 unzip \
4 lapack-devel hdf5-devel gsl gsl-devel \
5 autoconf automake bison flex gdb git cmake
6

7 # MPICH or OpenMPI is required for the FreeFEM parallel computing version


8 sudo dnf install mpich-devel
9 sudo dnf install openmpi-devel
10

11 # Then load one of the modules, for example


12 module load mpi/mpich-x86_64
13 # or
14 module load mpi/openmpi-x86_64

1.2. Installation guide 13


FreeFEM Documentation, Release 4.8

2. Download FreeFEM source from the repository

1 git clone https://github.com/FreeFem/FreeFem-sources.git

3. Autoconf

1 cd FreeFem-sources
2 autoreconf -i

Note: if your autoreconf version is too old, do tar zxvf AutoGeneratedFile.tar.gz

4. Configure

1 ./configure --enable-download --enable-optim


2 --prefix=/where/you/want/to/have/files/installed

Note: To see all the options, type ./configure --help

5. Download the 3rd party packages

1 ./3rdparty/getall -a

Note: All the third party packages have their own licence

6. If you want use PETSc/SLEPc and HPDDM (High Performance Domain Decomposition Methods) for massively
parallel computing

1 cd 3rdparty/ff-petsc
2 make petsc-slepc // add SUDO=sudo if your installation directory is the default /
˓→usr/local

3 cd -
4 ./reconfigure

7. Build your FreeFEM library and executable

1 make -j<nbProcs>
2 make -j<nbProcs> check

Note: make check is optional, but advised to check the validity of your FreeFEM build

8. Install the executable

1 make install

Note: it isn’t necessary to execute this last command, FreeFEM executable is avalaible here
your_installation/src/nw/FreeFem++ and mpi executable here your_installation/src/mpi/ff-mpirun

14 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

Compilation on Linux with Intel software tools

Follow the guide

Compilation on Windows

Warning: The support ended for all releases under Windows 32 bits since the V4. We assume your development
machine is 64-bit, and you want your compiler to target 64-bit windows by default.

1. Install the Microsoft MPI v10.1.2 (archived) (msmpisdk.msi and MSMpiSetup.exe)


2. Download msys2-x86_64-latest.exe (x86_64 version) and run it.
3. Install the version control system Git for Windows
4. In the MSYS2 shell, execute the following. Hint: if you right click the title bar, go to Options -> Keys and tick
“Ctrl+Shift+letter shortcuts” you can use Ctrl+Shift+V to paste in the MSYS shell.

1 pacman -Syuu

Close the MSYS2 shell once you’re asked to. There are now 3 MSYS subsystems installed: MSYS2, MinGW32
and MinGW64. They can respectively be launched from C:devmsys64msys2.exe, C:devmsys64mingw32.exe and
C:devmsys64mingw64.exe Reopen MSYS2 (doesn’t matter which version, since we’re merely installing packages).
Repeatedly run the following command until it says there are no further updates. You might have to restart your shell
again.

1 pacman -Syuu

5. Now that MSYS2 is fully up-to-date, install the following dependancies


• for 64 bit systems:

1 pacman -S autoconf make automake-wrapper bison git \


2 mingw-w64-x86_64-freeglut mingw-w64-x86_64-toolchain \
3 mingw-w64-x86_64-openblas patch python perl pkg-config pkgfile \
4 rebase tar time tzcode unzip which mingw-w64-x86_64-libmicroutils \
5 --ignore mingw-w64-x86_64-gcc-ada --ignore mingw-w64-x86_64-gcc-objc \
6 --ignore mingw-w64-x86_64-gdb mingw-w64-x86_64-cmake --noconfirm

• for 32 bit systems (FreeFEM lower than version 4):

1 pacman -S autoconf automake-wrapper bash bash-completion \


2 bison bsdcpio bsdtar bzip2 coreutils curl dash file filesystem \
3 findutils flex gawk gcc gcc-fortran gcc-libs grep gzip inetutils \
4 info less lndir make man-db git mingw-w64-i686-freeglut \
5 mingw-w64-i686-toolchain mingw-w64-i686-gsl mingw-w64-i686-hdf5 \
6 mingw-w64-i686-openblas mintty msys2-keyring msys2-launcher-git \
7 msys2-runtime ncurses pacman pacman-mirrors pactoys-git patch pax-git \
8 perl pkg-config pkgfile rebase sed tar tftp-hpa time tzcode unzip \
9 util-linux which

6. Open a MingW64 terminal (or MingW32 for old 32 bit FreeFEM version) and compile the FreeFEM source

1.2. Installation guide 15


FreeFEM Documentation, Release 4.8

1 git clone https://github.com/FreeFem/FreeFem-sources


2 cd FreeFem-sources
3 autoreconf -i
4 ./configure --enable-generic --enable-optim \
5 --enable-download --enable-maintainer-mode \
6 CXXFLAGS=-mtune=generic CFLAGS=-mtune=generic \
7 FFLAGS=-mtune=generic --enable-download --disable-hips
8 --prefix=/where/you/want/to/have/files/installed

7. If you want use HPDDM (High Performance Domain Decomposition Methods) for massively parallel computing,
install PETSc/SLEPc

1 cd 3rdparty/ff-petsc
2 make petsc-slepc SUDO=sudo
3 cd -
4 ./reconfigure

8. Download the 3rd party packages and build your FreeFEM library and executable

1 ./3rdparty/getall -a
2 make
3 make check
4 make install

Note: The FreeFEM executable (and some other like ffmedit, . . . ) are in C:\msys64\mingw64\bin (or
C:\msys32\mingw32\bin).

1.2.3 Environment variables and init file

FreeFEM reads a user’s init file named freefem++.pref to initialize global variables: verbosity, includepath,
loadpath.

Note: The variable verbosity changes the level of internal printing (0: nothing unless there are syntax errors, 1:
few, 10: lots, etc. . . . ), the default value is 2.
The included files are found in the includepath list and the load files are found in the loadpath list.

The syntax of the file is:

1 verbosity = 5
2 loadpath += "/Library/FreeFem++/lib"
3 loadpath += "/Users/hecht/Library/FreeFem++/lib"
4 includepath += "/Library/FreeFem++/edp"
5 includepath += "/Users/hecht/Library/FreeFem++/edp"
6 # This is a comment
7 load += "funcTemplate"
8 load += "myfunction"
9 load += "MUMPS_seq"

The possible paths for this file are

16 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

• under Unix and MacOs

1 /etc/freefem++.pref
2 $(HOME)/.freefem++.pref
3 freefem++.pref

• under windows

1 freefem++.pref

We can also use shell environment variables to change verbosity and the search rule before the init files.

1 export FF_VERBOSITY=50
2 export FF_INCLUDEPATH="dir;;dir2"
3 export FF_LOADPATH="dir;;dir3"

Note: The separator between directories must be “;” and not “:” because “:” is used under Windows.

Note: To show the list of init of FreeFEM , do

1 export FF_VERBOSITY=100;
2 ./FreeFem++-nw

1.2.4 Coloring Syntax FreeFem++

Atom

In order to get the syntax highlighting in Atom, you have to install the FreeFEM language support.
You can do it directly in Atom: Edit -> Preferences -> Install, and search for language-freefem-offical.
To launch scripts directly from Atom, you have to install the atom-runner package. Once installed, modify the Atom
configuration file (Edit -> Config. . . ) to have something like that:

1 "*":
2 ...
3

4 runner:
5 extensions:
6 edp: "FreeFem++"
7 scopes:
8 "Freefem++": "FreeFem++"

Reboot Atom, and use Alt+R to run a FreeFem++ script.

1.2. Installation guide 17


FreeFEM Documentation, Release 4.8

Gedit

In order to get the syntax highlighting in Gedit, you have to downlaod the Gedit parser and copy it in /usr/share/
gtksourceview-3.0/language-specs/.

Textmate 2, an editor under macOS

To use the coloring FreeFEM syntax with the Textmate 2 editor on Mac 10.7 or better, download from macromates.com
and download the textmate freefem++ syntax here (version june 2107). To install this parser, unzip Textmate2-ff++.zip
and follow the explanation given in file How_To.rtf.
rom www.freefem.org/ff++/Textmate2-ff++.zip (version june 2107) unzip Textmate2-

Notepad++,an editor under windows

Read and follow the instruction, FREEFEM++ COLOR SYNTAX OF WINDOWS .

Emacs editor

For emacs editor you can download ff++-mode.el .

1.3 Download

1.3.1 Latest binary packages

FreeFEM v4.6 runs under macOS, Ubuntu, and 64-bit Windows.

Operating System FreeFEM Version Size Date


macOS 10.10.5 or higher 4.5 412 MB Feb 11, 2020
Ubuntu 16.04 or higher 4.6 212 MB Mar 02, 2020
64-bit Windows | 4.6 | 185 MB Mar 02, 2020
Docker image 4.6 487 MB Mar 02, 2020
Source 4.6 4.6 12.4 MB Mar 02, 2020
previous releases - - -

The source code is available on the FreeFEM GitHub Repository.

Note: The support ended for all releases under Windows 32 bits.

18 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

1.3.2 Syntax highlighters

Lexer type Version Description


Emacs 0.3 freefem-mode.el
Textmate 2 1.0 FreeFem.tmbundle
Gedit 1.0 ffpp.lang
Atom 0.3 language-freefem or via the Atom package manager
Pygments 1.0 freefem.py
Vim 0.1 edp.vim

1.4 History

The project has evolved from MacFem, PCfem, written in Pascal. The first C version lead to freefem
3.4; it offered mesh adaptivity on a single mesh only.
A thorough rewriting in C++ led to freefem+ (freefem+ 1.2.10 was its last release), which included
interpolation over multiple meshes (functions defined on one mesh can be used on any other mesh); this
software is no longer maintained but is still in use because it handles a problem description using the strong
form of the PDEs. Implementing the interpolation from one unstructured mesh to another was not easy
because it had to be fast and non-diffusive; for each point, one had to find the containing triangle. This
is one of the basic problems of computational geometry (see [PREPARATA1985] for example). Doing it
in a minimum number of operations was the challenge. Our implementation is 𝒪(𝑛𝑙𝑜𝑔𝑛) and based on a
quadtree. This version also grew out of hand because of the evolution of the template syntax in C++.
We have been working for a few years now on FreeFEM , entirely re-written again in C++ with a thorough
usage of template and generic programming for coupled systems of unknown size at compile time. Like
all versions of freefem, it has a high level user friendly input language which is not too far from the
mathematical writing of the problems.
The freefem language allows for a quick specification of any partial differential system of equa-
tions. The language syntax of FreeFEM is the result of a new design which makes use of the
STL [STROUSTRUP2000], templates, and bison for its implementation; more details can be found in
[HECHT2002]. The outcome is a versatile software in which any new finite elements can be included
in a few hours; but a recompilation is then necessary. Therefore the library of finite elements available
in FreeFEM will grow with the version number and with the number of users who program more new
elements. So far we have discontinuous 𝑃0 elements,linear 𝑃1 and quadratic 𝑃2 Lagrangian elements,
discontinuous 𝑃1 and Raviart-Thomas elements and a few others like bubble elements.

The development of FreeFEM through more than 30 years

1987
MacFem/PCFem the old ones (O. Pironneau in Pascal) no free.

1.4. History 19
FreeFEM Documentation, Release 4.8

1992
FreeFem rewrite in C++ (P1,P0 one mesh ) O. Pironneau, D. Bernardi, F.Hecht (mesh adaptation , bamg)
, C. Prudhomme .
1996
FreeFem+ rewrite in C++ (P1,P0 more mesh) O. Pironneau, D. Bernardi, F.Hecht (algebra of function).
1998
FreeFem++ rewrite with an other finite element kernel and an new language F. Hecht, O. Pironneau,
K.Ohtsuka.
1999
FreeFem 3d (S. Del Pino), a fist 3d version base on fictitious domaine method.
2008
FreeFem++ v3 use a new finite element kernel multidimensionnels: 1d,2d,3d. . .
2014
FreeFem++ v3.34 parallel version
2017
FreeFem++ v3.57 parallel version
2018
FreeFem++ v4: New matrix type, Surface element, New Parallel tools . . .

1.5 Citation

1.5.1 If you use FreeFEM, please cite the following reference in your work:

BibTeX

1 @article{MR3043640,
2 AUTHOR = {Hecht, F.},
3 TITLE = {New development in FreeFem++},
4 JOURNAL = {J. Numer. Math.},
5 FJOURNAL = {Journal of Numerical Mathematics},
6 VOLUME = {20}, YEAR = {2012},
7 NUMBER = {3-4}, PAGES = {251--265},
8 ISSN = {1570-2820},
9 MRCLASS = {65Y15},
10 MRNUMBER = {3043640},
11 URL = {https://freefem.org/}
12 }

20 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

APA

1 Hecht, F. (2012). New development in FreeFem++. Journal of numerical mathematics, 20(3-


˓→4), 251-266.

ISO690

1 HECHT, Frédéric. New development in FreeFem++. Journal of numerical mathematics, 2012,␣


˓→vol. 20, no 3-4, p. 251-266.

MLA

1 Hecht, Frédéric. "New development in FreeFem++." Journal of numerical mathematics 20.3-4␣


˓→(2012): 251-266.

1.6 Authors

Frédéric Hecht
Professor at Laboratoire Jacques Louis Lions (LJLL), Sorbonne University, Paris
[email protected]
https://www.ljll.math.upmc.fr/hecht/

Sylvain Auliac
Former PhD student at LJLL, optimization interface with nlopt, ipopt, cmaes, . . .
https://www.ljll.math.upmc.fr/auliac/

Olivier Pironneau
Professor of numerical analysis at the Paris VI university and at LJLL, numerical methods in fluid
Member of the Institut Universitaire de France and Academie des Sciences
https://www.ljll.math.upmc.fr/pironneau/

Jacques Morice
Former Post-Doc at LJLL, three dimensions mesh generation and coupling with medit

Antoine Le Hyaric
CNRS research engineer at Laboratoire Jacques Louis Lions, expert in software engineering for scientific applica-
tions, electromagnetics simulations, parallel computing and three-dimensionsal visualization
https://www.ljll.math.upmc.fr/lehyaric/

Kohji Ohtsuka
Professor at Hiroshima Kokusai Gakuin University, Japan and chairman of the World Scientific and Engineering
Academy and Society, Japan. Fracture dynamic, modeling and computing
https://sites.google.com/a/comfos.org/comfos/

1.6. Authors 21
FreeFEM Documentation, Release 4.8

Pierre-Henri Tournier
CNRS research engineer at Laboratoire Jacques Louis Lions (LJLL), Sorbonne University, Paris

Pierre Jolivet
CNRS researcher, MPI interface with PETSc, HPDDM, . . .
http://jolivet.perso.enseeiht.fr/

Frédéric Nataf
CNRS senior researcher at Laboratoire Jacques Louis Lions (LJLL), Sorbonne University, Paris
https://www.ljll.math.upmc.fr/nataf/

Simon Garnotel
Reasearch engineer at Airthium
https://github.com/sgarnotel

Karla Pérez
Developer, Airthium internship
https://github.com/karlaprzbr

Loan Cannard
Web designer, Airthium internship
https://www.linkedin.com/in/loancannard

And all the dedicated Github contributors

1.7 Contributing

1.7.1 Bug report

Concerning the FreeFEM documentation

Open an Issue on FreeFem-doc repository.

Concerning the FreeFEM compilation or usage

Open an Issue on FreeFem-sources repository.

22 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8

1.7.2 Improve content

Ask one of the contributors for Collaborator Access or make a Pull Request.

1.7. Contributing 23
FreeFEM Documentation, Release 4.8

24 Chapter 1. Introduction
CHAPTER

TWO

LEARNING BY EXAMPLES

The FreeFEM language is typed, polymorphic and reentrant with macro generation.
Every variable must be typed and declared in a statement, that is separated from the next by a semicolon ;.
The FreeFEM language is a C++ idiom with something that is more akin to LaTeX.
For the specialist, one key guideline is that FreeFEM rarely generates an internal finite element array, this was adopted
for speed and consequently FreeFEM could be hard to beat in terms of execution speed, except for the time lost in the
interpretation of the language (which can be reduced by a systematic usage of varf and matrix instead of problem).
The Development Cycle: Edit–Run/Visualize–Revise
Many examples and tutorials are given there after and in the examples section. It is better to study them and learn by
example.
If you are a beginner in the finite element method, you may also have to read a book on variational formulations.
The development cycle includes the following steps:
Modeling: From strong forms of PDE to weak forms, one must know the variational formulation to use FreeFEM;
one should also have an eye on the reusability of the variational formulation so as to keep the same internal matrices; a
typical example is the time dependent heat equation with an implicit time scheme: the internal matrix can be factorized
only once and FreeFEM can be taught to do so.
Programming: Write the code in FreeFEM language using a text editor such as the one provided in your integrated
environment.
Run: Run the code (here written in file mycode.edp). That can also be done in terminal mode by :

1 FreeFem++ mycode.edp

Visualization: Use the keyword plot directly in mycode.edp to display functions while FreeFEM is running. Use
the plot-parameter wait=1 to stop the program at each plot.
Debugging: A global variable debug (for example) can help as in wait=true to wait=false.

1 bool debug = true;


2

3 border a(t=0, 2.*pi){x=cos(t); y=sin(t); label=1;};


4 border b(t=0, 2.*pi){x=0.8+0.3*cos(t); y=0.3*sin(t); label=2;};
5

6 plot(a(50) + b(-30), wait=debug); //plot the borders to see the intersection


(continues on next page)

25
FreeFEM Documentation, Release 4.8

(continued from previous page)


7 //so change (0.8 in 0.3 in b)
8 //if debug == true, press Enter to continue
9

10 mesh Th = buildmesh(a(50) + b(-30));


11 plot(Th, wait=debug); //plot Th then press Enter
12

13 fespace Vh(Th,P2);
14 Vh f = sin(pi*x)*cos(pi*y);
15 Vh g = sin(pi*x + cos(pi*y));
16

17 plot(f, wait=debug); //plot the function f


18 plot(g, wait=debug); //plot the function g

Changing debug to false will make the plots flow continuously. Watching the flow of graphs on the screen (while
drinking coffee) can then become a pleasant experience.
Error management
Error messages are displayed in the console window. They are not always very explicit because of the template structure
of the C++ code (we did our best!). Nevertheless they are displayed at the right place. For example, if you forget
parenthesis as in:

1 bool debug = true;


2 mesh Th = square(10,10;
3 plot(Th);

then you will get the following message from FreeFEM:

1 2 : mesh Th = square(10,10;
2 Error line number 2, in file bb.edp, before token ;
3 parse error
4 current line = 2
5 syntax error
6 current line = 2
7 Compile error : syntax error
8 line number :2, ;
9 error Compile error : syntax error
10 line number :2, ;
11 code = 1 mpirank: 0

If you use the same symbol twice as in:

1 real aaa = 1;
2 real aaa;

then you will get the message:

1 2 : real aaa; The identifier aaa exists


2 the existing type is <Pd>
3 the new type is <Pd>

If you find that the program isn’t doing what you want you may also use cout to display in text format on the console
window the value of variables, just as you would do in C++.
The following example works:

26 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

1 ...
2 fespace Vh(Th, P1);
3 Vh u;
4 cout << u;
5 matrix A = a(Vh, Vh);
6 cout << A;

Another trick is to comment in and out by using // as in C++. For example:

1 real aaa =1;


2 // real aaa;

2.1 Getting started

For a given function 𝑓 (𝑥, 𝑦), find a function 𝑢(𝑥, 𝑦) satisfying :

−∆𝑢(𝑥, 𝑦) = 𝑓 (𝑥, 𝑦) for all (𝑥, 𝑦) in Ω


(2.1)
𝑢(𝑥, 𝑦) =0 for all (𝑥, 𝑦) on 𝜕Ω
𝜕2𝑢 𝜕2𝑢
Here 𝜕Ω is the boundary of the bounded open set Ω ⊂ R2 and ∆𝑢 = 𝜕𝑥2 + 𝜕𝑦 2 .

We will compute 𝑢 with 𝑓 (𝑥, 𝑦) = 𝑥𝑦 and Ω the unit disk. The boundary 𝐶 = 𝜕Ω is defined as:

𝐶 = {(𝑥, 𝑦)| 𝑥 = cos(𝑡), 𝑦 = sin(𝑡), 0 ≤ 𝑡 ≤ 2𝜋}

Note: In FreeFEM, the domain Ω is assumed to be described by the left side of its boundary.

The following is the FreeFEM program which computes 𝑢:

1 // Define mesh boundary


2 border C(t=0, 2*pi){x=cos(t); y=sin(t);}
3

4 // The triangulated domain Th is on the left side of its boundary


5 mesh Th = buildmesh(C(50));
6

7 // The finite element space defined over Th is called here Vh


8 fespace Vh(Th, P1);
9 Vh u, v;// Define u and v as piecewise-P1 continuous functions
10

11 // Define a function f
12 func f= x*y;
13

14 // Get the clock in second


15 real cpu=clock();
16

17 // Define the PDE


18 solve Poisson(u, v, solver=LU)
19 = int2d(Th)( // The bilinear part
20 dx(u)*dx(v)
21 + dy(u)*dy(v)
(continues on next page)

2.1. Getting started 27


FreeFEM Documentation, Release 4.8

(a) Mesh Th by buildmesh(C(50)) (b) Isovalue by plot(u)

Fig. 2.1: Poisson’s equation

(continued from previous page)


22 )
23 - int2d(Th)( // The right hand side
24 f*v
25 )
26 + on(C, u=0); // The Dirichlet boundary condition
27

28 // Plot the result


29 plot(u);
30

31 // Display the total computational time


32 cout << "CPU time = " << (clock()-cpu) << endl;

As illustrated in Fig. 2.1b, we can see the isovalue of 𝑢 by using FreeFEM plot command (see line 29 above).

Note: The qualifier solver=LU (line 18) is not required and by default a multi-frontal LU is used.
The lines containing clock are equally not required.

Tip: Note how close to the mathematics FreeFEM language is.


Lines 19 to 24 correspond to the mathematical variational equation:
∫︁ ∫︁
𝜕𝑢 𝜕𝑣 𝜕𝑢 𝜕𝑣
( + )d𝑥d𝑦 = 𝑓 𝑣d𝑥d𝑦
𝑇ℎ 𝜕𝑥 𝜕𝑥 𝜕𝑦 𝜕𝑦 𝑇ℎ

for all 𝑣 which are in the finite element space 𝑉ℎ and zero on the boundary 𝐶.

Tip: Change P1 into P2 and run the program.

28 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

This first example shows how FreeFEM executes with no effort all the usual steps required by the finite element method
(FEM). Let’s go through them one by one.
On the line 2:
∑︀𝐽
The boundary Γ is described analytically by a parametric equation for 𝑥 and for 𝑦. When Γ = 𝑗=0 Γ𝑗 then each
curve Γ𝑗 must be specified and crossings of Γ𝑗 are not allowed except at end points.
The keyword label can be added to define a group of boundaries for later use (boundary conditions for instance).
Hence the circle could also have been described as two half circle with the same label:

1 border Gamma1(t=0, pi){x=cos(t); y=sin(t); label=C};


2 border Gamma2(t=pi, 2.*pi){x=cos(t); y=sin(t); label=C};

Boundaries can be referred to either by name (Gamma1 for example) or by label (C here) or even by its internal number
here 1 for the first half circle and 2 for the second (more examples are in Meshing Examples).
On the line 5
The triangulation 𝒯ℎ of Ω is automatically generated by buildmesh(C(50)) using 50 points on C as in Fig. 2.1a.
The domain is assumed to be on the left side of the boundary which is implicitly oriented by the parametrization. So
an elliptic hole can be added by typing:

1 border C(t=2.*pi, 0){x=0.1+0.3*cos(t); y=0.5*sin(t);};

If by mistake one had written:

1 border C(t=0, 2.*pi){x=0.1+0.3*cos(t); y=0.5*sin(t);};

then the inside of the ellipse would be triangulated as well as the outside.

Note: Automatic mesh generation is based on the Delaunay-Voronoi algorithm. Refinement of the mesh are done by
increasing the number of points on Γ, for example buildmesh(C(100)), because inner vertices are determined by the
density of points on the boundary.
Mesh adaptation can be performed also against a given function f by calling adaptmesh(Th,f).

Now the name 𝒯ℎ (Th in FreeFEM) refers to the family {𝑇𝑘 }𝑘=1,··· ,𝑛𝑡 of triangles shown in Fig. 2.1a.
Traditionally ℎ refers to the mesh size, 𝑛𝑡 to the number of triangles in 𝒯ℎ and 𝑛𝑣 to the number of vertices, but it is
seldom that we will have to use them explicitly.
If Ω is not a polygonal domain, a “skin” remains between the exact domain Ω and its approximation Ωℎ = ∪𝑛𝑘=1
𝑡
𝑇𝑘 .
However, we notice that all corners of Γℎ = 𝜕Ωℎ are on Γ.
On line 8:
A finite element space is, usually, a space of polynomial functions on elements, triangles here only, with certain match-
ing properties at edges, vertices etc. Here fespace Vh(Th, P1) defines 𝑉ℎ to be the space of continuous functions
which are affine in 𝑥, 𝑦 on each triangle of 𝑇ℎ .
As it is a linear vector space of finite dimension, basis can be found. The canonical basis is made of functions, called
the hat function 𝜑𝑘 , which are continuous piecewise affine and are equal to 1 on one vertex and 0 on all others. A
typical hat function is shown on Fig. 2.2b.

Note: The easiest way to define


∑︀ 𝜑𝑘 is by∑︀
making use of the barycentric coordinates 𝜆𝑖 (𝑥, 𝑦), 𝑖 = 1, 2, 3 of a point
𝑞 = (𝑥, 𝑦) ∈ 𝑇 , defined by 𝑖 𝜆𝑖 = 1, 𝑞 𝑖 = ⃗𝑞 where 𝑞 𝑖 , 𝑖 = 1, 2, 3 are the 3 vertices of 𝑇 . Then it is easy to
𝑖 𝜆𝑖 ⃗

2.1. Getting started 29


FreeFEM Documentation, Release 4.8

(b) Graph of 𝜑1 (left) and 𝜑6 (right)

(a) mesh Th

Fig. 2.2: Hat functions

see that the restriction of 𝜑𝑘 on 𝑇 is precisely 𝜆𝑘 .

Then:
{︃ ⃒ 𝑀
}︃
⃒ ∑︁
𝑉ℎ (𝒯ℎ , 𝑃1 ) = 𝑤(𝑥, 𝑦) ⃒ 𝑤(𝑥, 𝑦) = 𝑤𝑘 𝜑𝑘 (𝑥, 𝑦), 𝑤𝑘 are real numbers (2.2)


𝑘=1

where 𝑀 is the dimension of 𝑉ℎ , i.e. the number of vertices. The 𝑤𝑘 are called the degrees of freedom of 𝑤 and 𝑀
the number of degree of freedom.
It is said also that the nodes of this finite element method are the vertices.
Setting the problem
On line 9, Vh u, v declares that 𝑢 and 𝑣 are approximated as above, namely:
𝑀
∑︁−1
𝑢(𝑥, 𝑦) ≃ 𝑢ℎ (𝑥, 𝑦) = 𝑢𝑘 𝜑𝑘 (𝑥, 𝑦) (2.3)
𝑘=0

On the line 12, the right hand side f is defined analytically using the keyword func.
Line 18 to 26 define the bilinear form of equation (2.1) and its Dirichlet boundary conditions.
This variational formulation is derived by multiplying (2.1) by 𝑣(𝑥, 𝑦) and integrating the result over Ω:
∫︁ ∫︁
− 𝑣∆𝑢 d𝑥d𝑦 = 𝑣𝑓 d𝑥d𝑦
Ω Ω

Then, by Green’s formula, the problem is converted into finding 𝑢 such that

𝑎(𝑢, 𝑣) − ℓ(𝑓, 𝑣) = 0 ∀𝑣 satisfying 𝑣 = 0 on 𝜕Ω.

with:
= ∫︀Ω ∇𝑢 · ∇𝑣 d𝑥d𝑦
∫︀
𝑎(𝑢, 𝑣)
(2.4)
ℓ(𝑓, 𝑣) = Ω 𝑓 𝑣 d𝑥d𝑦

30 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

In FreeFEM the Poisson problem can be declared only as in:

1 Vh u,v; problem Poisson(u,v) = ...

and solved later as in:

1 Poisson; //the problem is solved here

or declared and solved at the same time as in:

1 Vh u,v; solve Poisson(u,v) = ...

and (2.4) is written with dx(u) = 𝜕𝑢/𝜕𝑥, dy(u) = 𝜕𝑢/𝜕𝑦 and:


∫︁
∇𝑢 · ∇𝑣 d𝑥d𝑦 −→ int2d(Th)( dx(u)*dx(v) + dy(u)*dy(v) )
Ω
∫︁
𝑓 𝑣 d𝑥d𝑦 −→ int2d(Th)( f*v ) (Notice here, 𝑢 is unused)
Ω

Warning: In FreeFEM bilinear terms and linear terms should not be under the same integral indeed to
construct the linear systems FreeFEM finds out which integral contributes to the bilinear form by checking if both
terms, the unknown (here u) and test functions (here v) are present.

Solution and visualization


On line 15, the current time in seconds is stored into the real-valued variable cpu.
Line 18, the problem is solved.
Line 29, the visualization is done as illustrated in Fig. 2.1b.
(see Plot for zoom, postscript and other commands).
Line 32, the computing time (not counting graphics) is written on the console. Notice the C++-like syntax; the user
needs not study C++ for using FreeFEM, but it helps to guess what is allowed in the language.
Access to matrices and vectors
Internally FreeFEM will solve a linear system of the type
𝑀
∑︁−1 ∫︁
𝐴𝑖𝑗 𝑢𝑗 − 𝐹𝑖 = 0, 𝑖 = 0, · · · , 𝑀 − 1; 𝐹𝑖 = 𝑓 𝜑𝑖 d𝑥d𝑦 (2.5)
𝑗=0 Ω

which is found by using (2.3) and replacing 𝑣 by 𝜑𝑖 in (2.4). The Dirichlet conditions are implemented by penalty,
namely by setting 𝐴𝑖𝑖 = 1030 and 𝐹𝑖 = 1030 * 0 if 𝑖 is a boundary degree of freedom.

Note: The number 1030 is called tgv (très grande valeur or very high value in english) and it is generally possible to
change this value, see the item :freefem`solve, tgv=`

The matrix 𝐴 = (𝐴𝑖𝑗 ) is called stiffness matrix. If the user wants to access 𝐴 directly he can do so by using (see section
Variational form, Sparse matrix, PDE data vector for details).

1 varf a(u,v)
2 = int2d(Th)(
3 dx(u)*dx(v)
(continues on next page)

2.1. Getting started 31


FreeFEM Documentation, Release 4.8

(continued from previous page)


4 + dy(u)*dy(v)
5 )
6 + on(C, u=0)
7 ;
8 matrix A = a(Vh, Vh); //stiffness matrix

The vector 𝐹 in (2.5) can also be constructed manually:

1 varf l(unused,v)
2 = int2d(Th)(
3 f*v
4 )
5 + on(C, unused=0)
6 ;
7 Vh F;
8 F[] = l(0,Vh); //F[] is the vector associated to the function F

The problem can then be solved by:

1 u[] = A^-1*F[]; //u[] is the vector associated to the function u

Note: Here u and F are finite element function, and u[] and F[] give the array of value associated (u[] ≡
(𝑢𝑖 )𝑖=0,...,𝑀 −1 and F[] ≡ (𝐹𝑖 )𝑖=0,...,𝑀 −1 ).
So we have:
𝑀
∑︁−1 𝑀
∑︁−1
u(𝑥, 𝑦) = u[][𝑖]𝜑𝑖 (𝑥, 𝑦), F(𝑥, 𝑦) = F[][𝑖]𝜑𝑖 (𝑥, 𝑦)
𝑖=0 𝑖=0

where 𝜑𝑖 , 𝑖 = 0..., , 𝑀 − 1 are the basis functions of Vh like in equation :eq: equation3, and 𝑀 = Vh.ndof is the
number of degree of freedom (i.e. the dimension of the space Vh).

The linear system (2.5) is solved by UMFPACK unless another option is mentioned specifically as in:

1 Vh u, v;
2 problem Poisson(u, v, solver=CG) = int2d(...

meaning that Poisson is declared only here and when it is called (by simply writing Poisson;) then (2.5) will be
solved by the Conjugate Gradient method.

2.2 Classification of partial differential equations

Summary : It is usually not easy to determine the type of a system. Yet the approximations and algorithms suited to
the problem depend on its type:
• Finite Elements compatible (LBB conditions) for elliptic systems
• Finite difference on the parabolic variable and a time loop on each elliptic subsystem of parabolic systems; better
stability diagrams when the schemes are implicit in time.
• Upwinding, Petrov-Galerkin, Characteristics-Galerkin, Discontinuous-Galerkin, Finite Volumes for hyperbolic
systems plus, possibly, a time loop.

32 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

When the system changes type, then expect difficulties (like shock discontinuities) !
Elliptic, parabolic and hyperbolic equations
A partial differential equation (PDE) is a relation between a function of several variables and its derivatives.
𝜕2𝜙 𝜕𝑚𝜙
(︂ )︂
𝜕𝜙 𝜕𝜙
𝐹 𝜙(𝑥), (𝑥), · · · , (𝑥), 2 (𝑥), · · · , 𝑚 (𝑥) = 0, ∀𝑥 ∈ Ω ⊂ R𝑑
𝜕𝑥1 𝜕𝑥𝑑 𝜕𝑥1 𝜕𝑥𝑑
The range of 𝑥 over which the equation is taken, here Ω, is called the domain of the PDE. The highest derivation index,
here 𝑚, is called the order. If 𝐹 and 𝜙 are vector valued functions, then the PDE is actually a system of PDEs.
Unless indicated otherwise, here by convention one PDE corresponds to one scalar valued 𝐹 and 𝜙. If 𝐹 is linear with
respect to its arguments, then the PDE is said to be linear.
The general form of a second order, linear scalar PDE is

𝛼𝜙 + 𝑎 · ∇𝜙 + 𝐵 : ∇(∇𝜙) = 𝑓 in Ω ⊂ R𝑑 ,
2 ∑︀𝑑
where 𝜕𝑥𝜕𝑖 𝜕𝑥
𝜙
𝑗
and 𝐴 : 𝐵 means 𝑖,𝑗=1 𝑎𝑖𝑗 𝑏𝑖𝑗 ., 𝑓 (𝑥), 𝛼(𝑥) ∈ R, 𝑎(𝑥) ∈ R𝑑 , 𝐵(𝑥) ∈ R𝑑×𝑑 are the PDE coefficients.
If the coefficients are independent of 𝑥, the PDE is said to have constant coefficients.
To a PDE we associate a quadratic form, by replacing 𝜙 by 1, 𝜕𝜙/𝜕𝑥𝑖 by 𝑧𝑖 and 𝜕 2 𝜙/𝜕𝑥𝑖 𝜕𝑥𝑗 by 𝑧𝑖 𝑧𝑗 , where 𝑧 is a
vector in R𝑑 :

𝛼 + 𝐴 · 𝑧 + 𝑧 𝑇 𝐵𝑧 = 𝑓.

If it is the equation of an ellipse (ellipsoid if 𝑑 ≥ 2), the PDE is said to be elliptic; if it is the equation of a parabola or
a hyperbola, the PDE is said to be parabolic or hyperbolic.
If 𝐵 ≡ 0, the degree is no longer 2 but 1, and for reasons that will appear more clearly later, the PDE is still said to be
hyperbolic.
These concepts can be generalized to systems, by studying whether or not the polynomial system 𝑃 (𝑧) associated with
the PDE system has branches at infinity (ellipsoids have no branches at infinity, paraboloids have one, and hyperboloids
have several).
If the PDE is not linear, it is said to be non-linear. These are said to be locally elliptic, parabolic, or hyperbolic according
to the type of the linearized equation.
For example, for the non-linear equation
𝜕 2 𝜙 𝜕𝜙 𝜕 2 𝜙
− =1
𝜕𝑡2 𝜕𝑥 𝜕𝑥2
we have 𝑑 = 2, 𝑥1 = 𝑡, 𝑥2 = 𝑥 and its linearized form is:
𝜕 2 𝑢 𝜕𝑢 𝜕 2 𝜙 𝜕𝜙 𝜕 2 𝑢
− − =0
𝜕𝑡2 𝜕𝑥 𝜕𝑥2 𝜕𝑥 𝜕𝑥2
which for the unknown 𝑢 is locally elliptic if 𝜕𝜙
𝜕𝑥 < 0 and locally hyperbolic if 𝜕𝜙
𝜕𝑥 > 0.

Tip: Laplace’s equation is elliptic:


𝜕2𝜙 𝜕2𝜙 𝜕2𝜙
∆𝜙 ≡ + + · · · + = 𝑓, ∀𝑥 ∈ Ω ⊂ R𝑑
𝜕𝑥21 𝜕𝑥22 𝜕𝑥2𝑑

Tip: The heat equation is parabolic in 𝑄 = Ω×]0, 𝑇 [⊂ R𝑑+1 :


𝜕𝜙
− 𝜇∆𝜙 = 𝑓 ∀𝑥 ∈ Ω ⊂ R𝑑 , ∀𝑡 ∈]0, 𝑇 [
𝜕𝑡

2.2. Classification of partial differential equations 33


FreeFEM Documentation, Release 4.8

Tip: If 𝜇 > 0, the wave equation is hyperbolic:


𝜕2𝜙
− 𝜇∆𝜙 = 𝑓 in 𝑄.
𝜕𝑡2

Tip: The convection diffusion equation is parabolic if 𝜇 ̸= 0 and hyperbolic otherwise:


𝜕𝜙
+ 𝑎∇𝜙 − 𝜇∆𝜙 = 𝑓
𝜕𝑡

Tip: The biharmonic equation is elliptic:

∆(∆𝜙) = 𝑓 in Ω.

Boundary conditions
A relation between a function and its derivatives is not sufficient to define the function. Additional information on the
boundary Γ = 𝜕Ω of Ω, or on part of Γ is necessary. Such information is called a boundary condition.
For example:

𝜙(𝑥) given, ∀𝑥 ∈ Γ,

is called a Dirichlet boundary condition. The Neumann condition is


𝜕𝜙
(𝑥) given on Γ (or 𝑛 · 𝐵∇𝜙, given on Γ for a general second order PDE)
𝜕𝑛
where 𝑛 is the normal at 𝑥 ∈ Γ directed towards the exterior of Ω (by definition 𝜕𝜙
𝜕𝑛 = ∇𝜙 · 𝑛).
Another classical condition, called a Robin (or Fourier) condition is written as:
𝜕𝜙
𝜙(𝑥) + 𝛽(𝑥) (𝑥) given on Γ.
𝜕𝑛
Finding a set of boundary conditions that defines a unique 𝜙 is a difficult art.
In general, an elliptic equation is well posed (i.e. 𝜙 is unique) with one Dirichlet, Neumann or Robin condition on the
whole boundary.
Thus, Laplace’s equation is well posed with a Dirichlet or Neumann condition but also with :
𝜕𝜙
𝜙 given on Γ1 , given on Γ2 , Γ1 ∪ Γ2 = Γ, Γ˙1 ∩ Γ˙2 = ∅.
𝜕𝑛
Parabolic and hyperbolic equations rarely require boundary conditions on all of Γ×]0, 𝑇 [. For instance, the heat equa-
tion is well posed with :

𝜙 given at 𝑡 = 0 and Dirichlet or Neumann or mixed conditions on 𝜕Ω.

Here 𝑡 is time so the first condition is called an initial condition. The whole set of conditions is also called Cauchy
condition.
The wave equation is well posed with :
𝜕𝜙
𝜙 and given at 𝑡 = 0 and Dirichlet or Neumann or mixed conditions on 𝜕Ω.
𝜕𝑡

34 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

2.3 Membrane

Summary : Here we shall learn how to solve a Dirichlet and/or mixed Dirichlet Neumann problem for the Laplace
operator with application to the equilibrium of a membrane under load. We shall also check the accuracy of the method
and interface with other graphics packages
An elastic membrane Ω is attached to a planar rigid support Γ, and a force 𝑓 (𝑥)𝑑𝑥 is exerted on each surface element
d𝑥 = d𝑥1 d𝑥2 . The vertical membrane displacement, 𝜙(𝑥), is obtained by solving Laplace’s equation:

−∆𝜙 = 𝑓 in Ω

As the membrane is fixed to its planar support, one has:

𝜙|Γ = 0

If the support wasn’t planar but had an elevation 𝑧(𝑥1 , 𝑥2 ) then the boundary conditions would be of non-homogeneous
Dirichlet type.

𝜙|Γ = 𝑧

If a part Γ2 of the membrane border Γ is not fixed to the support but is left hanging, then due to the membrane’s rigidity
the angle with the normal vector 𝑛 is zero; thus the boundary conditions are:

𝜕𝜙
𝜙|Γ1 = 𝑧, |Γ = 0
𝜕𝑛 2

where Γ1 = Γ − Γ2 ; recall that 𝜕𝜙


𝜕𝑛 = ∇𝜙 · 𝑛 Let us recall also that the Laplace operator ∆ is defined by:

𝜕2𝜙 𝜕2𝜙
∆𝜙 = +
𝜕𝑥21 𝜕𝑥22

Todo: Check references

With such “mixed boundary conditions” the problem has a unique solution (see Dautray-Lions (1988), Strang (1986)
and Raviart-Thomas (1983)). The easiest proof is to notice that 𝜙 is the state of least energy, i.e.
∫︁
1
𝐸(𝜑) = min 𝐸(𝑣), with 𝐸(𝑣) = ( |∇𝑣|2 − 𝑓 𝑣)
𝜙−𝑧∈𝑉 Ω 2

and where 𝑉 is the subspace of the Sobolev space 𝐻 1 (Ω) of functions which have zero trace on Γ1 . Recall that
(𝑥 ∈ R𝑑 , 𝑑 = 2 here):

𝐻 1 (Ω) = {𝑢 ∈ 𝐿2 (Ω) : ∇𝑢 ∈ (𝐿2 (Ω))𝑑 }

Calculus of variation shows that the minimum must satisfy, what is known as the weak form of the PDE or its variational
formulation (also known here as the theorem of virtual work)
∫︁ ∫︁
∇𝜙 · ∇𝑤 = 𝑓 𝑤 ∀𝑤 ∈ 𝑉
Ω Ω

Next an integration by parts (Green’s formula) will show that this is equivalent to the PDE when second derivatives
exist.

2.3. Membrane 35
FreeFEM Documentation, Release 4.8

Warning: Unlike the previous version Freefem+ which had both weak and strong forms, FreeFEM implements
only weak formulations. It is not possible to go further in using this software if you don’t know the weak form
(i.e. variational formulation) of your problem: either you read a book, or ask help form a colleague or drop the
matter. Now if you want to solve a system of PDE like 𝐴(𝑢, 𝑣) = 0, 𝐵(𝑢, 𝑣) = 0 don’t close this manual, because
in weak form it is

∫︁
(𝐴(𝑢, 𝑣)𝑤1 + 𝐵(𝑢, 𝑣)𝑤2 ) = 0 ∀𝑤1 , 𝑤2 ...
Ω

Example
Let an ellipse have the length of the semimajor axis 𝑎 = 2, and unitary the semiminor axis. Let the surface force be
𝑓 = 1. Programming this case with FreeFEM gives:

1 // Parameters
2 real theta = 4.*pi/3.;
3 real a = 2.; //The length of the semimajor axis
4 real b = 1.; //The length of the semiminor axis
5 func z = x;
6

7 // Mesh
8 border Gamma1(t=0., theta){x=a*cos(t); y=b*sin(t);}
9 border Gamma2(t=theta, 2.*pi){x=a*cos(t); y=b*sin(t);}
10 mesh Th = buildmesh(Gamma1(100) + Gamma2(50));
11

12 // Fespace
13 fespace Vh(Th, P2); //P2 conforming triangular FEM
14 Vh phi, w, f=1;
15

16 // Solve
17 solve Laplace(phi, w)
18 = int2d(Th)(
19 dx(phi)*dx(w)
20 + dy(phi)*dy(w)
21 )
22 - int2d(Th)(
23 f*w
24 )
25 + on(Gamma1, phi=z)
26 ;
27

28 // Plot
29 plot(phi, wait=true, ps="membrane.eps"); //Plot phi
30 plot(Th, wait=true, ps="membraneTh.eps"); //Plot Th
31

32 // Save mesh
33 savemesh(Th,"Th.msh");

A triangulation is built by the keyword buildmesh. This keyword calls a triangulation subroutine based on the De-
launay test, which first triangulates with only the boundary points, then adds internal points by subdividing the edges.
How fine the triangulation becomes is controlled by the size of the closest boundary edges.
The PDE is then discretized using the triangular second order finite element method on the triangulation; as was briefly

36 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(a) Mesh of the ellipse (b) Level lines of the membrane deformation

Fig. 2.3: Membrane

indicated in the previous chapter, a linear system is derived from the discrete formulation whose size is the number of
vertices plus the number of mid-edges in the triangulation.
The system is solved by a multi-frontal Gauss LU factorization implemented in the package UMFPACK.
The keyword plot will display both Tℎ and 𝜙 (remove Th if 𝜙 only is desired) and the qualifier fill=true replaces
the default option (colored level lines) by a full color display.

1 plot(phi,wait=true,fill=true); //Plot phi with full color display

Results are on Fig. 2.3a and Fig. 2.3b.


Next we would like to check the results !
One simple way is to adjust the parameters so as to know the solutions. For instance on the unit circle a=1, 𝜙𝑒 =
sin(𝑥2 + 𝑦 2 − 1) solves the problem when:

𝑧 = 0, 𝑓 = −4(cos(𝑥2 + 𝑦 2 − 1) − (𝑥2 + 𝑦 2 ) sin(𝑥2 + 𝑦 2 − 1))

except that on Γ2 𝜕𝑛 𝜙 = 2 instead of zero. So we will consider a non-homogeneous Neumann condition and solve:
∫︁ ∫︁ ∫︁
∇𝜙 · ∇𝑤 = 𝑓𝑤 + 2𝑤 ∀𝑤 ∈ 𝑉
Ω Ω Γ2

We will do that with two triangulations, compute the 𝐿2 error:


∫︁
𝜖= |𝜙 − 𝜙𝑒 |2
Ω

and print the error in both cases as well as the log of their ratio an indication of the rate of convergence.

1 // Parameters
2 verbosity = 0; //to remove all default output
3 real theta = 4.*pi/3.;
4 real a=1.; //the length of the semimajor axis
(continues on next page)

2.3. Membrane 37
FreeFEM Documentation, Release 4.8

(continued from previous page)


5 real b=1.; //the length of the semiminor axis
6 func f = -4*(cos(x^2+y^2-1) - (x^2+y^2)*sin(x^2+y^2-1));
7 func phiexact = sin(x^2 + y^2 - 1);
8

9 // Mesh
10 border Gamma1(t=0., theta){x=a*cos(t); y=b*sin(t);}
11 border Gamma2(t=theta, 2.*pi){x=a*cos(t); y=b*sin(t);}
12

13 // Error loop
14 real[int] L2error(2); //an array of two values
15 for(int n = 0; n < 2; n++){
16 // Mesh
17 mesh Th = buildmesh(Gamma1(20*(n+1)) + Gamma2(10*(n+1)));
18

19 // Fespace
20 fespace Vh(Th, P2);
21 Vh phi, w;
22

23 // Solve
24 solve Laplace(phi, w)
25 = int2d(Th)(
26 dx(phi)*dx(w)
27 + dy(phi)*dy(w)
28 )
29 - int2d(Th)(
30 f*w
31 )
32 - int1d(Th, Gamma2)(
33 2*w
34 )
35 + on(Gamma1,phi=0)
36 ;
37

38 // Plot
39 plot(Th, phi, wait=true, ps="membrane.eps");
40

41 // Error
42 L2error[n] = sqrt(int2d(Th)((phi-phiexact)^2));
43 }
44

45 // Display loop
46 for(int n = 0; n < 2; n++)
47 cout << "L2error " << n << " = " << L2error[n] << endl;
48

49 // Convergence rate
50 cout << "convergence rate = "<< log(L2error[0]/L2error[1])/log(2.) << endl;

The output is:

1 L2error 0 = 0.00462991
2 L2error 1 = 0.00117128
3 convergence rate = 1.9829
(continues on next page)

38 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


4 times: compile 0.02s, execution 6.94s

We find a rate of 1.98 , which is not close enough to the 3 predicted by the theory.
The Geometry is always a polygon so we lose one order due to the geometry approximation in 𝑂(ℎ2 ).
Now if you are not satisfied with the .eps plot generated by FreeFEM and you want to use other graphic facilities,
then you must store the solution in a file very much like in C++. It will be useless if you don’t save the triangulation as
well, consequently you must do

1 {
2 ofstream ff("phi.txt");
3 ff << phi[];
4 }
5 savemesh(Th,"Th.msh");

For the triangulation the name is important: the extension determines the format.

Fig. 2.4: The 3D version drawn by gnuplot from a file generated by FreeFEM

Still that may not take you where you want. Here is an interface with gnuplot (see : web site link ) to produce the Fig.
2.4.

1 //to build a gnuplot data file


2 {
3 ofstream ff("graph.txt");
4 for (int i = 0; i < Th.nt; i++)
5 {
6 for (int j = 0; j < 3; j++)
7 ff << Th[i][j].x << " "<< Th[i][j].y << " " << phi[][Vh(i,j)] << endl;
8

9 ff << Th[i][0].x << " " << Th[i][0].y << " " << phi[][Vh(i,0)] << "\n\n\n"
(continues on next page)

2.3. Membrane 39
FreeFEM Documentation, Release 4.8

(continued from previous page)


10 }
11 }

We use the finite element numbering, where Wh(i,j) is the global index of 𝑗 𝑇 ℎ degrees of freedom of triangle number
𝑖.
Then open gnuplot and do:

1 set palette rgbformulae 30,31,32


2 splot "graph.txt" w l pal

This works with P2 and P1, but not with P1nc because the 3 first degrees of freedom of P2 or P2 are on vertices and
not with P1nc.

2.4 Heat Exchanger

Summary: Here we shall learn more about geometry input and triangulation files, as well as read and write operations.
The problem Let {𝐶𝑖 }1,2 , be 2 thermal conductors within an enclosure 𝐶0 (see Fig. 2.5).

Fig. 2.5: Heat exchanger geometry

The first one is held at a constant temperature 𝑢1 the other one has a given thermal conductivity 𝜅2 3 times larger than
the one of 𝐶0 .
We assume that the border of enclosure 𝐶0 is held at temperature 20∘ 𝐶 and that we have waited long enough for thermal
equilibrium.
In order to know 𝑢(𝑥) at any point 𝑥 of the domain Ω, we must solve:

∇ · (𝜅∇𝑢) = 0 in Ω, 𝑢|Γ = 𝑔

where Ω is the interior of 𝐶0 minus the conductor 𝐶1 and Γ is the boundary of Ω, that is 𝐶0 ∪ 𝐶1 .

40 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

Here 𝑔 is any function of 𝑥 equal to 𝑢𝑖 on 𝐶𝑖 .


The second equation is a reduced form for:

𝑢 = 𝑢𝑖 on 𝐶𝑖 , 𝑖 = 0, 1.

The variational formulation for this problem is in the subspace 𝐻01 (Ω) ⊂ 𝐻 1 (Ω) of functions which have zero traces
on Γ.
∫︁
1
𝑢 − 𝑔 ∈ 𝐻0 (Ω) : ∇𝑢∇𝑣 = 0∀𝑣 ∈ 𝐻01 (Ω)
Ω

Let us assume that 𝐶0 is a circle of radius 5 centered at the origin, 𝐶𝑖 are rectangles, 𝐶1 being at the constant temperature
𝑢1 = 60∘ 𝐶 (so we can only consider its boundary).

1 // Parameters
2 int C1=99;
3 int C2=98; //could be anything such that !=0 and C1!=C2
4

5 // Mesh
6 border C0(t=0., 2.*pi){x=5.*cos(t); y=5.*sin(t);}
7

8 border C11(t=0., 1.){x=1.+t; y=3.; label=C1;}


9 border C12(t=0., 1.){x=2.; y=3.-6.*t; label=C1;}
10 border C13(t=0., 1.){x=2.-t; y=-3.; label=C1;}
11 border C14(t=0., 1.){x=1.; y=-3.+6.*t; label=C1;}
12

13 border C21(t=0., 1.){x=-2.+t; y=3.; label=C2;}


14 border C22(t=0., 1.){x=-1.; y=3.-6.*t; label=C2;}
15 border C23(t=0., 1.){x=-1.-t; y=-3.; label=C2;}
16 border C24(t=0., 1.){x=-2.; y=-3.+6.*t; label=C2;}
17

18 plot( C0(50) //to see the border of the domain


19 + C11(5)+C12(20)+C13(5)+C14(20)
20 + C21(-5)+C22(-20)+C23(-5)+C24(-20),
21 wait=true, ps="heatexb.eps");
22

23 mesh Th=buildmesh(C0(50)
24 + C11(5)+C12(20)+C13(5)+C14(20)
25 + C21(-5)+C22(-20)+C23(-5)+C24(-20));
26

27 plot(Th,wait=1);
28

29 // Fespace
30 fespace Vh(Th, P1);
31 Vh u, v;
32 Vh kappa=1 + 2*(x<-1)*(x>-2)*(y<3)*(y>-3);
33

34 // Solve
35 solve a(u, v)
36 = int2d(Th)(
37 kappa*(
38 dx(u)*dx(v)
39 + dy(u)*dy(v)
40 )
(continues on next page)

2.4. Heat Exchanger 41


FreeFEM Documentation, Release 4.8

(a) Heat exchanger mesh


(b) Heat exchanger solution

Fig. 2.6: Heat exchanger

(continued from previous page)


41 )
42 +on(C0, u=20)
43 +on(C1, u=60)
44 ;
45

46 // Plot
47 plot(u, wait=true, value=true, fill=true, ps="HeatExchanger.eps");

Note the following:


• C0 is oriented counterclockwise by 𝑡, while C1 is oriented clockwise and C2 is oriented counterclockwise. This
is why C1 is viewed as a hole by buildmesh.
• C1 and C2 are built by joining pieces of straight lines. To group them in the same logical unit to input the
boundary conditions in a readable way we assigned a label on the boundaries. As said earlier, borders have an
internal number corresponding to their order in the program (check it by adding a cout << C22; above). This
is essential to understand how a mesh can be output to a file and re-read (see below).
• As usual the mesh density is controlled by the number of vertices assigned to each boundary. It is not possible
to change the (uniform) distribution of vertices but a piece of boundary can always be cut in two or more parts,
for instance C12 could be replaced by C121+C122:

1 // border C12(t=0.,1.){x=2.; y=3.-6.*t; label=C1;}


2 border C121(t=0.,0.7){x=2.; y=3.-6.*t; label=C1;}
3 border C122(t=0.7,1.){x=2.; y=3.-6.*t; label=C1;}
4 ...
5 buildmesh(.../*+ C12(20) */ + C121(12) + C122(8) + ...);

Tip: Exercise :
Use the symmetry of the problem with respect to the x axes.

42 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

Triangulate only one half of the domain, and set homogeneous Neumann conditions on the horizontal axis.

Writing and reading triangulation files Suppose that at the end of the previous program we added the line

1 savemesh(Th, "condensor.msh");

and then later on we write a similar program but we wish to read the mesh from that file. Then this is how the condenser
should be computed:

1 // Mesh
2 mesh Sh = readmesh("condensor.msh");
3

4 // Fespace
5 fespace Wh(Sh, P1);
6 Wh us, vs;
7

8 // Solve
9 solve b(us, vs)
10 = int2d(Sh)(
11 dx(us)*dx(vs)
12 + dy(us)*dy(vs)
13 )
14 +on(1, us=0)
15 +on(99, us=1)
16 +on(98, us=-1)
17 ;
18

19 // Plot
20 plot(us);

Note that the names of the boundaries are lost but either their internal number (in the case of C0) or their label number
(for C1 and C2) are kept.

2.5 Acoustics

Summary : Here we go to grip with ill posed problems and eigenvalue problems
Pressure variations in air at rest are governed by the wave equation:

𝜕2𝑢
− 𝑐2 ∆𝑢 = 0
𝜕𝑡2
When the solution wave is monochromatic (and that depends on the boundary and initial conditions), 𝑢 is of the form
𝑢(𝑥, 𝑡) = 𝑅𝑒(𝑣(𝑥)𝑒𝑖𝑘𝑡 ) where 𝑣 is a solution of Helmholtz’s equation:

𝑘 2 𝑣 + 𝑐2 ∆𝑣 =0 in Ω
𝜕𝑣
𝜕𝑛 |Γ =𝑔

where 𝑔 is the source.


Note the “+” sign in front of the Laplace operator and that 𝑘 > 0 is real. This sign may make the problem ill posed for
some values of 𝑘𝑐 , a phenomenon called “resonance”.
At resonance there are non-zero solutions even when 𝑔 = 0. So the following program may or may not work:

2.5. Acoustics 43
FreeFEM Documentation, Release 4.8

1 // Parameters
2 real kc2 = 1.;
3 func g = y*(1.-y);
4

5 // Mesh
6 border a0(t=0., 1.){x=5.; y=1.+2.*t;}
7 border a1(t=0., 1.){x=5.-2.*t; y=3.;}
8 border a2(t=0., 1.){x=3.-2.*t; y=3.-2.*t;}
9 border a3(t=0., 1.){x=1.-t; y=1.;}
10 border a4(t=0., 1.){x=0.; y=1.-t;}
11 border a5(t=0., 1.){x=t; y=0.;}
12 border a6(t=0., 1.){x=1.+4.*t; y=t;}
13

14 mesh Th = buildmesh(a0(20) + a1(20) + a2(20)


15 + a3(20) + a4(20) + a5(20) + a6(20));
16

17 // Fespace
18 fespace Vh(Th, P1);
19 Vh u, v;
20

21 // Solve
22 solve sound(u, v)
23 = int2d(Th)(
24 u*v * kc2
25 - dx(u)*dx(v)
26 - dy(u)*dy(v)
27 )
28 - int1d(Th, a4)(
29 g * v
30 )
31 ;
32

33 // Plot
34 plot(u, wait=1, ps="Sound.eps");

Results are on Fig. 2.7a. But when 𝑘𝑐2 is an eigenvalue of the problem, then the solution is not unique:
• if 𝑢𝑒 ̸= 0 is an eigen state, then for any given solution 𝑢 + 𝑢𝑒 is another solution.
To find all the 𝑢𝑒 one can do the following :
1 // Parameters
2 real sigma = 20; //value of the shift
3

4 // Problem
5 // OP = A - sigma B ; // The shifted matrix
6 varf op(u1, u2)
7 = int2d(Th)(
8 dx(u1)*dx(u2)
9 + dy(u1)*dy(u2)
10 - sigma* u1*u2
11 )
12 ;
13

(continues on next page)

44 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(a) Amplitude of an acoustic signal coming from the left ver- (b) First eigen state (𝜆 = (𝑘/𝑐)2 = 14.695) close to 15 of
tical wall. eigenvalue problem: −Δ𝜙 = 𝜆𝜙 and 𝜕𝑛 𝜕𝜙
= 0 on Γ}

Fig. 2.7: Acoustics

(continued from previous page)


14 varf b([u1], [u2])
15 = int2d(Th)(
16 u1*u2
17 )
18 ; // No Boundary condition see note \ref{note BC EV}
19

20 matrix OP = op(Vh, Vh, solver=Crout, factorize=1);


21 matrix B = b(Vh, Vh, solver=CG, eps=1e-20);
22

23 // Eigen values
24 int nev=2; // Number of requested eigenvalues near sigma
25

26 real[int] ev(nev); // To store the nev eigenvalue


27 Vh[int] eV(nev); // To store the nev eigenvector
28

29 int k=EigenValue(OP, B, sym=true, sigma=sigma, value=ev, vector=eV,


30 tol=1e-10, maxit=0, ncv=0);
31

32 cout << ev(0) << " 2 eigen values " << ev(1) << endl;
33 v = eV[0];
34 plot(v, wait=true, ps="eigen.eps");

2.6 Thermal Conduction

Summary : Here we shall learn how to deal with a time dependent parabolic problem. We shall also show how to
treat an axisymmetric problem and show also how to deal with a nonlinear problem
How air cools a plate
We seek the temperature distribution in a plate (0, 𝐿𝑥) × (0, 𝐿𝑦) × (0, 𝐿𝑧) of rectangular cross section Ω = (0, 6) ×
(0, 1); the plate is surrounded by air at temperature 𝑢𝑒 and initially at temperature 𝑢 = 𝑢0 + 𝐿 𝑥
𝑢1 . In the plane
perpendicular to the plate at 𝑧 = 𝐿𝑧/2, the temperature varies little with the coordinate 𝑧; as a first approximation the
problem is 2D.

2.6. Thermal Conduction 45


FreeFEM Documentation, Release 4.8

We must solve the temperature equation in Ω in a time interval (0,T).


𝜕𝑡 𝑢 − ∇ · (𝜅∇𝑢) =0 in Ω × (0, 𝑇 )
𝑢(𝑥, 𝑦, 0) = 𝑢0 + 𝑥𝑢1
𝜕𝑢
𝜅 𝜕𝑛 + 𝛼(𝑢 − 𝑢𝑒 ) =0 on Γ × (0, 𝑇 )
Here the diffusion 𝜅 will take two values, one below the middle horizontal line and ten times less above, so as to
simulate a thermostat.
The term 𝛼(𝑢 − 𝑢𝑒 ) accounts for the loss of temperature by convection in air. Mathematically this boundary condition
is of Fourier (or Robin, or mixed) type.
The variational formulation is in 𝐿2 (0, 𝑇 ; 𝐻 1 (Ω)); in loose terms and after applying an implicit Euler finite difference
approximation in time; we shall seek 𝑢𝑛 (𝑥, 𝑦) satisfying for all 𝑤 ∈ 𝐻 1 (Ω):
𝑢𝑛 − 𝑢𝑛−1
∫︁ ∫︁
( 𝑤 + 𝜅∇𝑢𝑛 ∇𝑤) + 𝛼(𝑢𝑛 − 𝑢𝑢 𝑒)𝑤 = 0
Ω 𝛿𝑡 Γ

1 // Parameters
2 func u0 = 10. + 90.*x/6.;
3 func k = 1.8*(y<0.5) + 0.2;
4 real ue = 25.;
5 real alpha=0.25;
6 real T=5.;
7 real dt=0.1 ;
8

9 // Mesh
10 mesh Th = square(30, 5, [6.*x,y]);
11

12 // Fespace
13 fespace Vh(Th, P1);
14 Vh u=u0, v, uold;
15

16 // Problem
17 problem thermic(u, v)
18 = int2d(Th)(
19 u*v/dt
20 + k*(
21 dx(u) * dx(v)
22 + dy(u) * dy(v)
23 )
24 )
25 + int1d(Th, 1, 3)(
26 alpha*u*v
27 )
28 - int1d(Th, 1, 3)(
29 alpha*ue*v
30 )
31 - int2d(Th)(
32 uold*v/dt
33 )
34 + on(2, 4, u=u0)
35 ;
36

37 // Time iterations
(continues on next page)

46 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(b) Decay of temperature versus time at 𝑥 = 3, 𝑦 = 0.5

(a) Temperature at 𝑡 = 4.9.

Fig. 2.8: Thermal conduction

(continued from previous page)


38 ofstream ff("thermic.dat");
39 for(real t = 0; t < T; t += dt){
40 uold = u; //equivalent to u^{n-1} = u^n
41 thermic; //here the thermic problem is solved
42 ff << u(3., 0.5) << endl;
43 plot(u);
44 }

Note: We must separate by hand the bilinear part from the linear one.

Note: The way we store the temperature at point (3, 0.5) for all times in file thermic.dat. Should a one dimensional
plot be required (you can use gnuplot tools), the same procedure can be used. For instance to print 𝑥 ↦→ 𝜕𝑢𝜕𝑦 (𝑥, 0.9)
one would do:

1 for(int i = 0; i < 20; i++)


2 cout << dy(u)(6.0*i/20.0,0.9) << endl;

Results are shown on Fig. 2.8a and Fig. 2.8b.

2.6. Thermal Conduction 47


FreeFEM Documentation, Release 4.8

2.6.1 Axisymmetry: 3D Rod with circular section

Let us now deal with a cylindrical rod instead of a flat plate. For simplicity we take 𝜅 = 1.
In cylindrical coordinates, the Laplace operator becomes (𝑟 is the distance to the axis, 𝑧 is the distance along the axis,
𝜃 polar angle in a fixed plane perpendicular to the axis):
1 1 2 2
∆𝑢 = 𝜕𝑟 (𝑟𝜕𝑟 𝑢) + 2 𝜕𝜃𝜃 𝑢 + 𝜕𝑧𝑧 .
𝑟 𝑟
Symmetry implies that we loose the dependence with respect to 𝜃; so the domain Ω is again a rectangle ]0, 𝑅[×]0, |[
. We take the convention of numbering of the edges as in square() (1 for the bottom horizontal . . . ); the problem is
now:
𝑟𝜕𝑡 𝑢 − 𝜕𝑟 (𝑟𝜕𝑟 𝑢) − 𝜕𝑧 (𝑟𝜕𝑧 𝑢) =0 in Ω
𝑢(𝑡 = 0) = 𝑢0 + 𝐿𝑧𝑧 (𝑢1 − 𝑢)
𝑢|Γ4 = 𝑢0
𝑢|Γ2 = 𝑢1
𝜕𝑢
𝛼(𝑢 − 𝑢𝑒 ) + 𝜕𝑛 |Γ1 ∪Γ3 =0
Note that the PDE has been multiplied by 𝑟.
After discretization in time with an implicit scheme, with time steps dt, in the FreeFEM syntax 𝑟 becomes 𝑥 and 𝑧
becomes 𝑦 and the problem is:

1 problem thermaxi(u, v)
2 = int2d(Th)(
3 (u*v/dt + dx(u)*dx(v) + dy(u)*dy(v))*x
4 )
5 + int1d(Th, 3)(
6 alpha*x*u*v
7 )
8 - int1d(Th, 3)(
9 alpha*x*ue*v
10 )
11 - int2d(Th)(
12 uold*v*x/dt
13 )
14 + on(2, 4, u=u0);

Note: The bilinear form degenerates at 𝑥 = 0. Still one can prove existence and uniqueness for 𝑢 and because of this
degeneracy no boundary conditions need to be imposed on Γ1 .

2.6.2 A Nonlinear Problem : Radiation

Heat loss through radiation is a loss proportional to the absolute temperature to the fourth power (Stefan’s Law). This
adds to the loss by convection and gives the following boundary condition:
𝜕𝑢
𝜅 + 𝛼(𝑢 − 𝑢𝑒 ) + 𝑐[(𝑢 + 273)4 − (𝑢𝑒 + 273)4 ] = 0
𝜕𝑛
The problem is nonlinear, and must be solved iteratively with fixed-point iteration where 𝑚 denotes the iteration index,
a semi-linearization of the radiation condition gives
𝜕𝑢𝑚+1
+ 𝛼(𝑢𝑚+1 − 𝑢𝑒 ) + 𝑐(𝑢𝑚+1 − 𝑢𝑒 )(𝑢𝑚 + 𝑢𝑒 + 546)((𝑢𝑚 + 273)2 + (𝑢𝑒 + 273)2 ) = 0,
𝜕𝑛

48 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

because we have the identity 𝑎4 − 𝑏4 = (𝑎 − 𝑏)(𝑎 + 𝑏)(𝑎2 + 𝑏2 ).


The iterative process will work with 𝑣 = 𝑢 − 𝑢𝑒 .

1 ...
2 // Mesh
3 fespace Vh(Th, P1);
4 Vh vold, w, v=u0-ue, b,vp;
5

6 // Problem
7 problem thermradia(v, w)
8 = int2d(Th)(
9 v*w/dt
10 + k*(dx(v) * dx(w) + dy(v) * dy(w))
11 )
12 + int1d(Th, 1, 3)(
13 b*v*w
14 )
15 - int2d(Th)(
16 vold*w/dt
17 )
18 + on(2, 4, v=u0-ue)
19 ;
20

21 verbosity=0; // to remove spurious FREEfem print


22 for (real t=0;t<T;t+=dt){
23 vold[] = v[];// just copy DoF's, faster than interpolation pv=v;
24 for (int m = 0; m < 5; m++) {
25 vp[]=v[];// save previous state of commute error
26 b = alpha + rad * (v + 2*uek) * ((v+uek)^2 + uek^2);
27 thermradia;
28 vp[]-=v[];
29 real err = vp[].linfty;// error value
30 cout << " time " << t << " iter " << m << " err = "<< vp[].linfty << endl;
31 if( err < 1e-5) break; // if error is enough small break fixed-point loop
32 }
33 }
34 v[] += ue;// add a constant to all DoF's of v
35

36 plot(v);

2.7 Irrotational Fan Blade Flow and Thermal effects

Summary : Here we will learn how to deal with a multi-physics system of PDEs on a complex geometry, with multiple
meshes within one problem. We also learn how to manipulate the region indicator and see how smooth is the projection
operator from one mesh to another.
Incompressible flow
Without viscosity and vorticity incompressible flows have a velocity given by:
(︃ )︃
𝜕𝜓
𝑢= 𝜕𝑥
−𝜕𝜓 , where 𝜓 is solution of ∆𝜓 = 0
𝜕𝑦

2.7. Irrotational Fan Blade Flow and Thermal effects 49


FreeFEM Documentation, Release 4.8

This equation expresses both incompressibility (∇ · 𝑢 = 0) and absence of vortex (∇ × 𝑢 = 0).


As the fluid slips along the walls, normal velocity is zero, which means that 𝜓 satisfies:

𝜓 constant on the walls.

One can also prescribe the normal velocity at an artificial boundary, and this translates into non constant Dirichlet data
for 𝜓.
Airfoil
Let us consider a wing profile 𝑆 in a uniform flow. Infinity will be represented by a large circle 𝐶 where the flow is
assumed to be of uniform velocity; one way to model this problem is to write:

∆𝜓 = 0 in Ω, 𝜓|𝑆 = −𝑙, 𝜓|𝐶 = 𝑢∞ .𝑥⊥

where 𝜕Ω = 𝐶 ∪ 𝑆 and 𝑙 is the lift force.


The NACA0012 Airfoil
An equation for the upper surface of a NACA0012 (this is a classical wing profile in aerodynamics) is:

𝑦 = 0.17735 𝑥 − 0.075597𝑥 − 0.212836𝑥2 + 0.17363𝑥3 − 0.06254𝑥4 .

1 // Parameters
2 int S = 99;// wing label
3 // u infty
4 real theta = 8*pi/180;// // 1 degree on incidence => lift
5 real lift = theta*0.151952/0.0872665; // lift approximation formula
6 real uinfty1= cos(theta), uinfty2= sin(theta);
7 // Mesh
8 func naca12 = 0.17735*sqrt(x) - 0.075597*x - 0.212836*(x^2) + 0.17363*(x^3) - 0.06254*(x^
˓→4);

9 border C(t=0., 2.*pi){x=5.*cos(t); y=5.*sin(t);}


10 border Splus(t=0., 1.){x=t; y=naca12; label=S;}
11 border Sminus(t=1., 0.){x=t; y=-naca12; label=S;}
12 mesh Th = buildmesh(C(50) + Splus(70) + Sminus(70));
13

14 // Fespace
15 fespace Xh(Th, P2);
16 Xh psi, w;
17 macro grad(u) [dx(u),dy(u)]// def of grad operator
18 // Solve
19 solve potential(psi, w)
20 = int2d(Th)(
21 grad(psi)'*grad(w) // scalar product
22 )
23 + on(C, psi = [uinfty1,uinfty2]'*[y,-x])
24 + on(S, psi=-lift) // to get a correct value
25 ;
26

27 plot(psi, wait=1);

A zoom of the streamlines are shown on Fig. 2.9a.

50 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(a) Zoom around the NACA0012 airfoil showing the stream- (b) Temperature distribution at time T=25 (now the maxi-
lines (curve 𝜓 = constant). To obtain such a plot use the mum is at 90 instead of 120).
interactive graphic command: “+” and p.

Fig. 2.9: The NACA0012 Airfoil

2.7.1 Heat Convection around the airfoil

Now let us assume that the airfoil is hot and that air is there to cool it. Much like in the previous section the heat
equation for the temperature 𝑣 is

𝜕𝑣
𝜕𝑡 𝑣 − ∇ · (𝜅∇𝑣) + 𝑢 · ∇𝑣 = 0, 𝑣(𝑡 = 0) = 𝑣0 , |𝐶,𝑢·𝑛>0 = 0, 𝑣|𝐶,𝑢·𝑛<0 = 0
𝜕𝑛
But now the domain is outside AND inside 𝑆 and 𝜅 takes a different value in air and in steel. Furthermore there is
convection of heat by the flow, hence the term 𝑢 · ∇𝑣 above.
Consider the following, to be plugged at the end of the previous program:

1 // Corrected by F. Hecht may 2021


2 // Parameters
3 real S = 99;
4

5 border C(t=0, 2*pi){x=3*cos(t); y=3*sin(t);} // Label 1,2


6 border Splus(t=0, 1){x=t; y=0.17735*sqrt(t) - 0.075597*t - 0.212836*(t^2) + 0.17363*(t^
˓→3) - 0.06254*(t^4); label=S;}

7 border Sminus(t=1, 0){x=t; y=-(0.17735*sqrt(t) - 0.075597*t - 0.212836*(t^2) + 0.


˓→17363*(t^3) - 0.06254*(t^4)); label=S;}

8 mesh Th = buildmesh(C(50) + Splus(70) + Sminus(70));


9 // Fespace
10 fespace Vh(Th, P2);
11 Vh psi, w;
12

13 // Problem
14 solve potential(psi, w)
15 = int2d(Th)(dx(psi)*dx(w)+dy(psi)*dy(w))
16 + on(C, psi = y)
17 + on(S, psi=0);
18

19 // Plot
20 plot(psi, wait=1);
21

22 /// Thermic
23 // Parameters
(continues on next page)

2.7. Irrotational Fan Blade Flow and Thermal effects 51


FreeFEM Documentation, Release 4.8

(continued from previous page)


24 real dt = 0.005, nbT = 50;
25

26 // Mesh
27 border D(t=0, 2){x=1+t; y=0;}
28 mesh Sh = buildmesh(C(25) + Splus(-90) + Sminus(-90) + D(200));
29 int steel = Sh(0.5, 0).region, air = Sh(-1, 0).region;
30 // Change label to put BC on In flow
31 // Fespace
32 fespace Wh(Sh, P1);
33 Wh vv;
34

35 fespace W0(Sh, P0);


36 W0 k = 0.01*(region == air) + 0.1*(region == steel);
37 W0 u1 = dy(psi)*(region == air), u2 = -dx(psi)*(region == air);
38 Wh v = 120*(region == steel), vold;
39 // set the label to 10 on inflow boundary to inforce the temperature.
40 Sh = change(Sh,flabel = (label == C && [u1,u2]'*N<0) ? 10 : label);
41 int i;
42 problem thermic(v, vv, init=i, solver=LU)
43 = int2d(Sh)(
44 v*vv/dt + k*(dx(v)*dx(vv) + dy(v)*dy(vv))
45 + 10*(u1*dx(v) + u2*dy(v))*vv
46 )
47 - int2d(Sh)(vold*vv/dt)
48 + on(10, v= 0);
49

50

51 for(i = 0; i < nbT; i++) {


52 vold[]= v[];
53 thermic;
54 plot(v);
55 }

Note: How steel and air are identified by the mesh parameter region which is defined when buildmesh is called and
takes an integer value corresponding to each connected component of Ω;

Note: We use the change function to put label 10 on inflow boundary, remark the trick to chanhe only label C flabel
= (label == C && [u1,u2]'*N<0) ? 10 : label
How the convection terms are added without upwinding. Upwinding is necessary when the Pecley number |𝑢|𝐿/𝜅 is
large (here is a typical length scale), The factor 10 in front of the convection terms is a quick way of multiplying the
velocity by 10 (else it is too slow to see something).
The solver is Gauss’ LU factorization and when init ̸= 0 the LU decomposition is reused so it is much faster after the
first iteration.

52 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

2.8 Pure Convection : The Rotating Hill

Summary: Here we will present two methods for upwinding for the simplest convection problem. We will learn about
Characteristics-Galerkin and Discontinuous-Galerkin Finite Element Methods.
Let Ω be the unit disk centered at (0, 0); consider the rotation vector field

u = [𝑢1, 𝑢2], 𝑢1 = 𝑦, 𝑢2 = −𝑥

Pure convection by u is

𝜕𝑡 𝑐 + u.∇𝑐 =0 in Ω × (0, 𝑇 )
𝑐(𝑡 = 0) = 𝑐0 in Ω.

The exact solution 𝑐(𝑥𝑡 , 𝑡) at time 𝑡 en point 𝑥𝑡 is given by:

𝑐(𝑥𝑡 , 𝑡) = 𝑐0 (𝑥, 0)

where 𝑥𝑡 is the particle path in the flow starting at point 𝑥 at time 0. So 𝑥𝑡 are solutions of

d(𝑥𝑡
𝑥˙𝑡 = 𝑢(𝑥𝑡 ), 𝑥𝑡=0 = 𝑥, where 𝑥˙𝑡 =
d𝑡
The ODE are reversible and we want the solution at point 𝑥 at time 𝑡 ( not at point 𝑥𝑡 ) the initial point is 𝑥−𝑡 , and we
have

𝑐(𝑥, 𝑡) = 𝑐0 (𝑥−𝑡 , 0)

The game consists in solving the equation until 𝑇 = 2𝜋, that is for a full revolution and to compare the final solution
with the initial one; they should be equal.

2.8.1 Solution by a Characteristics-Galerkin Method

In FreeFEM there is an operator called convect([u1,u2], dt, c) which compute 𝑐 ∘ 𝑋 with 𝑋 is the convect
field defined by 𝑋(𝑥) = 𝑥𝑑𝑡 and where 𝑥𝜏 is particule path in the steady state velocity field u = [𝑢1, 𝑢2] starting at
point 𝑥 at time 𝜏 = 0, so 𝑥𝜏 is solution of the following ODE:

𝑥˙ 𝜏 = 𝑢(𝑥𝜏 ), 𝑥𝜏 =0 = 𝑥.

When u is piecewise constant; this is possible because 𝑥𝜏 is then a polygonal curve which can be computed exactly
and the solution exists always when u is divergence free; convect returns 𝑐(𝑥𝑑𝑓 ) = 𝐶 ∘ 𝑋.

1 // Parameters
2 real dt = 0.17;
3

4 // Mesh
5 border C(t=0., 2.*pi) {x=cos(t); y=sin(t);};
6 mesh Th = buildmesh(C(100));
7

8 // Fespace
9 fespace Uh(Th, P1);
10 Uh cold, c = exp(-10*((x-0.3)^2 +(y-0.3)^2));
11 Uh u1 = y, u2 = -x;
12

(continues on next page)

2.8. Pure Convection : The Rotating Hill 53


FreeFEM Documentation, Release 4.8

(continued from previous page)


13 // Time loop
14 real t = 0;
15 for (int m = 0; m < 2.*pi/dt; m++){
16 t += dt;
17 cold = c;
18 c = convect([u1, u2], -dt, cold);
19 plot(c, cmm=" t="+t +", min="+c[].min+", max="+c[].max);
20 }

Note: 3D plots can be done by adding the qualifyer dim=3 to the plot instruction.

The method is very powerful but has two limitations:


• it is not conservative
• it may diverge in rare cases when |u| is too small due to quadrature error.

2.8.2 Solution by Discontinuous-Galerkin FEM

Discontinuous Galerkin methods take advantage of the discontinuities of 𝑐 at the edges to build upwinding. There are
may formulations possible. We shall implement here the so-called dual-𝑃1𝐷𝐶 formulation (see [ERN2006]):

𝑐𝑛+1 − 𝑐𝑛
∫︁ ∫︁ ∫︁
1
( + 𝑢 · ∇𝑐)𝑤 + (𝛼|𝑛 · 𝑢| − 𝑛 · 𝑢)[𝑐]𝑤 = |𝑛 · 𝑢|𝑐𝑤 ∀𝑤
Ω 𝛿𝑡 𝐸 2 −
𝐸Γ

where 𝐸 is the set of inner edges and 𝐸Γ− is the set of boundary edges where 𝑢 · 𝑛 < 0 (in our case there is no such
edges). Finally [𝑐] is the jump of 𝑐 across an edge with the convention that 𝑐+ refers to the value on the right of the
oriented edge.

1 // Parameters
2 real al=0.5;
3 real dt = 0.05;
4

5 // Mesh
6 border C(t=0., 2.*pi) {x=cos(t); y=sin(t);};
7 mesh Th = buildmesh(C(100));
8

9 // Fespace
10 fespace Vh(Th,P1dc);
11 Vh w, ccold, v1 = y, v2 = -x, cc = exp(-10*((x-0.3)^2 +(y-0.3)^2));
12

13 // Macro
14 macro n() (N.x*v1 + N.y*v2) // Macro without parameter
15

16 // Problem
17 problem Adual(cc, w)
18 = int2d(Th)(
19 (cc/dt+(v1*dx(cc)+v2*dy(cc)))*w
20 )
21 + intalledges(Th)(
22 (1-nTonEdge)*w*(al*abs(n)-n/2)*jump(cc)
(continues on next page)

54 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


23 )
24 - int2d(Th)(
25 ccold*w/dt
26 )
27 ;
28

29 // Time iterations
30 for (real t = 0.; t < 2.*pi; t += dt){
31 ccold = cc;
32 Adual;
33 plot(cc, fill=1, cmm="t="+t+", min="+cc[].min+", max="+ cc[].max);
34 }
35

36 // Plot
37 real [int] viso = [-0.2, -0.1, 0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1];
38 plot(cc, wait=1, fill=1, ps="ConvectCG.eps", viso=viso);
39 plot(cc, wait=1, fill=1, ps="ConvectDG.eps", viso=viso);

Note: New keywords: intalledges to integrate on all edges of all triangles


∑︁ ∫︁
intalledges(Th) ≡
𝑇 ∈Th 𝜕𝑇

(so all internal edges are see two times), nTonEdge which is one if the triangle has a boundary edge and two otherwise,
jump to implement [𝑐].
Results of both methods are shown on Fig. 2.10a nad Fig. 2.10b with identical levels for the level line; this is done with
the plot-modifier viso.
Notice also the macro where the parameter u is not used (but the syntax needs one) and which ends with a //; it simply
replaces the name n by (N.x*v1+N.y*v2). As easily guessed N.x,N.y is the normal to the edge.
Now if you think that DG is too slow try this:

1 // Mesh
2 border C(t=0., 2.*pi) {x=cos(t); y=sin(t);};
3 mesh Th = buildmesh(C(100));
4

5 fespace Vh(Th,P1);//P1,P2,P0,P1dc,P2dc, uncond stable


6

7 Vh vh,vo,u1 = y, u2 = -x, v = exp(-10*((x-0.3)^2 +(y-0.3)^2));


8 real dt = 0.03,t=0, tmax=2*pi, al=0.5, alp=200;
9

10 problem A(v,vh) = int2d(Th)(v*vh/dt-v*(u1*dx(vh)+u2*dy(vh)))


11 + intalledges(Th)(vh*(mean(v)*(N.x*u1+N.y*u2)
12 +alp*jump(v)*abs(N.x*u1+N.y*u2)))
13 + int1d(Th,1)(((N.x*u1+N.y*u2)>0)*(N.x*u1+N.y*u2)*v*vh)
14 - int2d(Th)(vo*vh/dt);
15

16 varf Adual(v,vh) = int2d(Th)((v/dt+(u1*dx(v)+u2*dy(v)))*vh)


17 + intalledges(Th)((1-nTonEdge)*vh*(al*abs(N.x*u1+N.y*u2)
18 -(N.x*u1+N.y*u2)/2)*jump(v));
(continues on next page)

2.8. Pure Convection : The Rotating Hill 55


FreeFEM Documentation, Release 4.8

(a) The rotating hill after one revolution with (b) The rotating hill after one revolution with Discontinuous
Characteristics-Galerkin 𝑃1 Galerkin

Fig. 2.10: Rotating hill

(continued from previous page)


19

20 varf rhs(vo,vh)= int2d(Th)(vo*vh/dt);


21

22 real[int] viso=[-0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1.1];
23

24 matrix AA=Adual(Vh,Vh,solver=GMRES);
25 matrix BB=rhs(Vh,Vh);
26

27 for ( t=0; t< tmax ; t+=dt)


28 {
29 vo[]=v[];
30 vh[]=BB*vo[];
31 v[]=AA^-1*vh[];
32 plot(v,fill=0,viso=viso,cmm=" t="+t + ", min=" + v[].min + ", max=" + v[].max);
33 };

2.9 The System of elasticity

Elasticity
Solid objects deform under the action of applied forces:
a point in the solid, originally at (𝑥, 𝑦, 𝑧) will come to (𝑋, 𝑌, 𝑍) after some time; the vector u = (𝑢1 , 𝑢2 , 𝑢3 ) =
(𝑋 − 𝑥, 𝑌 − 𝑦, 𝑍 − 𝑧) is called the displacement. When the displacement is small and the solid is elastic, Hooke’s law
gives a relationship between the stress tensor 𝜎(𝑢) = (𝜎𝑖𝑗 (𝑢)) and the strain tensor 𝜖(𝑢) = 𝜖𝑖𝑗 (𝑢)

𝜎𝑖𝑗 (𝑢) = 𝜆𝛿𝑖𝑗 ∇.u + 2𝜇𝜖𝑖𝑗 (𝑢),

56 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

where the Kronecker symbol 𝛿𝑖𝑗 = 1 if 𝑖 = 𝑗, 0 otherwise, with


1 𝜕𝑢𝑖 𝜕𝑢𝑗
𝜖𝑖𝑗 (𝑢) = ( + ),
2 𝜕𝑥𝑗 𝜕𝑥𝑖
and where 𝜆, 𝜇 are two constants that describe the mechanical properties of the solid, and are themselves related to the
better known constants 𝐸, Young’s modulus, and 𝜈, Poisson’s ratio:
𝐸 𝐸𝜈
𝜇= , 𝜆= .
2(1 + 𝜈) (1 + 𝜈)(1 − 2𝜈)
Lamé’s system
Let us consider a beam with axis 𝑂𝑧 and with perpendicular section Ω. The components along 𝑥 and 𝑦 of the strain
u(𝑥) in a section Ω subject to forces f perpendicular to the axis are governed by:

−𝜇∆u − (𝜇 + 𝜆)∇(∇.u) = f in Ω,

where 𝜆 ,𝜇 are the Lamé coefficients introduced above.


Remark, we do not use this equation because the associated variational form does not give the right boundary condition,
we simply use:

−𝑑𝑖𝑣(𝜎) = f in Ω

where the corresponding variational form is:


∫︁ ∫︁
𝜎(𝑢) : 𝜖(v) 𝑑𝑥 − v𝑓 𝑑𝑥 = 0;
Ω Ω

where : denotes the tensor scalar product, i.e. 𝑎 : 𝑏 = 𝑎𝑖𝑗 𝑏𝑖𝑗 .


∑︀
𝑖,𝑗

So the variational form can be written as :


∫︁ ∫︁
𝜆∇.𝑢∇.𝑣 + 2𝜇𝜖(u) : 𝜖(v) 𝑑𝑥 − v𝑓 𝑑𝑥 = 0;
Ω Ω

Tip: Consider an elastic plate with the undeformed rectangle shape [0, 20] × [−1, 1].
The body force is the gravity force f and the boundary force g is zero on lower, upper and right sides. The left vertical
side of the beam is fixed. The boundary conditions are:

𝜎.n =g =0 on Γ1 , Γ4 , Γ3 ,
u =0 on Γ2

Here u = (𝑢, 𝑣) has two components.


The above two equations are strongly coupled by their mixed derivatives, and thus any iterative solution on each of the
components is risky. One should rather use FreeFEM’s system approach and write:

1 // Parameters
2 real E = 21e5;
3 real nu = 0.28;
4

5 real f = -1;
6

7 // Mesh
(continues on next page)

2.9. The System of elasticity 57


FreeFEM Documentation, Release 4.8

(continued from previous page)


8 mesh Th = square(10, 10, [20*x,2*y-1]);
9

10 // Fespace
11 fespace Vh(Th, P2);
12 Vh u, v;
13 Vh uu, vv;
14

15 // Macro
16 real sqrt2=sqrt(2.);
17 macro epsilon(u1,u2) [dx(u1),dy(u2),(dy(u1)+dx(u2))/sqrt2] //
18 // The sqrt2 is because we want: epsilon(u1,u2)'* epsilon(v1,v2) = epsilon(u): epsilon(v)
19 macro div(u,v) ( dx(u)+dy(v) ) //
20

21 // Problem
22 real mu= E/(2*(1+nu));
23 real lambda = E*nu/((1+nu)*(1-2*nu));
24

25 solve lame([u, v], [uu, vv])


26 = int2d(Th)(
27 lambda * div(u, v) * div(uu, vv)
28 + 2.*mu * ( epsilon(u,v)' * epsilon(uu,vv) )
29 )
30 - int2d(Th)(
31 f*vv
32 )
33 + on(4, u=0, v=0)
34 ;
35

36 // Plot
37 real coef=100;
38 plot([u, v], wait=1, ps="lamevect.eps", coef=coef);
39

40 // Move mesh
41 mesh th1 = movemesh(Th, [x+u*coef, y+v*coef]);
42 plot(th1,wait=1,ps="lamedeform.eps");
43

44 // Output
45 real dxmin = u[].min;
46 real dymin = v[].min;
47

48 cout << " - dep. max x = "<< dxmin << " y=" << dymin << endl;
49 cout << " dep. (20, 0) = " << u(20, 0) << " " << v(20, 0) << endl;

The output is:

1 -- square mesh : nb vertices =121 , nb triangles = 200 , nb boundary edges 40


2 -- Solve : min -0.00174137 max 0.00174105
3 min -0.0263154 max 1.47016e-29
4 - dep. max x = -0.00174137 y=-0.0263154
5 dep. (20,0) = -1.8096e-07 -0.0263154
6 times: compile 0.010219s, execution 1.5827s

Solution of Lamé’s equations for elasticity for a 2D beam deflected by its own weight and clamped by its left vertical

58 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(a) Vector (b) Deformation

Fig. 2.11: Elasticity

side is shown Fig. 2.11a and Fig. 2.11b. Result are shown with a amplification factor equal to 100. The size of the
arrow is automatically bound, but the color gives the real length.

2.10 The System of Stokes for Fluids

In the case of a flow invariant with respect to the third coordinate (two-dimensional flow), flows at low Reynolds number
(for instance micro-organisms) satisfy,

−∆u + ∇𝑝 = 0
∇·u = 0

where u = (𝑢1 , 𝑢2 ) is the fluid velocity and 𝑝 its pressure.


The driven cavity is a standard test. It is a box full of liquid with its lid moving horizontally at speed one. The pressure
and the velocity must be discretized in compatible fintie element spaces for the LBB conditions to be satisfied:
(u, ∇𝑝)
sup ≥ 𝛽|u| ∀u ∈ 𝑈ℎ
𝑝∈𝑃ℎ |𝑝|

1 // Parameters
2 int nn = 30;
3

4 // Mesh
5 mesh Th = square(nn, nn);
6

7 // Fespace
8 fespace Uh(Th, P1b);
9 Uh u, v;
10 Uh uu, vv;
11

12 fespace Ph(Th, P1);


13 Ph p, pp;
14

15 // Problem
16 solve stokes ([u, v, p], [uu, vv, pp])
17 = int2d(Th)(
18 dx(u)*dx(uu)
19 + dy(u)*dy(uu)
20 + dx(v)*dx(vv)
21 + dy(v)*dy(vv)
22 + dx(p)*uu
23 + dy(p)*vv
24 + pp*(dx(u) + dy(v))
(continues on next page)

2.10. The System of Stokes for Fluids 59


FreeFEM Documentation, Release 4.8

(continued from previous page)


25 - 1e-10*p*pp
26 )
27 + on(1, 2, 4, u=0, v=0)
28 + on(3, u=1, v=0)
29 ;
30

31 // Plot
32 plot([u, v], p, wait=1);

Note: We add a stabilization term −10e − 10 * p * pp to fix the constant part of the pressure.

Results are shown on Fig. 2.12.

2.11 A projection algorithm for the Navier-Stokes equations

Summary : Fluid flows require good algorithms and good triangultions. We show here an example of a complex
algorithm and or first example of mesh adaptation.
An incompressible viscous fluid satisfies:

𝜕𝑡 u + u · ∇u + ∇𝑝 − 𝜈∆u =0 in Ω×]0, 𝑇 [
∇·u =0 in Ω×]0, 𝑇 [
u|𝑡=0 = u0
u|Γ = uΓ

A possible algorithm, proposed by Chorin, is:


1 𝑚+1
𝛿𝑡 [u − u𝑚 𝑜X𝑚 ] + ∇𝑝𝑚 − 𝜈∆u𝑚 = 0
u|Γ = uΓ
𝜈𝜕𝑛 u|Γ𝑜𝑢𝑡 = 0

−∆𝑝𝑚+1 = −∇ · u𝑚 𝑜X𝑚
𝜕𝑛 𝑝𝑚+1 =0 on Γ
𝑝𝑚+1 =0 on Γ𝑜𝑢𝑡
where u𝑜X(𝑥) = u(x − u(x)𝛿𝑡) since 𝜕𝑡 u + u · ∇u is approximated by the method of characteristics, as in the
previous section.
We use the Chorin’s algorithm with free boundary condition at outlet (i.e. 𝑝 = 0, 𝜈𝜕𝑛 𝑢 = 0), to compute a correction,
q, to the pressure.

−∆𝑞 =∇·u
𝑞 = 0 on Γ𝑜𝑢𝑡

and define
u𝑚+1 = ũ + 𝑃 ∇𝑞𝛿𝑡
𝑝𝑚+1 = 𝑝𝑚 − 𝑞

where ũ is the (u𝑚+1 , 𝑣 𝑚+1 ) of Chorin’s algorithm, and where 𝑃 is the 𝐿2 projection with mass lumping ( a sparse
matrix).
The backward facing step

60 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

Fig. 2.12: Solution of Stokes’ equations for the driven cavity problem, showing the velocity field and the pressure level
lines.

2.11. A projection algorithm for the Navier-Stokes equations 61


FreeFEM Documentation, Release 4.8

The geometry is that of a channel with a backward facing step so that the inflow section is smaller than the outflow
section. This geometry produces a fluid recirculation zone that must be captured correctly.
This can only be done if the triangulation is sufficiently fine, or well adapted to the flow.

Note: There is a technical difficulty in the example: the output B.C. Here we put 𝑝 = 0 and 𝜈𝜕𝑛 𝑢 = 0.

1 // Parameters
2 verbosity = 0;
3 int nn = 1;
4 real nu = 0.0025;
5 real dt = 0.2;
6 real epsv = 1e-6;
7 real epsu = 1e-6;
8 real epsp = 1e-6;
9

10 // Mesh
11 border a0(t=1, 0){x=-2; y=t; label=1;}
12 border a1(t=-2, 0){x=t; y=0; label=2;}
13 border a2(t=0, -0.5){x=0; y=t; label=2;}
14 border a3(t=0, 1){x=18*t^1.2; y=-0.5; label=2;}
15 border a4(t=-0.5, 1){x=18; y=t; label=3;}
16 border a5(t=1, 0){x=-2+20*t; y=1; label=4;}
17

18 mesh Th = buildmesh(a0(3*nn) + a1(20*nn) + a2(10*nn) + a3(150*nn) + a4(5*nn) +␣


˓→a5(100*nn));

19 plot(Th);
20

21 // Fespace
22 fespace Vh(Th, P1);
23 Vh w;
24 Vh u=0, v=0;
25 Vh p=0;
26 Vh q=0;
27

28 // Definition of Matrix dtMx and dtMy


29 matrix dtM1x, dtM1y;
30

31 // Macro
32 macro BuildMat()
33 { /* for memory managenemt */
34 varf vM(unused, v) = int2d(Th)(v);
35 varf vdx(u, v) = int2d(Th)(v*dx(u)*dt);
36 varf vdy(u, v) = int2d(Th)(v*dy(u)*dt);
37

38 real[int] Mlump = vM(0, Vh);


39 real[int] one(Vh.ndof); one = 1;
40 real[int] M1 = one ./ Mlump;
41 matrix dM1 = M1;
42 matrix Mdx = vdx(Vh, Vh);
43 matrix Mdy = vdy(Vh, Vh);
44 dtM1x = dM1*Mdx;
(continues on next page)

62 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


45 dtM1y = dM1*Mdy;
46 } //
47

48 // Build matrices
49 BuildMat
50

51 // Time iterations
52 real err = 1.;
53 real outflux = 1.;
54 for(int n = 0; n < 300; n++){
55 // Update
56 Vh uold=u, vold=v, pold=p;
57

58 // Solve
59 solve pb4u (u, w, init=n, solver=CG, eps=epsu)
60 = int2d(Th)(
61 u*w/dt
62 + nu*(dx(u)*dx(w) + dy(u)*dy(w))
63 )
64 -int2d(Th)(
65 convect([uold, vold], -dt, uold)/dt*w
66 - dx(p)*w
67 )
68 + on(1, u=4*y*(1-y))
69 + on(2, 4, u=0)
70 ;
71

72 plot(u);
73

74 solve pb4v (v, w, init=n, solver=CG, eps=epsv)


75 = int2d(Th)(
76 v*w/dt
77 + nu*(dx(v)*dx(w) + dy(v)*dy(w))
78 )
79 -int2d(Th)(
80 convect([uold,vold],-dt,vold)/dt*w
81 - dy(p)*w
82 )
83 +on(1, 2, 3, 4, v=0)
84 ;
85

86 solve pb4p (q, w, solver=CG, init=n, eps=epsp)


87 = int2d(Th)(
88 dx(q)*dx(w)+dy(q)*dy(w)
89 )
90 - int2d(Th)(
91 (dx(u)+ dy(v))*w/dt
92 )
93 + on(3, q=0)
94 ;
95

96 //to have absolute epsilon in CG algorithm.


(continues on next page)

2.11. A projection algorithm for the Navier-Stokes equations 63


FreeFEM Documentation, Release 4.8

(a) Adapted mesh

(b) Pressure

(c) Velocity

Fig. 2.13: Navier-Stokes projection

(continued from previous page)


97 epsv = -abs(epsv);
98 epsu = -abs(epsu);
99 epsp = -abs(epsp);
100

101 p = pold-q;
102 u[] += dtM1x*q[];
103 v[] += dtM1y*q[];
104

105 // Mesh adaptation


106 if (n%50 == 49){
107 Th = adaptmesh(Th, [u, v], q, err=0.04, nbvx=100000);
108 plot(Th, wait=true);
109 BuildMat // Rebuild mat.
110 }
111

112 // Error & Outflux


113 err = sqrt(int2d(Th)(square(u-uold)+square(v-vold))/Th.area);
114 outflux = int1d(Th)([u,v]'*[N.x,N.y]);
115 cout << " iter " << n << " Err L2 = " << err << " outflux = " << outflux << endl;
116 if(err < 1e-3) break;
117 }
118

119 // Verification
120 assert(abs(outflux)< 2e-3);
121

122 // Plot
123 plot(p, wait=1, ps="NSprojP.eps");
124 plot(u, wait=1, ps="NSprojU.eps");

Rannacher’s projection algorithm: result on an adapted mesh, Fig. 2.13a, showing the pressure, Fig. 2.13b, and the
horizontal velocity Fig. 2.13c for a Reynolds number of 400 where mesh adaptation is done after 50 iterations on the
first mesh.

64 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

2.12 Newton Method for the Steady Navier-Stokes equations

The problem is find the velocity field u = (𝑢𝑖 )𝑑𝑖=1 and the pressure 𝑝 of a Flow satisfying in the domain Ω ⊂ R𝑑 (𝑑 =
2, 3):

(u · ∇)u − 𝜈∆u + ∇𝑝 = 0
∇·u = 0

where 𝜈 is the viscosity of the fluid, ∇ = (𝜕𝑖 )𝑑𝑖=1 , the dot product is ·, and ∆ = ∇ · ∇ with the same boundary
conditions (u is given on Γ).
The weak form is find u, 𝑝 such that for ∀v (zero on Γ), and ∀𝑞:
∫︁
((u · ∇)u).v + 𝜈∇u : ∇v − 𝑝∇ · v − 𝑞∇ · u = 0
Ω

The Newton Algorithm to solve nonlinear problem is:


Find 𝑢 ∈ 𝑉 such that 𝐹 (𝑢) = 0 where 𝐹 : 𝑉 ↦→ 𝑉 .
1. choose 𝑢0 ∈ R𝑛 , ;
2. for ( 𝑖 = 0; 𝑖 < niter; 𝑖 = 𝑖 + 1)
1. solve 𝐷𝐹 (𝑢𝑖 )𝑤𝑖 = 𝐹 (𝑢𝑖 );
2. 𝑢𝑖+1 = 𝑢𝑖 − 𝑤𝑖 ;
break ||𝑤𝑖 || < 𝜀.
Where 𝐷𝐹 (𝑢) is the differential of 𝐹 at point 𝑢, this is a linear application such that:

𝐹 (𝑢 + 𝛿) = 𝐹 (𝑢) + 𝐷𝐹 (𝑢)𝛿 + 𝑜(𝛿)

For Navier Stokes, 𝐹 and 𝐷𝐹 are:


∫︀
𝐹 (u, 𝑝) = ∫︀Ω ((u · ∇)u).v + 𝜈∇u : ∇v − 𝑝∇ · v − 𝑞∇ · u
𝐷𝐹 (u, 𝑝)(𝛿u, 𝛿𝑝) = Ω ((𝛿u · ∇)u).v + ((u · ∇)𝛿u).v
+ 𝜈∇𝛿u : ∇v − 𝛿𝑝∇ · v − 𝑞∇ · 𝛿u

So the Newton algorithm become:

1 // Parameters
2 real R = 5.;
3 real L = 15.;
4

5 real nu = 1./50.;
6 real nufinal = 1/200.;
7 real cnu = 0.5;
8

9 real eps = 1e-6;


10

11 verbosity = 0;
12

13 // Mesh
14 border cc(t=0, 2*pi){x=cos(t)/2.; y=sin(t)/2.; label=1;}
15 border ce(t=pi/2, 3*pi/2){x=cos(t)*R; y=sin(t)*R; label=1;}
16 border beb(tt=0, 1){real t=tt^1.2; x=t*L; y=-R; label=1;}
(continues on next page)

2.12. Newton Method for the Steady Navier-Stokes equations 65


FreeFEM Documentation, Release 4.8

(continued from previous page)


17 border beu(tt=1, 0){real t=tt^1.2; x=t*L; y=R; label=1;}
18 border beo(t=-R, R){x=L; y=t; label=0;}
19 border bei(t=-R/4, R/4){x=L/2; y=t; label=0;}
20 mesh Th = buildmesh(cc(-50) + ce(30) + beb(20) + beu(20) + beo(10) + bei(10));
21 plot(Th);
22

23 //bounding box for the plot


24 func bb = [[-1,-2],[4,2]];
25

26 // Fespace
27 fespace Xh(Th, P2);
28 Xh u1, u2;
29 Xh v1,v2;
30 Xh du1,du2;
31 Xh u1p,u2p;
32

33 fespace Mh(Th,P1);
34 Mh p;
35 Mh q;
36 Mh dp;
37 Mh pp;
38

39 // Macro
40 macro Grad(u1,u2) [dx(u1), dy(u1), dx(u2),dy(u2)] //
41 macro UgradV(u1,u2,v1,v2) [[u1,u2]'*[dx(v1),dy(v1)],
42 [u1,u2]'*[dx(v2),dy(v2)]] //
43 macro div(u1,u2) (dx(u1) + dy(u2)) //
44

45 // Initialization
46 u1 = (x^2+y^2) > 2;
47 u2 = 0;
48

49 // Viscosity loop
50 while(1){
51 int n;
52 real err=0;
53 // Newton loop
54 for (n = 0; n < 15; n++){
55 // Newton
56 solve Oseen ([du1, du2, dp], [v1, v2, q])
57 = int2d(Th)(
58 nu * (Grad(du1,du2)' * Grad(v1,v2))
59 + UgradV(du1,du2, u1, u2)' * [v1,v2]
60 + UgradV( u1, u2,du1,du2)' * [v1,v2]
61 - div(du1,du2) * q
62 - div(v1,v2) * dp
63 - 1e-8*dp*q //stabilization term
64 )
65 - int2d(Th) (
66 nu * (Grad(u1,u2)' * Grad(v1,v2))
67 + UgradV(u1,u2, u1, u2)' * [v1,v2]
68 - div(u1,u2) * q
(continues on next page)

66 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


69 - div(v1,v2) * p
70 )
71 + on(1, du1=0, du2=0)
72 ;
73

74 u1[] -= du1[];
75 u2[] -= du2[];
76 p[] -= dp[];
77

78 real Lu1=u1[].linfty, Lu2=u2[].linfty, Lp=p[].linfty;


79 err = du1[].linfty/Lu1 + du2[].linfty/Lu2 + dp[].linfty/Lp;
80

81 cout << n << " err = " << err << " " << eps << " rey = " << 1./nu << endl;
82 if(err < eps) break; //converge
83 if( n>3 && err > 10.) break; //blowup
84 }
85

86 if(err < eps){ //converge: decrease $\nu$ (more difficult)


87 // Plot
88 plot([u1, u2], p, wait=1, cmm=" rey = " + 1./nu , coef=0.3, bb=bb);
89

90 // Change nu
91 if( nu == nufinal) break;
92 if( n < 4) cnu = cnu^1.5; //fast converge => change faster
93 nu = max(nufinal, nu* cnu); //new viscosity
94

95 // Update
96 u1p = u1;
97 u2p = u2;
98 pp = p;
99 }
100 else{ //blowup: increase $\nu$ (more simple)
101 assert(cnu< 0.95); //the method finally blowup
102

103 // Recover nu
104 nu = nu/cnu;
105 cnu= cnu^(1./1.5); //no conv. => change lower
106 nu = nu* cnu; //new viscosity
107 cout << " restart nu = " << nu << " Rey = " << 1./nu << " (cnu = " << cnu << " )␣
˓→ \n";
108

109 // Recover a correct solution


110 u1 = u1p;
111 u2 = u2p;
112 p = pp;
113 }
114 }

Note: We use a trick to make continuation on the viscosity 𝜈, because the Newton method blowup owe start with the
final viscosity 𝜈.

2.12. Newton Method for the Steady Navier-Stokes equations 67


FreeFEM Documentation, Release 4.8

(a) Mesh (b) Velocity and pressure

Fig. 2.14: Naver-Stokes newton

𝜈 is gradually increased to the desired value.

2.13 A Large Fluid Problem

A friend of one of us in Auroville-India was building a ramp to access an air conditioned room. As I was visiting the
construction site he told me that he expected to cool air escaping by the door to the room to slide down the ramp and
refrigerate the feet of the coming visitors. I told him “no way” and decided to check numerically.
The fluid velocity and pressure are solution of the Navier-Stokes equations with varying density function of the tem-
perature.
The geometry is trapezoidal with prescribed inflow made of cool air at the bottom and warm air above and so are the
initial conditions; there is free outflow, slip velocity at the top (artificial) boundary and no-slip at the bottom. However
the Navier-Stokes cum temperature equations have a RANS 𝑘 − 𝜖 model and a Boussinesq approximation for the
buoyancy. This comes to :

𝜕𝑡 𝜃 + 𝑢∇𝜃 − ∇ · (𝜅𝑚 𝑇 ∇𝜃) = 0


𝜕𝑡 𝑢 + 𝑢∇𝑢 − ∇ · (𝜇𝑇 ∇𝑢) + ∇𝑝 + 𝑒(𝜃 − 𝜃0 )⃗𝑒2 = 0
∇·𝑢 = 0
2
𝜇𝑇 = 𝑐𝜇 𝑘𝜖
𝜅𝑇 = 𝜅𝜇𝑇
𝜇𝑇 𝑇 2
𝜕𝑡 𝑘 + 𝑢∇𝑘 + 𝜖 − ∇ · (𝜇𝑇 ∇𝑘) = 2 |∇𝑢 + ∇𝑢 |
2
𝜕𝑡 𝜖 + 𝑢∇𝜖 + 𝑐2 𝜖𝑘 − 𝑐𝑐𝜇𝜖 ∇ · (𝜇𝑇 ∇𝜖) = 𝑐1 𝑇 2
2 𝑘|∇𝑢 + ∇𝑢 |

We use a time discretization which preserves positivity and uses the method of characteristics (𝑋 𝑚 (𝑥) ≈ 𝑥−𝑢𝑚 (𝑥)𝛿𝑡)
1 𝑚+1
𝛿𝑡 (𝜃 − 𝜃𝑚 ∘ 𝑋 𝑚 ) − ∇ · (𝜅𝑚
𝑇 ∇𝜃
𝑚+1
) = 0
1 𝑚+1 𝑚 𝑚 𝑚 𝑚+1 𝑚+1 𝑚+1
𝛿𝑡 (𝑢 −𝑢 ∘𝑋 )−∇· (𝜇𝑇 ∇𝑢 ) + ∇𝑝 + 𝑒(𝜃 − 𝜃0 )⃗𝑒2 = 0
𝑚+1
∇·𝑢 = 0
𝑚+1 𝜖𝑚 𝜇𝑚
1
𝛿𝑡 (𝑘 𝑚+1
− 𝑘 𝑚
∘ 𝑋 𝑚
) + 𝑘 𝑘𝑚𝑚 − ∇ · (𝜇 𝑚
𝑇 ∇𝑘 𝑚+1
) = 2𝑇 |∇𝑢𝑚 + ∇𝑢𝑚 𝑇 |2
1 𝑚+1
− 𝜖𝑚 ∘ 𝑋 𝑚 ) + 𝑐2 𝜖𝑚+1 𝑘𝜖 𝑚 − 𝑐𝑐𝜇𝜖 ∇(𝜇˙ 𝑚 ∇𝜖𝑚+1 ) = 𝑐1 𝑘 𝑚 |∇𝑢𝑚 + ∇𝑢𝑚 𝑇 |2
𝛿𝑡 (𝜖 𝑇 2
𝑚+1 2
𝜇𝑚+1
𝑇 = 𝑐𝜇 𝑘𝜖𝑚+1
𝜅𝑚+1
𝑇 = 𝜅𝜇𝑚+1
𝑇

In variational form and with appropriated boundary conditions the problem is :

68 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

1 load "iovtk"
2

3 verbosity=0;
4

5 // Parameters
6 int nn = 15;
7 int nnPlus = 5;
8 real l = 1.;
9 real L = 15.;
10 real hSlope = 0.1;
11 real H = 6.;
12 real h = 0.5;
13

14 real reylnods =500;


15 real beta = 0.01;
16

17 real eps = 9.81/303.;


18 real nu = 1;
19 real numu = nu/sqrt(0.09);
20 real nuep = pow(nu,1.5)/4.1;
21 real dt = 0.;
22

23 real Penalty = 1.e-6;


24

25 // Mesh
26 border b1(t=0, l){x=t; y=0;}
27 border b2(t=0, L-l){x=1.+t; y=-hSlope*t;}
28 border b3(t=-hSlope*(L-l), H){x=L; y=t;}
29 border b4(t=L, 0){x=t; y=H;}
30 border b5(t=H, h){x=0; y=t;}
31 border b6(t=h, 0){x=0; y=t;}
32

33 mesh Th=buildmesh(b1(nnPlus*nn*l) + b2(nn*sqrt((L-l)^2+(hSlope*(L-l))^2)) + b3(nn*(H +␣


˓→hSlope*(L-l))) + b4(nn*L) + b5(nn*(H-h)) + b6(nnPlus*nn*h));

34 plot(Th);
35

36 // Fespaces
37 fespace Vh2(Th, P1b);
38 Vh2 Ux, Uy;
39 Vh2 Vx, Vy;
40 Vh2 Upx, Upy;
41

42 fespace Vh(Th,P1);
43 Vh p=0, q;
44 Vh Tp, T=35;
45 Vh k=0.0001, kp=k;
46 Vh ep=0.0001, epp=ep;
47

48 fespace V0h(Th,P0);
49 V0h muT=1;
50 V0h prodk, prode;
51 Vh kappa=0.25e-4, stress;
52
(continues on next page)

2.13. A Large Fluid Problem 69


FreeFEM Documentation, Release 4.8

(continued from previous page)


53 // Macro
54 macro grad(u) [dx(u), dy(u)] //
55 macro Grad(U) [grad(U#x), grad(U#y)] //
56 macro Div(U) (dx(U#x) + dy(U#y)) //
57

58 // Functions
59 func g = (x) * (1-x) * 4;
60

61 // Problem
62 real alpha = 0.;
63

64 problem Temperature(T, q)
65 = int2d(Th)(
66 alpha * T * q
67 + kappa* grad(T)' * grad(q)
68 )
69 + int2d(Th)(
70 - alpha*convect([Upx, Upy], -dt, Tp)*q
71 )
72 + on(b6, T=25)
73 + on(b1, b2, T=30)
74 ;
75

76 problem KineticTurbulence(k, q)
77 = int2d(Th)(
78 (epp/kp + alpha) * k * q
79 + muT* grad(k)' * grad(q)
80 )
81 + int2d(Th)(
82 prodk * q
83 - alpha*convect([Upx, Upy], -dt, kp)*q
84 )
85 + on(b5, b6, k=0.00001)
86 + on(b1, b2, k=beta*numu*stress)
87 ;
88

89 problem ViscosityTurbulence(ep, q)
90 = int2d(Th)(
91 (1.92*epp/kp + alpha) * ep * q
92 + muT * grad(ep)' * grad(q)
93 )
94 + int1d(Th, b1, b2)(
95 T * q * 0.001
96 )
97 + int2d(Th)(
98 prode * q
99 - alpha*convect([Upx, Upy], -dt, epp)*q
100 )
101 + on(b5, b6, ep=0.00001)
102 + on(b1, b2, ep=beta*nuep*pow(stress,1.5))
103 ;
104

(continues on next page)

70 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


105 // Initialization with stationary solution
106 solve NavierStokes ([Ux, Uy, p], [Vx, Vy, q])
107 = int2d(Th)(
108 alpha * [Ux, Uy]' * [Vx, Vy]
109 + muT * (Grad(U) : Grad(V))
110 + p * q * Penalty
111 - p * Div(V)
112 - Div(U) * q
113 )
114 + int1d(Th, b1, b2, b4)(
115 Ux * Vx * 0.1
116 )
117 + int2d(Th)(
118 eps * (T-35) * Vx
119 - alpha*convect([Upx, Upy], -dt, Upx)*Vx
120 - alpha*convect([Upx, Upy], -dt, Upy)*Vy
121 )
122 + on(b6, Ux=3, Uy=0)
123 + on(b5, Ux=0, Uy=0)
124 + on(b1, b4, Uy=0)
125 + on(b2, Uy=-Upx*N.x/N.y)
126 + on(b3, Uy=0)
127 ;
128

129 plot([Ux, Uy], p, value=true, coef=0.2, cmm="[Ux, Uy] - p");


130

131 {
132 real[int] xx(21), yy(21), pp(21);
133 for (int i = 0 ; i < 21; i++){
134 yy[i] = i/20.;
135 xx[i] = Ux(0.5,i/20.);
136 pp[i] = p(i/20.,0.999);
137 }
138 cout << " " << yy << endl;
139 plot([xx, yy], wait=true, cmm="Ux x=0.5 cup");
140 plot([yy, pp], wait=true, cmm="p y=0.999 cup");
141 }
142

143 // Initialization
144 dt = 0.1; //probably too big
145 int nbiter = 3;
146 real coefdt = 0.25^(1./nbiter);
147 real coefcut = 0.25^(1./nbiter);
148 real cut = 0.01;
149 real tol = 0.5;
150 real coeftol = 0.5^(1./nbiter);
151 nu = 1./reylnods;
152

153 T = T - 10*((x<1)*(y<0.5) + (x>=1)*(y+0.1*(x-1)<0.5));


154

155 // Convergence loop


156 real T0 = clock();
(continues on next page)

2.13. A Large Fluid Problem 71


FreeFEM Documentation, Release 4.8

(continued from previous page)


157 for (int iter = 1; iter <= nbiter; iter++){
158 cout << "Iteration " << iter << " - dt = " << dt << endl;
159 alpha = 1/dt;
160

161 // Time loop


162 real t = 0.;
163 for (int i = 0; i <= 500; i++){
164 t += dt;
165 cout << "Time step " << i << " - t = " << t << endl;
166

167 // Update
168 Upx = Ux;
169 Upy = Uy;
170 kp = k;
171 epp = ep;
172 Tp = max(T, 25); //for beauty only should be removed
173 Tp = min(Tp, 35); //for security only should be removed
174 kp = max(k, 0.0001); epp = max(ep, 0.0001); // to be secure: should not be active
175 muT = 0.09*kp*kp/epp;
176

177 // Solve NS
178 NavierStokes;
179

180 // Update
181 prode = -0.126*kp*(pow(2*dx(Ux),2)+pow(2*dy(Uy),2)+2*pow(dx(Uy)+dy(Ux),2))/2;
182 prodk = -prode*kp/epp*0.09/0.126;
183 kappa = muT/0.41;
184 stress = abs(dy(Ux));
185

186 // Solve k-eps-T


187 KineticTurbulence;
188 ViscosityTurbulence;
189 Temperature;
190

191 // Plot
192 plot(T, value=true, fill=true);
193 plot([Ux, Uy], p, coef=0.2, cmm=" [Ux, Uy] - p", WindowIndex=1);
194

195 // Time
196 cout << "\tTime = " << clock()-T0 << endl;
197 }
198

199 // Check
200 if (iter >= nbiter) break;
201

202 // Adaptmesh
203 Th = adaptmesh(Th, [dx(Ux), dy(Ux), dx(Ux), dy(Uy)], splitpbedge=1, abserror=0,␣
˓→cutoff=cut, err=tol, inquire=0, ratio=1.5, hmin=1./1000);

204 plot(Th);
205

206 // Update
207 dt = dt * coefdt;
(continues on next page)

72 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


208 tol = tol * coeftol;
209 cut = cut * coefcut;
210 }
211 cout << "Total Time = " << clock()-T0 << endl;

2.14 An Example with Complex Numbers

In a microwave oven heat comes from molecular excitation by an electromagnetic field. For a plane monochromatic
wave, amplitude is given by Helmholtz’s equation:

𝛽𝑣 + ∆𝑣 = 0.

We consider a rectangular oven where the wave is emitted by part of the upper wall. So the boundary
(︂ of)︂the domain is
𝑦−𝑐
made up of a part Γ1 where 𝑣 = 0 and of another part Γ2 = [𝑐, 𝑑] where for instance 𝑣 = sin 𝜋 .
𝑐−𝑑
Within an object to be cooked, denoted by 𝐵, the heat source is proportional to 𝑣 2 . At equilibrium, one has :

−∆𝜃 = 𝑣 2 𝐼𝐵
𝜃Γ = 0

where 𝐼𝐵 is 1 in the object and 0 elsewhere.



In the program below 𝛽 = 1/(1 − 𝑖/2) in the air and 2/(1 − 𝑖/2) in the object (𝑖 = −1):

1 // Parameters
2 int nn = 2;
3 real a = 20.;
4 real b = 20.;
5 real c = 15.;
6 real d = 8.;
7 real e = 2.;
8 real l = 12.;
9 real f = 2.;
10 real g = 2.;
11

12 // Mesh
13 border a0(t=0, 1){x=a*t; y=0; label=1;}
14 border a1(t=1, 2){x=a; y=b*(t-1); label=1;}
15 border a2(t=2, 3){ x=a*(3-t); y=b; label=1;}
16 border a3(t=3, 4){x=0; y=b-(b-c)*(t-3); label=1;}
17 border a4(t=4, 5){x=0; y=c-(c-d)*(t-4); label=2;}
18 border a5(t=5, 6){x=0; y=d*(6-t); label=1;}
19

20 border b0(t=0, 1){x=a-f+e*(t-1); y=g; label=3;}


21 border b1(t=1, 4){x=a-f; y=g+l*(t-1)/3; label=3;}
22 border b2(t=4, 5){x=a-f-e*(t-4); y=l+g; label=3;}
23 border b3(t=5, 8){x=a-e-f; y=l+g-l*(t-5)/3; label=3;}
24

25 mesh Th = buildmesh(a0(10*nn) + a1(10*nn) + a2(10*nn) + a3(10*nn) +a4(10*nn) + a5(10*nn)


26 + b0(5*nn) + b1(10*nn) + b2(5*nn) + b3(10*nn));
27 real meat = Th(a-f-e/2, g+l/2).region;
(continues on next page)

2.14. An Example with Complex Numbers 73


FreeFEM Documentation, Release 4.8

(a) Temperature at time step 100 (b) Velocity at time step 100

(c) Temperature at time step 200 (d) Velocity at time step 200

(e) Temperature at time step 300 (f) Velocity at time step 300

(g) Temperature at time step 400 (h) Velocity at time step 400

(i) Temperature at time step 500 (j) Velocity at time step 500

Fig. 2.15: A large fluid problem

74 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


28 real air= Th(0.01,0.01).region;
29 plot(Th, wait=1);
30

31 // Fespace
32 fespace Vh(Th, P1);
33 Vh R=(region-air)/(meat-air);
34 Vh<complex> v, w;
35 Vh vr, vi;
36

37 fespace Uh(Th, P1);


38 Uh u, uu, ff;
39

40 // Problem
41 solve muwave(v, w)
42 = int2d(Th)(
43 v*w*(1+R)
44 - (dx(v)*dx(w) + dy(v)*dy(w))*(1 - 0.5i)
45 )
46 + on(1, v=0)
47 + on(2, v=sin(pi*(y-c)/(c-d)))
48 ;
49

50 vr = real(v);
51 vi = imag(v);
52

53 // Plot
54 plot(vr, wait=1, ps="rmuonde.ps", fill=true);
55 plot(vi, wait=1, ps="imuonde.ps", fill=true);
56

57 // Problem (temperature)
58 ff=1e5*(vr^2 + vi^2)*R;
59

60 solve temperature(u, uu)


61 = int2d(Th)(
62 dx(u)* dx(uu)+ dy(u)* dy(uu)
63 )
64 - int2d(Th)(
65 ff*uu
66 )
67 + on(1, 2, u=0)
68 ;
69

70 // Plot
71 plot(u, wait=1, ps="tempmuonde.ps", fill=true);

Results are shown on Fig. 2.16a, Fig. 2.16b and Fig. 2.16c.

2.14. An Example with Complex Numbers 75


FreeFEM Documentation, Release 4.8

(a) Real part (b) Imaginary part

(c) Temperature

Fig. 2.16: Microwave

76 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

2.15 Optimal Control

Thanks to the function BFGS it is possible to solve complex nonlinear optimization problem within FreeFEM. For
example consider the following inverse problem

min𝑏,𝑐,𝑑∈𝑅 𝐽 = 𝐸 (𝑢 − 𝑢𝑑 )2
∫︀

−∇(𝜅(𝑏, 𝑐, 𝑑) · ∇𝑢) = 0
𝑢|Γ = 𝑢Γ

where the desired state 𝑢𝑑 , the boundary data 𝑢Γ and the observation set 𝐸 ⊂ Ω are all given. Furthermore let us
assume that:

𝜅(𝑥) = 1 + 𝑏𝐼𝐵 (𝑥) + 𝑐𝐼𝐶 (𝑥) + 𝑑𝐼𝐷 (𝑥) ∀𝑥 ∈ Ω

where 𝐵, 𝐶, 𝐷 are separated subsets of Ω.


To solve this problem by the quasi-Newton BFGS method we need the derivatives of 𝐽 with respect to 𝑏, 𝑐, 𝑑. We self
explanatory notations, if 𝛿𝑏, 𝛿𝑐, 𝛿𝑑 are variations of 𝑏, 𝑐, 𝑑 we have:
∫︀
𝛿𝐽 ≈ 2 𝐸 (𝑢 − 𝑢𝑑 )𝛿𝑢
−∇(𝜅 · ∇𝛿𝑢) ≈ ∇(𝛿𝜅 · ∇𝑢)
𝛿𝑢|Γ = 0

Obviously 𝐽𝑏′ is equal to 𝛿𝐽 when 𝛿𝑏 = 1, 𝛿𝑐 = 0, 𝛿𝑑 = 0, and so on for 𝐽𝑐′ and 𝐽𝑑′ .


All this is implemented in the following program:

1 // Mesh
2 border aa(t=0, 2*pi){x=5*cos(t); y=5*sin(t);};
3 border bb(t=0, 2*pi){x=cos(t); y=sin(t);};
4 border cc(t=0, 2*pi){x=-3+cos(t); y=sin(t);};
5 border dd(t=0, 2*pi){x=cos(t); y =-3+sin(t);};
6

7 mesh th = buildmesh(aa(70) + bb(35) + cc(35) + dd(35));


8

9 // Fespace
10 fespace Vh(th, P1);
11 Vh Ib=((x^2+y^2)<1.0001),
12 Ic=(((x+3)^2+ y^2)<1.0001),
13 Id=((x^2+(y+3)^2)<1.0001),
14 Ie=(((x-1)^2+ y^2)<=4),
15 ud, u, uh, du;
16

17 // Problem
18 real[int] z(3);
19 problem A(u, uh)
20 = int2d(th)(
21 (1+z[0]*Ib+z[1]*Ic+z[2]*Id)*(dx(u)*dx(uh) + dy(u)*dy(uh))
22 )
23 + on(aa, u=x^3-y^3)
24 ;
25

26 // Solve
27 z[0]=2; z[1]=3; z[2]=4;
28 A;
(continues on next page)

2.15. Optimal Control 77


FreeFEM Documentation, Release 4.8

(continued from previous page)


29 ud = u;
30

31 ofstream f("J.txt");
32 func real J(real[int] & Z){
33 for (int i = 0; i < z.n; i++)
34 z[i] =Z[i];
35 A;
36 real s = int2d(th)(Ie*(u-ud)^2);
37 f << s << " ";
38 return s;
39 }
40

41 // Problem BFGS
42 real[int] dz(3), dJdz(3);
43 problem B (du, uh)
44 = int2d(th)(
45 (1+z[0]*Ib+z[1]*Ic+z[2]*Id)*(dx(du)*dx(uh) + dy(du)*dy(uh))
46 )
47 + int2d(th)(
48 (dz[0]*Ib+dz[1]*Ic+dz[2]*Id)*(dx(u)*dx(uh) + dy(u)*dy(uh))
49 )
50 +on(aa, du=0)
51 ;
52

53 func real[int] DJ(real[int] &Z){


54 for(int i = 0; i < z.n; i++){
55 for(int j = 0; j < dz.n; j++)
56 dz[j] = 0;
57 dz[i] = 1;
58 B;
59 dJdz[i] = 2*int2d(th)(Ie*(u-ud)*du);
60 }
61 return dJdz;
62 }
63

64 real[int] Z(3);
65 for(int j = 0; j < z.n; j++)
66 Z[j]=1;
67

68 BFGS(J, DJ, Z, eps=1.e-6, nbiter=15, nbiterline=20);


69 cout << "BFGS: J(z) = " << J(Z) << endl;
70 for(int j = 0; j < z.n; j++)
71 cout << z[j] << endl;
72

73 // Plot
74 plot(ud, value=1, ps="u.eps");

In this example the sets 𝐵, 𝐶, 𝐷, 𝐸 are circles of boundaries 𝑏𝑏, 𝑐𝑐, 𝑑𝑑, 𝑒𝑒 and the domain Ω is the circle of boundary
𝑎𝑎.
The desired state 𝑢𝑑 is the solution of the PDE for 𝑏 = 2, 𝑐 = 3, 𝑑 = 4. The unknowns are packed into array 𝑧.

78 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(b) Successive evaluations of 𝐽 by BFGS (5 values above


500 have been removed for readability)

(a) Level line of 𝑢.

Fig. 2.17: Optimal control

Note: It is necessary to recopy 𝑍 into 𝑧 because one is a local variable while the other one is global.

The program found 𝑏 = 2.00125, 𝑐 = 3.00109, 𝑑 = 4.00551.


Fig. 2.17a and Fig. 2.17b show 𝑢 at convergence and the successive function evaluations of 𝐽.
Note that an adjoint state could have been used. Define 𝑝 by:

−∇ · (𝜅∇𝑝) = 2𝐼𝐸 (𝑢 − 𝑢𝑑 )
𝑝|Γ = 0

Consequently:
∫︀
𝛿𝐽 = −
∫︀ Ω (∇ · (𝜅∇𝑝))𝛿𝑢
= Ω∫︀(𝜅∇𝑝 · ∇𝛿𝑢)
= − Ω (𝛿𝜅∇𝑝 · ∇𝑢)

Then the derivatives are found by setting 𝛿𝑏 = 1, 𝛿𝑐 = 𝛿𝑑 = 0 and so on:

𝐽𝑏′ = − ∫︀𝐵 ∇𝑝 · ∇𝑢
∫︀

𝐽𝑐′ = − ∫︀𝐶 ∇𝑝 · ∇𝑢
𝐽𝑑′ = − 𝐷 ∇𝑝 · ∇𝑢

Note: As BFGS stores an 𝑀 × 𝑀 matrix where 𝑀 is the number of unknowns, it is dangerously expensive to use this
method when the unknown 𝑥 is a Finite Element Function. One should use another optimizer such as the NonLinear
Conjugate Gradient NLCG (also a key word of FreeFEM).

2.15. Optimal Control 79


FreeFEM Documentation, Release 4.8

2.16 A Flow with Shocks

Compressible Euler equations should be discretized with Finite Volumes or FEM with flux up-winding scheme but these
are not implemented in FreeFEM. Nevertheless acceptable results can be obtained with the method of characteristics
1 (︀ +
provided that the mean values 𝑓¯ = 𝑓 + 𝑓 − are used at shocks in the scheme, and finally mesh adaptation.
)︀
2
¯∇𝜌 + 𝜌¯∇ · 𝑢 = 0
𝜕𝑡 𝜌 + 𝑢
𝜌¯(𝜕𝑡 𝑢 + 𝜌𝑢
𝜌¯ ∇𝑢 + ∇𝑝 = 0
¯∇𝑝 + (𝛾 − 1)¯
𝜕𝑡 𝑝 + 𝑢 𝑝∇ · 𝑢 = 0
One possibility is to couple 𝑢, 𝑝 and then update 𝜌, i.e.:
1 𝑚+1
(𝛾−1)𝛿𝑡𝑝¯𝑚 (𝑝 − 𝑝𝑚 ∘ 𝑋 𝑚 ) + ∇ · 𝑢𝑚+1 = 0
𝑚
𝜌¯ 𝑚+1 ˜ 𝑚 ) + ∇𝑝𝑚+1
− 𝑢𝑚 ∘ 𝑋
𝛿𝑡 (𝑢 = 0
𝑚+1 𝑚 𝑚 𝜌¯𝑚 𝑚+1
𝜌 = 𝜌 ∘ 𝑋 + (𝛾−1) 𝑝¯𝑚 (𝑝 − 𝑝𝑚 ∘ 𝑋 𝑚 )

A numerical result is given on Fig. 2.18 and the FreeFEM script is

1 // Parameters
2 verbosity = 1;
3 int anew = 1;
4 int m = 5;
5 real x0 = 0.5;
6 real y0 = 0.;
7 real rr = 0.2;
8 real dt = 0.01;
9 real u0 = 2.;
10 real err0 = 0.00625;
11 real pena = 2.;
12

13 // Mesh
14 border ccc(t=0, 2){x=2-t; y=1;};
15 border ddd(t=0, 1){x=0; y=1-t;};
16 border aaa1(t=0, x0-rr){x=t; y=0;};
17 border cercle(t=pi, 0){x=x0+rr*cos(t); y=y0+rr*sin(t);}
18 border aaa2(t=x0+rr, 2){x=t; y=0;};
19 border bbb(t=0, 1){x=2; y=t;};
20

21 mesh Th;
22 if(anew)
23 Th = buildmesh (ccc(5*m) + ddd(3*m) + aaa1(2*m) + cercle(5*m) + aaa2(5*m) + bbb(2*m));
24 else
25 Th = readmesh("Th_circle.mesh"); plot(Th);
26

27 // fespace
28 fespace Wh(Th, P1);
29 Wh u, v;
30 Wh u1, v1;
31 Wh uh, vh;
32

33 fespace Vh(Th, P1);


34 Vh r, rh, r1;
35

(continues on next page)

80 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


36 // Macro
37 macro dn(u) (N.x*dx(u)+N.y*dy(u)) //
38

39 // Initialization
40 if(anew){
41 u1 = u0;
42 v1 = 0;
43 r1 = 1;
44 }
45 else{
46 ifstream g("u.txt"); g >> u1[];
47 ifstream gg("v.txt"); gg >> v1[];
48 ifstream ggg("r.txt"); ggg >> r1[];
49 plot(u1, ps="eta.eps", value=1, wait=1);
50 err0 = err0/10;
51 dt = dt/10;
52 }
53

54 // Problem
55 problem euler(u, v, r, uh, vh, rh)
56 = int2d(Th)(
57 (u*uh + v*vh + r*rh)/dt
58 + ((dx(r)*uh + dy(r)*vh) - (dx(rh)*u + dy(rh)*v))
59 )
60 + int2d(Th)(
61 - (
62 rh*convect([u1,v1],-dt,r1)
63 + uh*convect([u1,v1],-dt,u1)
64 + vh*convect([u1,v1],-dt,v1)
65 )/dt
66 )
67 +int1d(Th, 6)(
68 rh*u
69 )
70 + on(2, r=0)
71 + on(2, u=u0)
72 + on(2, v=0)
73 ;
74

75 // Iterations
76 int j = 80;
77 for(int k = 0; k < 3; k++){
78 if(k==20){
79 err0 = err0/10;
80 dt = dt/10;
81 j = 5;
82 }
83

84 // Solve
85 for(int i = 0; i < j; i++){
86 euler;
87 u1=u;
(continues on next page)

2.16. A Flow with Shocks 81


FreeFEM Documentation, Release 4.8

(continued from previous page)


88 v1=v;
89 r1=abs(r);
90 cout << "k = " << k << " E = " << int2d(Th)(u^2+v^2+r) << endl;
91 plot(r, value=1);
92 }
93

94 // Mesh adaptation
95 Th = adaptmesh (Th, r, nbvx=40000, err=err0, abserror=1, nbjacoby=2, omega=1.8,␣
˓→ratio=1.8, nbsmooth=3, splitpbedge=1, maxsubdiv=5, rescaling=1);

96 plot(Th);
97 u = u;
98 v = v;
99 r = r;
100

101 // Save
102 savemesh(Th, "Th_circle.mesh");
103 ofstream f("u.txt"); f << u[];
104 ofstream ff("v.txt"); ff << v[];
105 ofstream fff("r.txt"); fff << r[];
106 r1 = sqrt(u*u+v*v);
107 plot(r1, ps="mach.eps", value=1);
108 r1 = r;
109 }

2.17 Time dependent schema optimization for heat equations

First, it is possible to define variational forms, and use this forms to build matrix and vector to make very fast script (4
times faster here).
For example solve the ThermalConduction problem, we must solve the temperature equation in Ω in a time interval
(0,T).

𝜕𝑡 𝑢 − ∇ · (𝜅∇𝑢) = 0 in Ω × (0, 𝑇 )
𝑢(𝑥, 𝑦, 0) = 𝑢0 + 𝑥𝑢1
𝑢 = 30 on Γ24 × (0, 𝑇 )
𝜕𝑢
𝜅 𝜕𝑛 + 𝛼(𝑢 − 𝑢𝑒 ) = 0 on Γ × (0, 𝑇 )

The variational formulation is in 𝐿2 (0, 𝑇 ; 𝐻 1 (Ω)); we shall seek 𝑢𝑛 satisfying:


∫︁ 𝑛
𝑢 − 𝑢𝑛−1
∫︁
∀𝑤 ∈ 𝑉0 ; 𝑤 + 𝜅∇𝑢𝑛 ∇𝑤) + 𝛼(𝑢𝑛 − 𝑢𝑢𝑒 )𝑤 = 0
Ω 𝛿𝑡 Γ

where 𝑉0 = {𝑤 ∈ 𝐻 1 (Ω)/𝑤|Γ24 = 0}.


So, to code the method with the matrices 𝐴 = (𝐴𝑖𝑗 ), 𝑀 = (𝑀𝑖𝑗 ), and the vectors 𝑢𝑛 , 𝑏𝑛 , 𝑏′ , 𝑏”, 𝑏𝑐𝑙 (notation if 𝑤 is a
vector then 𝑤𝑖 is a component of the vector).

𝑏𝑖 if 𝑖 ∈ Γ24
{︂ ′′
𝑛 −1 𝑛 ′ 𝑛−1 1 𝑛
𝑢 = 𝐴 𝑏 , 𝑏 = 𝑏0 + 𝑀 𝑢 , 𝑏” = 𝑏𝑐𝑙 , 𝑏𝑖 =
𝜀 𝑏′𝑖 else

82 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

Fig. 2.18: Pressure for a Euler flow around a disk at Mach 2 computed by (2.6)

2.17. Time dependent schema optimization for heat equations 83


FreeFEM Documentation, Release 4.8

Where with 1
𝜀 = tgv = 1030 :

if 𝑖 ∈ Γ24 , and
⎧ 1
⎨ ∫︁ 𝜀 ∫︁ 𝑗=𝑖
𝐴𝑖𝑗 =
⎩ 𝑤𝑗 𝑤𝑖 /𝑑𝑡 + 𝑘(∇𝑤𝑗 .∇𝑤𝑖 ) + 𝛼𝑤𝑗 𝑤𝑖 else
⎧ Ω Γ13
⎨ ∫︁ 1𝜀 if 𝑖 ∈ Γ24 , and 𝑗 = 𝑖
𝑀𝑖𝑗 =
⎩ 𝑛 𝑤𝑗 𝑤𝑖 /𝑑𝑡 else
∫︀ Ω
𝑏0,𝑖 = 𝑛 Γ13 𝛼𝑢𝑢𝑒 𝑤𝑖
𝑏𝑐𝑙 = 𝑢0 the initial data

The Fast version script:

1 ...
2 Vh u0=fu0, u=u0;

Create three variational formulation, and build the matrices 𝐴,𝑀 .

1 varf vthermic (u, v)


2 = int2d(Th)(
3 u*v/dt
4 + k*(dx(u)*dx(v) + dy(u)*dy(v))
5 )
6 + int1d(Th, 1, 3)(
7 alpha*u*v
8 )
9 + on(2,4,u=1)
10 ;
11

12 varf vthermic0 (u, v)


13 = int1d(Th, 1, 3)(
14 alpha*ue*v
15 )
16 ;
17 varf vMass (u,v)
18 = int2d(Th)(
19 u*v/dt
20 )
21 + on(2, 4, u=1)
22 ;
23

24 real tgv = 1e30;


25 matrix A = vthermic(Vh, Vh, tgv=tgv, solver=CG);
26 matrix M = vMass(Vh, Vh);

Now, to build the right hand size; we need 4 vectors.

1 real[int] b0 = vthermic0(0,Vh); //constant part of RHS


2 real[int] bcn = vthermic(0,Vh); //tgv on Dirichlet part
3 real[int] bcl = tgv*u0[]; //the Dirichlet B.C. part
4

5 // The fast loop


6 for(real t = 0; t < T; t += dt){
7 real[int] b = b0; //the RHS
(continues on next page)

84 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


8 b += M*u[]; //add the the time dependent part
9 b = bcn ? bcl : b; //do $\forall i$: b[i] = bcn[i] ? bcl[i] : b[i];
10 u[] = A^-1*b; //solve linear problem
11 plot(u);
12 }

2.18 Tutorial to write a transient Stokes solver in matrix form

Consider the following script to solve a time dependent Stokes problem in a cavity

1 // Parameters
2 real nu = 0.1;
3 real T=1.;
4 real dt = 0.1;
5

6 // Mesh
7 mesh Th = square(10, 10);
8

9 // Fespace
10 fespace Vh(Th, P2)
11 Vh u, v;
12 Vh uu, vv;
13 Vh uold=0, vold=0;
14

15 fespace Qh(Th, P1);


16 Qh p;
17 Qh pp;
18

19 // Problem
20 problem stokes (u, v, p, uu, vv, pp)
21 = int2d(Th)(
22 (u*uu+v*vv)/dt
23 + nu*(dx(u)*dx(uu) + dy(u)*dy(uu) + dx(v)*dx(vv) + dy(v)*dy(vv))
24 - p*pp*1.e-6
25 - p*(dx(uu) + dy(vv))
26 - pp*(dx(u) + dy(v))
27 )
28 - int2d(Th)(
29 (uold*uu+vold*vv)/dt
30 )
31 + on(1, 2, 4, u=0, v=0)
32 + on(3, u=1, v=0)
33 ;
34

35 // Time loop
36 int m, M = T/dt;
37 for(m = 0; m < M; m++){
38 stokes;
39 uold = u;
40 vold = v;
(continues on next page)

2.18. Tutorial to write a transient Stokes solver in matrix form 85


FreeFEM Documentation, Release 4.8

(continued from previous page)


41 }
42

43 // Plot
44 plot(p, [u, v], value=true, wait=true, cmm="t="+m*dt);

Every iteration is in fact of the form 𝐴[𝑢, 𝑣, 𝑝] = 𝐵[𝑢𝑜𝑙𝑑, 𝑣𝑜𝑙𝑑, 𝑝𝑜𝑙𝑑] + 𝑏 where 𝐴, 𝐵 are matrices and 𝑏 is a vector
containing the boundary conditions. 𝐴, 𝐵, 𝑏 are constructed by:

1 fespace Xh(Th, [P2, P2, P1]);


2 varf aa ([u, v, p], [uu, vv, pp])
3 = int2d(Th)(
4 (u*uu+v*vv)/dt
5 + nu*(dx(u)*dx(uu) + dy(u)*dy(uu) + dx(v)*dx(vv) + dy(v)*dy(vv))
6 - p*pp*1.e-6
7 - p*(dx(uu) + dy(vv))
8 - pp*(dx(u) + dy(v))
9 )
10 + on(1, 2, 4, u=0, v=0)
11 + on(3, u=1, v=0)
12 ;
13

14 varf bb ([uold, vold, pold], [uu, vv, pp])


15 = int2d(Th)(
16 (uold*uu+vold*vv)/dt
17 )
18 //+ on(1, 2, 4, uold=0, vold=0)
19 //+ on(3, uold=1, vold=0)
20 ;
21

22 varf bcl ([uold, vold, pold], [uu, vv, pp])


23 = on(1, 2, 4, uold=0, vold=0)
24 + on(3, uold=1, vold=0)
25 ;
26

27 matrix A = aa(Xh, Xh, solver=UMFPACK);


28 matrix B = bb(Xh, Xh);
29 real[int] b = bcl(0, Xh);

Note that the boundary conditions are not specified in 𝑏𝑏. Removing the comment // would cause the compiler to
multiply the diagonal terms corresponding to a Dirichlet degree of freedom by a very large term (tgv); if so 𝑏 would
not be needed, on the condition that 𝑢𝑜𝑙𝑑 = 1 on boundary 3 initially. Note also that b has a tgv on the Dirichlet nodes,
by construction, and so does A.
The loop will then be:

1 real[int] sol(Xh.ndof), aux(Xh.ndof);


2 for (m = 0; m < M; m++){
3 aux = B*sol; aux += b;
4 sol = A^-1 * aux;
5 }

There is yet a difficulty with the initialization of sol and with the solution from sol. For this we need a temporary
vector in 𝑋ℎ and here is a solution:

86 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

1 Xh [w1, w2, wp] = [uold, vold, pp];


2 sol = w1[]; //cause also the copy of w2 and wp
3 for (m = 0; m < M; m++){
4 aux = B*sol; aux += b;
5 sol = A^-1 * aux;
6 }
7 w1[]=sol; u=w1; v= w2; p=wp;
8 plot(p, [u, v], value=true, wait=true, cmm="t="+m*dt);

The freefem team agrees that the line sol=w1[]; is mysterious as it copies also w2 and wp into sol. Structured data
such as vectors of 𝑋ℎ here cannot be written component by component. Hence w1=u is not allowed.

2.19 Wifi Propagation

2.19.1 Summary

In this tutorial, we will study the wifi signal power in a flat. An awesome flat is especially designed for the experiment,
with 2 walls:

Fig. 2.19: Flat

Even if the flat seems small enough to be covered by wifi everywhere, it is still interesting to study where the signal’s
power is the lowest. We will study where to put the hotspot to get the best coverage, and as we’re a bit lazy we will
only put it next to the left wall.

2.19.2 Physics

In a nutshell, the Wifi is a electromagnetic wave that contains a signal : Internet data. Electromagnetic waves are well
know by physicists and are ruled by the 4 Maxwell equations which give you the solution for E, the electrical field,
and B, the magnetic field, in space but also in time.
We don’t care about the time here, because the signal period is really short so our internet quality will not change
with time. Without time, we’re looking for stationaries solutions, and the Maxwell equations can be simplified to one
equation, the Helmholtz one :

𝑘2
∇2 𝐸 + 𝐸=0
𝑛2

2.19. Wifi Propagation 87


FreeFEM Documentation, Release 4.8

Where k is the angular wavenumber of the wifi signal, and n the refractive index of the material the wave is in.
Indeed, the main point of this study is the impact of walls on the signal’s power, where the n is different from air (where
it is 1). In walls, the refractive index is a complex number in which the two parts have a physic interpretation:
• The real part defines the reflexion of the wall (the amount of signal that doesn’t pass).
• The imaginary part defines the absorption of the wall (the amount that disappears).
The wifi hotspot (simulated by a simple circle) will be the boundary condition, with a non null value for our electrical
field.

2.19.3 Coding

The domain

In order to create the domain of experimentation, we need to create border objects, like this :

1 real a = 40, b = 40, c = 0.5;


2 border a00(t=0, 1) {x=a*t; y=0; label=1;}
3 border a10(t=0, 1) {x=a; y=b*t; label=1;}
4 border a20(t=1, 0) {x=a*t; y=b; label=1;}
5 border a30(t=1, 0) {x=0; y=b*t; label=1;}
6 border a01(t=0, 1) {x=c+(a-c*2)*t; y=c; label=1;}
7 border a11(t=0, 1) {x=a-c; y=c+(b-c*2)*t; label=1;}
8 border a21(t=1, 0) {x=c+(a-c*2)*t; y=b-c; label=1;}
9 border a31(t=1, 0) {x=c; y=c+(b-c*2)*t; label=1;}
10

11 real p = 5, q = 20, d = 34, e = 1;


12 border b00(t=0, 1) {x=p+d*t; y=q; label=3;}
13 border b10(t=0, 1) {x=p+d; y=q+e*t; label=3;}
14 border b20(t=1, 0) {x=p+d*t; y=q+e; label=3;}
15 border b30(t=1, 0) {x=p; y=q+e*t; label=3;}
16

17 real r = 30, s =1 , j = 1, u = 15;


18 border c00(t=0, 1) {x=r+j*t; y=s; label=3;}
19 border c10(t=0, 1) {x=r+j; y=s+u*t; label=3;}
20 border c20(t=1, 0) {x=r+j*t; y=s+u; label=3;}
21 border c30(t=1, 0) {x=r; y=s+u*t; label=3;}

Let’s create a mesh

1 int n=13;
2 mesh Sh = buildmesh(a00(10*n) + a10(10*n) + a20(10*n) + a30(10*n)
3 + a01(10*n) + a11(10*n) + a21(10*n) + a31(10*n)
4 + b00(5*n) + b10(5*n) + b20(5*n) + b30(5*n)
5 + c00(5*n) + c10(5*n) + c20(5*n) + c30(5*n));
6 plot(Sh, wait=1);

So we are creating a mesh, and plotting it :


There is currently no wifi hotspot, and as we want to resolve the equation for a multiple number of position next to the
left wall, let’s do a for loop:

88 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

Fig. 2.20: Mesh

1 int bx;
2 for (bx = 1; bx <= 7; bx++){
3 border C(t=0, 2*pi){x=2+cos(t); y=bx*5+sin(t); label=2;}
4

5 mesh Th = buildmesh(a00(10*n) + a10(10*n) + a20(10*n) + a30(10*n)


6 + a01(10*n) + a11(10*n) + a21(10*n) + a31(10*n) + C(10)
7 + b00(5*n) + b10(5*n) + b20(5*n) + b30(5*n)
8 + c00(5*n) + c10(5*n) + c20(5*n) + c30(5*n));

The border C is our hotspot and as you can see a simple circle. Th is our final mesh, with all borders and the hotspot.
Let’s resolve this equation !

1 fespace Vh(Th, P1);


2 func real wall() {
3 if (Th(x,y).region == Th(0.5,0.5).region || Th(x,y).region == Th(7,20.5).region ||␣
˓→Th(x,y).region == Th(30.5,2).region) { return 1; }

4 else { return 0; }
5 }
6

7 Vh<complex> v,w;
8

9 randinit(900);
10 Vh wallreflexion = randreal1();
11 Vh<complex> wallabsorption = randreal1()*0.5i;
12 Vh k = 6;
13

14 cout << "Reflexion of walls min/max: " << wallreflexion[].min << " " << wallreflexion[].
˓→max << "\n";

15 cout << "Absorption of walls min/max: " << wallabsorption[].min << " "<< ␣
˓→wallabsorption[].max << "\n";

(continues on next page)

2.19. Wifi Propagation 89


FreeFEM Documentation, Release 4.8

(continued from previous page)


16

17 problem muwave(v,w) =
18 int2d(Th)(
19 (v*w*k^2)/(1+(wallreflexion+wallabsorption)*wall())^2
20 - (dx(v)*dx(w)+dy(v)*dy(w))
21 )
22 + on(2, v=1)
23 ;
24

25 muwave;
26 Vh vm = log(real(v)^2 + imag(v)^2);
27 plot(vm, wait=1, fill=true, value=0, nbiso=65);
28 }

A bit of understanding here :


• The fespace keyword defines a finite elements space, no need to know more here.
• The function wall return 0 if in air and 1 if in a wall (x and y are global variables).
• For this example, random numbers are used for the reflexion and the absorption.
• The problem is defined with problem and we solve it by calling it.
Finally, I plotted the log of the module of the solution v to see the signal’s power, and here we are :

Fig. 2.21: Solution

Beautiful isn’t it ? This is the first position for the hotspot, but there are 6 others, and the electrical field is evolving
depending on the position. You can see the other positions here :

90 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(a) Point 2 (b) Point 3 (c) Point 4

(d) Point 5 (e) Point 6 (f) Point 7

Fig. 2.22: Wifi propagation

2.19. Wifi Propagation 91


FreeFEM Documentation, Release 4.8

2.20 Plotting in Matlab and Octave

2.20.1 Overview

In order to create a plot of a FreeFEM simulation in Matlab© or Octave two steps are necessary:
• The mesh, the finite element space connectivity and the simulation data must be exported into files
• The files must be imported into the Matlab / Octave workspace. Then the data can be visualized with the ffmatlib
library
The steps are explained in more detail below using the example of a stripline capacitor.

Note: Finite element variables must be in P1 or P2. The simulation data can be 2D or 3D.

2.20.2 2D Problem

Consider a stripline capacitor problem which is also shown in Fig. 2.23. On the two boundaries (the electrodes) 𝐶𝐴 ,
𝐶𝐾 a Dirichlet condition and on the enclosure 𝐶𝐵 a Neumann condition is set. The electrostatic potential 𝑢 between
the two electrodes is given by the Laplace equation

∆𝑢(𝑥, 𝑦) = 0

and the electrostatic field E is calculated by

E = −∇𝑢

1 int CA=3, CK=4, CB=5;


2 real w2=1.0, h=0.4, d2=0.5;
3

4 border bottomA(t=-w2,w2){ x=t; y=d2; label=CA;};


5 border rightA(t=d2,d2+h){ x=w2; y=t; label=CA;};
6 border topA(t=w2,-w2){ x=t; y=d2+h; label=CA;};
7 border leftA(t=d2+h,d2){ x=-w2; y=t; label=CA;};
8

9 border bottomK(t=-w2,w2){ x=t; y=-d2-h; label=CK;};


10 border rightK(t=-d2-h,-d2){ x=w2; y=t; label=CK;};
11 border topK(t=w2,-w2){ x=t; y=-d2; label=CK;};
12 border leftK(t=-d2,-d2-h){ x=-w2; y=t; label=CK;};
13

14 border enclosure(t=0,2*pi){x=5*cos(t); y=5*sin(t); label=CB;}


15

16 int n=15;
17 mesh Th = buildmesh(enclosure(3*n)+
18 bottomA(-w2*n)+topA(-w2*n)+rightA(-h*n)+leftA(-h*n)+
19 bottomK(-w2*n)+topK(-w2*n)+rightK(-h*n)+leftK(-h*n));
20

21 fespace Vh(Th,P1);
22

23 Vh u,v;
24 real u0=2.0;
(continues on next page)

92 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

(continued from previous page)


25

26 problem Laplace(u,v,solver=LU) =
27 int2d(Th)(dx(u)*dx(v) + dy(u)*dy(v))
28 + on(CA,u=u0)+on(CK,u=0);
29

30 real error=0.01;
31 for (int i=0;i<1;i++){
32 Laplace;
33 Th=adaptmesh(Th,u,err=error);
34 error=error/2.0;
35 }
36 Laplace;
37

38 Vh Ex, Ey;
39 Ex = -dx(u);
40 Ey = -dy(u);
41

42 plot(u,[Ex,Ey],wait=true);

2.20.3 Exporting Data

The mesh is stored with the FreeFEM command savemesh(), while the connectivity of the finite element space and
the simulation data are stored with the macro commands ffSaveVh() and ffSaveData(). These two commands are
located in the ffmatlib.idp file which is included in the ffmatlib. Therefore, to export the stripline capacitor data
the following statement sequence must be added to the FreeFEM code:

1 include "ffmatlib.idp"
2

3 //Save mesh
4 savemesh(Th,"capacitor.msh");
5 //Save finite element space connectivity
6 ffSaveVh(Th,Vh,"capacitor_vh.txt");
7 //Save some scalar data
8 ffSaveData(u,"capacitor_potential.txt");
9 //Save a 2D vector field
10 ffSaveData2(Ex,Ey,"capacitor_field.txt");

2.20.4 Importing Data

The mesh file can be loaded into the Matlab / Octave workspace using the ffreadmesh() command. A mesh file
consists of three main sections:
1. The mesh points as nodal coordinates
2. A list of boundary edges including boundary labels
3. List of triangles defining the mesh in terms of connectivity
The three data sections mentioned are returned in the variables p, b and t. The finite element space connectivity and the
simulation data can be loaded using the ffreaddata() command. Therefore, to load the example data the following
statement sequence must be executed in Matlab / Octave:

2.20. Plotting in Matlab and Octave 93


FreeFEM Documentation, Release 4.8

1 %Add ffmatlib to the search path


2 addpath('add here the link to the ffmatlib');
3 %Load the mesh
4 [p,b,t,nv,nbe,nt,labels]=ffreadmesh('capacitor.msh');
5 %Load the finite element space connectivity
6 vh=ffreaddata('capacitor_vh.txt');
7 %Load scalar data
8 u=ffreaddata('capacitor_potential.txt');
9 %Load 2D vector field data
10 [Ex,Ey]=ffreaddata('capacitor_field.txt');

2.20.5 2D Plot Examples

ffpdeplot() is a plot solution for creating patch, contour, quiver, mesh, border, and region plots of 2D geometries.
The basic syntax is:

1 [handles,varargout] = ffpdeplot(p,b,t,varargin)

varargin specifies parameter name / value pairs to control the plot behaviour. A table showing all options can be
found in the ffmatlib documentation. A small selection of possible plot commands is given as follows:
• Plot of the boundary and the mesh:

1 ffpdeplot(p,b,t,'Mesh','on','Boundary','on');

Fig. 2.23: Boundary and Mesh

• Patch plot (2D map or density plot) including mesh and boundary:

1 ffpdeplot(p,b,t,'VhSeq',vh,'XYData',u,'Mesh','on','Boundary','on', ...
2 'XLim',[-2 2],'YLim',[-2 2]);

• 3D surf plot:

1 ffpdeplot(p,b,t,'VhSeq',vh,'XYData',u,'ZStyle','continuous', ...
2 'Mesh','off');
(continues on next page)

94 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

Fig. 2.24: Patch Plot with Mesh

(continued from previous page)


3 lighting gouraud;
4 view([-47,24]);
5 camlight('headlight');

Fig. 2.25: 3D Surf Plot

• Contour (isovalue) and quiver (vector field) plot:

1 ffpdeplot(p,b,t,'VhSeq',vh,'XYData',u,'Mesh','off','Boundary','on', ...
2 'XLim',[-2 2],'YLim',[-2 2],'Contour','on','CColor','b', ...
3 'XYStyle','off', 'CGridParam',[150, 150],'ColorBar','off', ...
4 'FlowData',[Ex,Ey],'FGridParam',[24, 24]);

Download run through example:


Matlab / Octave file
FreeFEM script

2.20. Plotting in Matlab and Octave 95


FreeFEM Documentation, Release 4.8

Fig. 2.26: Contour and Quiver Plot

2.20.6 3D Plot Examples

3D problems are handled by the ffpdeplot3D() command, which works similarly to the ffpdeplot() command.
In particular in three-dimensions cross sections of the solution can be created. The following example shows a cross-
sectional problem of a three-dimensional parallel plate capacitor.

Fig. 2.27: Slice on a 3D Parallel Plate Capacitor

Download run through example:


Matlab / Octave file
FreeFEM script

96 Chapter 2. Learning by Examples


FreeFEM Documentation, Release 4.8

2.20.7 References

• Octave
• Matlab
• ffmatlib

2.20. Plotting in Matlab and Octave 97


FreeFEM Documentation, Release 4.8

98 Chapter 2. Learning by Examples


CHAPTER

THREE

DOCUMENTATION

The fruit of a long maturing process, freefem, in its last avatar, FreeFEM , is a high level integrated development
environment (IDE) for numerically solving partial differential equations (PDE) in dimension 1,2 3 and surface and line
3D. It is the ideal tool for teaching the finite element method but it is also perfect for research to quickly test new ideas
or multi-physics and complex applications.
FreeFEM has an advanced automatic mesh generator, capable of a posteriori mesh adaptation; it has a general purpose
elliptic solver interfaced with fast algorithms, such as the multi-frontal method UMFPACK, SuperLU, MUMPS. Hy-
perbolic and parabolic problems are solved by iterative algorithms prescribed by the user with the high level language of
FreeFEM. It has several triangular finite elements, including discontinuous elements. Everything is there in FreeFEM
to prepare research quality reports with online color display, zooming and other features as well as postscript printouts.
This manual is meant for students at a Masters level, for researchers at any level, and for engineers (including financial
engineering) with some understanding of variational methods for partial differential equations.
Introduction
A partial differential equation is a relation between a function of several variables and its (partial) derivatives. Many
problems in physics, engineering, mathematics and even banking are modeled by one or several partial differential
equations.
FreeFEM is a software to solve these equations numerically. As its name implies, it is a free software (see the copyrights
for full detail) based on the Finite Element Method; it is not a package, it is an integrated product with its own high
level programming language. This software runs on all UNIX OS (with g++ 3.3 or later, and OpenGL), on Window
XP, Vista and 7, 8, 10 and on MacOS 10 intel.
Moreover FreeFEM is highly adaptive. Many phenomena involve several coupled systems. Fluid-structure interac-
tions, Lorentz forces for aluminum casting and ocean-atmosphere problems are three such systems. These require
different finite element approximations and polynomial degrees, possibly on different meshes. Some algorithms like
the Schwarz’ domain decomposition method also requires data interpolation on multiple meshes within one program.
FreeFEM can handle these difficulties, i.e. arbitrary finite element spaces on arbitrary unstructured and adapted bi-
dimensional meshes.
The characteristics of FreeFEM are:
• Problem description (real or complex valued) by their variational formulations, with access to the internal vectors
and matrices if needed.
• Multi-variables, multi-equations, bi-dimensional and three-dimensional static or time dependent, linear or non-
linear coupled systems; however the user is required to describe the iterative procedures which reduce the problem
to a set of linear problems.
• Easy geometric input by analytic description of boundaries by pieces; however this part is not a CAD system; for
instance when two boundaries intersect, the user must specify the intersection points.
• Automatic mesh generator, based on the Delaunay-Voronoi algorithm; the inner point density is proportional to
the density of points on the boundaries [GEORGE1996].

99
FreeFEM Documentation, Release 4.8

• Metric-based anisotropic mesh adaptation. The metric can be computed automatically from the Hessian of any
FreeFEM function [HECHT1998].
• High level user friendly typed input language with an algebra of analytic and finite element functions.
• Multiple finite element meshes within one application with automatic interpolation of data on different meshes
and possible storage of the interpolation matrices.
• A large variety of triangular finite elements: linear, quadratic Lagrangian elements and more, discontinuous P1
and Raviart-Thomas elements, elements of a non-scalar type, the mini-element,. . . (but no quadrangles).
• Tools to define discontinuous Galerkin finite element formulations P0, P1dc, P2dc and keywords: jump, mean,
intalledges.
• A large variety of linear direct and iterative solvers (LU, Cholesky, Crout, CG, GMRES, UMFPACK, MUMPS,
SuperLU, . . . ) and eigenvalue and eigenvector solvers (ARPARK) .
• Near optimal execution speed (compared with compiled C++ implementations programmed directly).
• Online graphics, generation of ,.txt,.eps,.gnu, mesh files for further manipulations of input and output data.
• Many examples and tutorials: elliptic, parabolic and hyperbolic problems, Navier-Stokes flows, elasticity, fluid
structure interactions, Schwarz’s domain decomposition method, eigenvalue problem, residual error indicator,
...
• A parallel version using MPI

3.1 Notations

Here mathematical expressions and corresponding FreeFEM commands are explained.

3.1.1 Generalities

• [𝛿𝑖𝑗 ] Kronecker delta (0 if 𝑖 ̸= 𝑗, 1 if 𝑖 = 𝑗 for integers 𝑖, 𝑗)


• [∀] for all
• [∃] there exists
• [i.e.] that is
• [PDE] partial differential equation (with boundary conditions)
• [∅] the empty set
• [N] the set of integers (𝑎 ∈ N ⇔ int a), int means long int inside FreeFEM
• [R] the set of real numbers (𝑎 ∈ R ⇔ real a), double inside FreeFEM
• [C] the set of complex numbers (𝑎 ∈ C ⇔ complex a), complex<double>
• [R𝑑 ] 𝑑-dimensional Euclidean space

100 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

3.1.2 Sets, Mappings, Matrices, Vectors

Let 𝐸, 𝐹, 𝐺 be three sets and 𝐴 the subset of 𝐸.


• [{𝑥 ∈ 𝐸| 𝑃 }] the subset of 𝐸 consisting of the elements possessing the property 𝑃
• [𝐸 ∪ 𝐹 ] the set of elements belonging to 𝐸 or 𝐹
• [𝐸 ∩ 𝐹 ] the set of elements belonging to 𝐸 and 𝐹
• [𝐸 ∖ 𝐴] the set {𝑥 ∈ 𝐸| 𝑥 ̸∈ 𝐴}
• [𝐸 + 𝐹 ] 𝐸 ∪ 𝐹 with 𝐸 ∩ 𝐹 = ∅
• [𝐸 × 𝐹 ] the Cartesian product of 𝐸 and 𝐹
• [𝐸 𝑛 ] the 𝑛-th power of 𝐸 (𝐸 2 = 𝐸 × 𝐸, 𝐸 𝑛 = 𝐸 × 𝐸 𝑛−1 )
• [𝑓 : 𝐸 → 𝐹 ] the mapping form 𝐸 into 𝐹 , i.e., 𝐸 ∋ 𝑥 ↦→ 𝑓 (𝑥) ∈ 𝐹
• [𝐼𝐸 or 𝐼] the identity mapping in 𝐸,i.e., 𝐼(𝑥) = 𝑥 ∀𝑥 ∈ 𝐸
• [𝑓 ∘ 𝑔] for 𝑓 : 𝐹 → 𝐺 and 𝑔 : 𝐸 → 𝐹 , 𝐸 ∋ 𝑥 ↦→ (𝑓 ∘ 𝑔)(𝑥) = 𝑓 (𝑔(𝑥)) ∈ 𝐺 (see Elementary function)
• [𝑓 |𝐴 ] the restriction of 𝑓 : 𝐸 → 𝐹 to the subset 𝐴 of 𝐸
• [{𝑎𝑘 }] column vector with components 𝑎𝑘
• [(𝑎𝑘 )] row vector with components 𝑎𝑘
• [(𝑎𝑘 )𝑇 ] denotes the transpose of a matrix (𝑎𝑘 ), and is {𝑎𝑘 }
• [{𝑎𝑖𝑗 }] matrix with components 𝑎𝑖𝑗 , and (𝑎𝑖𝑗 )𝑇 = (𝑎𝑗𝑖 )

3.1.3 Numbers

For two real numbers 𝑎, 𝑏


• [𝑎, 𝑏] is the interval {𝑥 ∈ R| 𝑎 ≤ 𝑥 ≤ 𝑏}
• ]𝑎, 𝑏] is the interval {𝑥 ∈ R| 𝑎 < 𝑥 ≤ 𝑏}
• [𝑎, 𝑏[ is the interval {𝑥 ∈ R| 𝑎 ≤ 𝑥 < 𝑏}
• ]𝑎, 𝑏[ is the interval {𝑥 ∈ R| 𝑎 < 𝑥 < 𝑏}

3.1.4 Differential Calculus

• [𝜕𝑓 /𝜕𝑥] the partial derivative of 𝑓 : R𝑑 → R with respect to 𝑥 (dx(f))


• [∇𝑓 ] the gradient of 𝑓 : Ω → R,i.e., ∇𝑓 = (𝜕𝑓 /𝜕𝑥, 𝜕𝑓 /𝜕𝑦)
• [div(f ) or ∇.f ] the divergence of f : Ω → R𝑑 , i.e., div(f ) = 𝜕𝑓1 /𝜕𝑥 + 𝜕𝑓2 /𝜕𝑦
• [∆𝑓 ] the Laplacian of 𝑓 : Ω → R, i.e., ∆𝑓 = 𝜕 2 𝑓 /𝜕𝑥2 + 𝜕 2 𝑓 /𝜕𝑦 2

3.1. Notations 101


FreeFEM Documentation, Release 4.8

3.1.5 Meshes

• [Ω] usually denotes a domain on which PDE is defined


• [Γ] denotes the boundary of Ω,i.e., Γ = 𝜕Ω (keyword border, see Border)
• [𝒯ℎ ] the triangulation of Ω, i.e., the set of triangles 𝑇𝑘 , where ℎ stands for mesh size (keyword mesh, buildmesh,
see Mesh Generation)
• [𝑛𝑡 ] the number of triangles in 𝒯ℎ (get by Th.nt)
• [Ωℎ ] denotes the approximated domain Ωℎ = ∪𝑛𝑘=1
𝑡
𝑇𝑘 of Ω. If Ω is polygonal domain, then it will be Ω = Ωℎ
• [Γℎ ] the boundary of Ωℎ
• [𝑛𝑣 ] the number of vertices in 𝒯ℎ (get by Th.nv)
• [𝑛𝑏𝑒 ] the number of boundary element in 𝒯ℎ (get by Th.nbe)
• [|Ωℎ |] the measure (area or volume) in 𝒯ℎ (get by Th.measure)
• [|𝜕Ωℎ |] the measure of the border (length or area) in 𝒯ℎ (get by Th.bordermeasure)
• [ℎ𝑚𝑖𝑛 ] the minimum edge size of 𝒯ℎ (get by Th.hmin)
• [ℎ𝑚𝑎𝑥 ] the maximum edge size of 𝒯ℎ (get by Th.hmax)
• [[𝑞 𝑖 𝑞 𝑗 ]] the segment connecting 𝑞 𝑖 and 𝑞 𝑗
• [𝑞 𝑘1 , 𝑞 𝑘2 , 𝑞 𝑘3 ] the vertices of a triangle 𝑇𝑘 with anti-clock direction (get the coordinate of 𝑞 𝑘𝑗 by
(Th[k-1][j-1].x, Th[k-1][j-1].y))
• [𝐼Ω ] the set {𝑖 ∈ N| 𝑞 𝑖 ̸∈ Γℎ }

3.1.6 Functional Spaces


{︂ ⃒ ∫︁ }︂
• [𝐿2 (Ω)] the set 𝑤(𝑥, 𝑦) ⃒⃒ |𝑤(𝑥, 𝑦)|2 d𝑥d𝑦 < ∞

Ω

(︂∫︁ )︂1/2
norm: ‖𝑤‖0,Ω = |𝑤(𝑥, 𝑦)| d𝑥d𝑦
2
Ω
∫︁
scalar product: (𝑣, 𝑤) = 𝑣𝑤
Ω
{︂ ⃒ ∫︁ }︂
• [𝐻 (Ω)] the set 𝑤 ∈ 𝐿 (Ω) ⃒ 2
|𝜕𝑤/𝜕𝑥| + |𝜕𝑤/𝜕𝑦| d𝑥d𝑦 < ∞
2 2
1
⃒ (︀ )︀

Ω

)︀1/2
norm: ‖𝑤‖1,Ω = ‖𝑤‖20,Ω + ‖∇𝑢‖20.Ω
(︀

𝜕 |𝛼| 𝑤
{︂ ⃒ ∫︁ }︂
• [𝐻 (Ω)] the set 𝑤 ∈ 𝐿 (Ω) ⃒⃒ 2
∈ 𝐿2 (Ω) 2
𝑚

𝛼1 𝛼2
∀𝛼 = (𝛼1 , 𝛼2 ) ∈ N , |𝛼| = 𝛼1 + 𝛼2
Ω 𝜕𝑥 𝜕𝑦

∑︁ ∫︁
scalar product: (𝑣, 𝑤)1,Ω = 𝐷𝛼 𝑣𝐷𝛼 𝑤
|𝛼|≤𝑚 Ω

• [𝐻01 (Ω)] the set 𝑤 ∈ 𝐻 1 (Ω) | 𝑢 = 0 on Γ


{︀ }︀

[𝐿2 (Ω)2 ] denotes 𝐿2 (Ω) × 𝐿2 (Ω), and also 𝐻 1 (Ω)2 = 𝐻 1 (Ω) × 𝐻 1 (Ω)

102 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

3.1.7 Finite Element Spaces

• [𝑉ℎ ] denotes the finite element space created by fespace Vh(Th, *) in FreeFEM (see Finite Elements for *)
• [Πℎ 𝑓 ] the projection of the function 𝑓 into 𝑉ℎ (func f=x^2*y^3; Vh v = f;) means 𝑣 = 𝑃 𝑖ℎ (𝑓 ) * [{𝑣}] for
FE-function 𝑣 in 𝑉ℎ means the column vector (𝑣1 , · · · , 𝑣𝑀 )𝑇 if 𝑣 = 𝑣1 𝜑1 + · · · + 𝑣𝑀 𝜑𝑀 , which is shown by
fespace Vh(Th, P2); Vh v; cout << v[] << endl;

3.2 Mesh Generation

In this section, operators and tools on meshes are presented.


FreeFEM type for mesh variable:
• 1D mesh: meshL
• 2D mesh: mesh
• 3D volume mesh: mesh3
• 3D border meshes
– 3D surface meshS
– 3D curve meshL
Through this presentation, the principal commands for the mesh generation and links between mesh - mesh3 - meshS
- meshL are described.

3.2.1 The type mesh in 2 dimension

Commands for 2d mesh Generation

The FreeFEM type to define a 2d mesh object is mesh.

The command square

The command square triangulates the unit square.


The following generates a 4 × 5 grid in the unit square [0, 1]2 . The labels of the boundaries are shown in Fig. 3.1.

1 mesh Th = square(4, 5);

To construct a 𝑛 × 𝑚 grid in the rectangle [𝑥0 , 𝑥1 ] × [𝑦0 , 𝑦1 ], proceed as follows:

1 real x0 = 1.2;
2 real x1 = 1.8;
3 real y0 = 0;
4 real y1 = 1;
5 int n = 5;
6 real m = 20;
7 mesh Th = square(n, m, [x0+(x1-x0)*x, y0+(y1-y0)*y]);

Note: Adding the named parameter flags=icase with icase:

3.2. Mesh Generation 103


FreeFEM Documentation, Release 4.8

Fig. 3.1: Boundary labels of the mesh by square(10,10)

0. will produce a mesh where all quads are split with diagonal 𝑥 − 𝑦 = 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡
1. will produce a Union Jack flag type of mesh
2. will produce a mesh where all quads are split with diagonal 𝑥 + 𝑦 = 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡
3. same as in case 0, except two corners where the triangles are the same as case 2, to avoid having 3 vertices on
the boundary
4. same as in case 2, except two corners where the triangles are the same as case 0, to avoid having 3 vertices on
the boundary

1 mesh Th = square(n, m, [x0+(x1-x0)*x, y0+(y1-y0)*y], flags=icase);

Note: Adding the named parameter label=labs will change the 4 default label numbers to labs[i-1], for example
int[int] labs=[11, 12, 13, 14], and adding the named parameter region=10 will change the region number
to 10, for instance (v 3.8).
To see all of these flags at work, check Square mesh example:

1 for (int i = 0; i < 5; ++i){


2 int[int] labs = [11, 12, 13, 14];
3 mesh Th = square(3, 3, flags=i, label=labs, region=10);
4 plot(Th, wait=1, cmm="square flags = "+i );
5 }

104 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) Multiple border ends intersect (b) Generated mesh

Fig. 3.2: Border

The command buildmesh

mesh building with border


Boundaries are defined piecewise by parametrized curves. The pieces can only intersect at their endpoints, but it is
possible to join more than two endpoints. This can be used to structure the mesh if an area touches a border and create
new regions by dividing larger ones:

1 int upper = 1;
2 int others = 2;
3 int inner = 3;
4

5 border C01(t=0, 1){x=0; y=-1+t; label=upper;}


6 border C02(t=0, 1){x=1.5-1.5*t; y=-1; label=upper;}
7 border C03(t=0, 1){x=1.5; y=-t; label=upper;}
8 border C04(t=0, 1){x=1+0.5*t; y=0; label=others;}
9 border C05(t=0, 1){x=0.5+0.5*t; y=0; label=others;}
10 border C06(t=0, 1){x=0.5*t; y=0; label=others;}
11 border C11(t=0, 1){x=0.5; y=-0.5*t; label=inner;}
12 border C12(t=0, 1){x=0.5+0.5*t; y=-0.5; label=inner;}
13 border C13(t=0, 1){x=1; y=-0.5+0.5*t; label=inner;}
14

15 int n = 10;
16 plot(C01(-n) + C02(-n) + C03(-n) + C04(-n) + C05(-n)
17 + C06(-n) + C11(n) + C12(n) + C13(n), wait=true);
18

19 mesh Th = buildmesh(C01(-n) + C02(-n) + C03(-n) + C04(-n) + C05(-n)


20 + C06(-n) + C11(n) + C12(n) + C13(n));
21

22 plot(Th, wait=true);
23

24 cout << "Part 1 has region number " << Th(0.75, -0.25).region << endl;
25 cout << "Part 2 has redion number " << Th(0.25, -0.25).region << endl;

Borders and mesh are respectively shown in Fig. 3.2a and Fig. 3.2b.
Triangulation keywords assume that the domain is defined as being on the left (resp right) of its oriented parameterized

3.2. Mesh Generation 105


FreeFEM Documentation, Release 4.8

boundary

Γ𝑗 = {(𝑥, 𝑦) | 𝑥 = 𝜙𝑥 (𝑡), 𝑦 = 𝜙𝑦 (𝑡), 𝑎𝑗 ≤ 𝑡 ≤ 𝑏𝑗 }

To check the orientation plot 𝑡 ↦→ (𝜙𝑥 (𝑡), 𝜙𝑦 (𝑡)), 𝑡0 ≤ 𝑡 ≤ 𝑡1 . If it is as in Fig. 3.3, then the domain lies on the shaded
area, otherwise it lies on the opposite side.

Fig. 3.3: Orientation of the boundary defined by (𝜑𝑥 (𝑡), 𝜑𝑦 (𝑡))

The general expression to define a triangulation with buildmesh is

1 mesh Mesh_Name = buildmesh(Gamma1(m1)+...+GammaJ(mj), OptionalParameter);

where 𝑚𝑗 are positive or negative numbers to indicate how many vertices should be on Γ𝑗 , Γ = ∪𝐽𝑗=1 Γ𝐽 , and the
optional parameter (see also References), separated with a comma, can be:
• nbvx= int, to set the maximum number of vertices in the mesh.
• fixedborder= bool, to say if the mesh generator can change the boundary mesh or not (by default the boundary
mesh can change; beware that with periodic boundary conditions (see. Finite Element), it can be dangerous.
The orientation of boundaries can be changed by changing the sign of 𝑚𝑗 .
The following example shows how to change the orientation. The example generates the unit disk with a small circular
hole, and assigns “1” to the unit disk (“2” to the circle inside). The boundary label must be non-zero, but it can also
be omitted.

1 border a(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}


2 border b(t=0, 2*pi){x=0.3+0.3*cos(t); y=0.3*sin(t); label=2;}
3 plot(a(50) + b(30)); //to see a plot of the border mesh
4 mesh Thwithouthole = buildmesh(a(50) + b(30));
5 mesh Thwithhole = buildmesh(a(50) + b(-30));
6 plot(Thwithouthole, ps="Thwithouthole.eps");
7 plot(Thwithhole, ps="Thwithhole.eps");

Note: Notice that the orientation is changed by b(-30) in the 5th line. In the 7th line, ps="fileName" is used to
generate a postscript file with identification shown on the figure.

Note: Borders are evaluated only at the time plot or buildmesh is called so the global variables are defined at this
time. In this case, since 𝑟 is changed between the two border calls, the following code will not work because the first
border will be computed with r=0.3:

106 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) Mesh without hole (b) Mesh with hole

Fig. 3.4: Mesh with a hole

1 real r=1;
2 border a(t=0, 2*pi){x=r*cos(t); y=r*sin(t); label=1;}
3 r=0.3;
4 border b(t=0, 2*pi){x=r*cos(t); y=r*sin(t); label=1;}
5 mesh Thwithhole = buildmesh(a(50) + b(-30)); // bug (a trap) because
6 // the two circles have the same radius = :math:`0.3`

mesh building with array of border


Sometimes it can be useful to make an array of the border, but unfortunately it is incompatible with the FreeFEM
syntax. To bypass this problem, if the number of segments of the discretization 𝑛 is an array, we make an implicit loop
on all of the values of the array, and the index variable 𝑖 of the loop is defined after the parameter definition, like in
border a(t=0, 2*pi; i) . . .
A first very small example:

1 border a(t=0, 2*pi; i){x=(i+1)*cos(t); y=(i+1)*sin(t); label=1;}


2 int[int] nn = [10, 20, 30];
3 plot(a(nn)); //plot 3 circles with 10, 20, 30 points

And a more complex example to define a square with small circles:

1 real[int] xx = [0, 1, 1, 0],


2 yy = [0, 0, 1, 1];
3 //radius, center of the 4 circles
4 real[int] RC = [0.1, 0.05, 0.05, 0.1],
5 XC = [0.2, 0.8, 0.2, 0.8],
6 YC = [0.2, 0.8, 0.8, 0.2];
7 int[int] NC = [-10,-11,-12,13]; //list number of :math:`\pm` segments of the 4 circles␣
˓→borders

(continues on next page)

3.2. Mesh Generation 107


FreeFEM Documentation, Release 4.8

(continued from previous page)


9 border bb(t=0, 1; i)
10 {
11 // i is the index variable of the multi border loop
12 int ii = (i+1)%4;
13 real t1 = 1-t;
14 x = xx[i]*t1 + xx[ii]*t;
15 y = yy[i]*t1 + yy[ii]*t;
16 label = 0;
17 }
18

19 border cc(t=0, 2*pi; i)


20 {
21 x = RC[i]*cos(t) + XC[i];
22 y = RC[i]*sin(t) + YC[i];
23 label = i + 1;
24 }
25 int[int] nn = [4, 4, 5, 7]; //4 border, with 4, 4, 5, 7 segment respectively
26 plot(bb(nn), cc(NC), wait=1);
27 mesh th = buildmesh(bb(nn) + cc(NC));
28 plot(th, wait=1);

Mesh Connectivity and data

The following example explains methods to obtain mesh information.

1 // Mesh
2 mesh Th = square(2, 2);
3

4 cout << "// Get data of the mesh" << endl;


5 {
6 int NbTriangles = Th.nt;
7 real MeshArea = Th.measure;
8 real BorderLength = Th.bordermeasure;
9

10 cout << "Number of triangle(s) = " << NbTriangles << endl;


11 cout << "Mesh area = " << MeshArea << endl;
12 cout << "Border length = " << BorderLength << endl;
13

14 // Th(i) return the vextex i of Th


15 // Th[k] return the triangle k of Th
16 // Th[k][i] return the vertex i of the triangle k of Th
17 for (int i = 0; i < NbTriangles; i++)
18 for (int j = 0; j < 3; j++)
19 cout << i << " " << j << " - Th[i][j] = " << Th[i][j]
20 << ", x = " << Th[i][j].x
21 << ", y= " << Th[i][j].y
22 << ", label=" << Th[i][j].label << endl;
23 }
24

25 cout << "// Hack to get vertex coordinates" << endl;


26 {
(continues on next page)

108 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


27 fespace femp1(Th, P1);
28 femp1 Thx=x,Thy=y;
29

30 int NbVertices = Th.nv;


31 cout << "Number of vertices = " << NbVertices << endl;
32

33 for (int i = 0; i < NbVertices; i++)


34 cout << "Th(" << i << ") : " << Th(i).x << " " << Th(i).y << " " << Th(i).label
35 << endl << "\told method: " << Thx[][i] << " " << Thy[][i] << endl;
36 }
37

38 cout << "// Method to find information of point (0.55,0.6)" << endl;
39 {
40 int TNumber = Th(0.55, 0.6).nuTriangle; //the triangle number
41 int RLabel = Th(0.55, 0.6).region; //the region label
42

43 cout << "Triangle number in point (0.55, 0.6): " << TNumber << endl;
44 cout << "Region label in point (0.55, 0.6): " << RLabel << endl;
45 }
46

47 cout << "// Information of triangle" << endl;


48 {
49 int TNumber = Th(0.55, 0.6).nuTriangle;
50 real TArea = Th[TNumber].area; //triangle area
51 real TRegion = Th[TNumber].region; //triangle region
52 real TLabel = Th[TNumber].label; //triangle label, same as region for triangles
53

54 cout << "Area of triangle " << TNumber << ": " << TArea << endl;
55 cout << "Region of triangle " << TNumber << ": " << TRegion << endl;
56 cout << "Label of triangle " << TNumber << ": " << TLabel << endl;
57 }
58

59 cout << "// Hack to get a triangle containing point x, y or region number (old method)" <
˓→< endl;

60 {
61 fespace femp0(Th, P0);
62 femp0 TNumbers; //a P0 function to get triangle numbering
63 for (int i = 0; i < Th.nt; i++)
64 TNumbers[][i] = i;
65 femp0 RNumbers = region; //a P0 function to get the region number
66

67 int TNumber = TNumbers(0.55, 0.6); // Number of the triangle containing (0.55, 0,6)
68 int RNumber = RNumbers(0.55, 0.6); // Number of the region containing (0.55, 0,6)
69

70 cout << "Point (0.55,0,6) :" << endl;


71 cout << "\tTriangle number = " << TNumber << endl;
72 cout << "\tRegion number = " << RNumber << endl;
73 }
74

75 cout << "// New method to get boundary information and mesh adjacent" << endl;
76 {
77 int k = 0;
(continues on next page)

3.2. Mesh Generation 109


FreeFEM Documentation, Release 4.8

(continued from previous page)


78 int l=1;
79 int e=1;
80

81 // Number of boundary elements


82 int NbBoundaryElements = Th.nbe;
83 cout << "Number of boundary element = " << NbBoundaryElements << endl;
84 // Boundary element k in {0, ..., Th.nbe}
85 int BoundaryElement = Th.be(k);
86 cout << "Boundary element " << k << " = " << BoundaryElement << endl;
87 // Vertice l in {0, 1} of boundary element k
88 int Vertex = Th.be(k)[l];
89 cout << "Vertex " << l << " of boundary element " << k << " = " << Vertex << endl;
90 // Triangle containg the boundary element k
91 int Triangle = Th.be(k).Element;
92 cout << "Triangle containing the boundary element " << k << " = " << Triangle <<␣
˓→endl;

93 // Triangle egde nubmer containing the boundary element k


94 int Edge = Th.be(k).whoinElement;
95 cout << "Triangle edge number containing the boundary element " << k << " = " <<␣
˓→Edge << endl;

96 // Adjacent triangle of the triangle k by edge e


97 int Adjacent = Th[k].adj(e); //The value of e is changed to the corresponding edge␣
˓→in the adjacent triangle

98 cout << "Adjacent triangle of the triangle " << k << " by edge " << e << " = " <<␣
˓→Adjacent << endl;

99 cout << "\tCorresponding edge = " << e << endl;


100 // If there is no adjacent triangle by edge e, the same triangle is returned
101 //Th[k] == Th[k].adj(e)
102 // Else a different triangle is returned
103 //Th[k] != Th[k].adj(e)
104 }
105

106 cout << "// Print mesh connectivity " << endl;
107 {
108 int NbTriangles = Th.nt;
109 for (int k = 0; k < NbTriangles; k++)
110 cout << k << " : " << int(Th[k][0]) << " " << int(Th[k][1])
111 << " " << int(Th[k][2])
112 << ", label " << Th[k].label << endl;
113

114 for (int k = 0; k < NbTriangles; k++)


115 for (int e = 0, ee; e < 3; e++)
116 //set ee to e, and ee is change by method adj,
117 cout << k << " " << e << " <=> " << int(Th[k].adj((ee=e))) << " " << ee
118 << ", adj: " << (Th[k].adj((ee=e)) != Th[k]) << endl;
119

120 int NbBoundaryElements = Th.nbe;


121 for (int k = 0; k < NbBoundaryElements; k++)
122 cout << k << " : " << Th.be(k)[0] << " " << Th.be(k)[1]
123 << " , label " << Th.be(k).label
124 << ", triangle " << int(Th.be(k).Element)
125 << " " << Th.be(k).whoinElement << endl;
(continues on next page)

110 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


126

127 real[int] bb(4);


128 boundingbox(Th, bb);
129 // bb[0] = xmin, bb[1] = xmax, bb[2] = ymin, bb[3] =ymax
130 cout << "boundingbox:" << endl;
131 cout << "xmin = " << bb[0]
132 << ", xmax = " << bb[1]
133 << ", ymin = " << bb[2]
134 << ", ymax = " << bb[3] << endl;
135 }

The output is:

1 // Get data of the mesh


2 Number of triangle = 8
3 Mesh area = 1
4 Border length = 4
5 0 0 - Th[i][j] = 0, x = 0, y= 0, label=4
6 0 1 - Th[i][j] = 1, x = 0.5, y= 0, label=1
7 0 2 - Th[i][j] = 4, x = 0.5, y= 0.5, label=0
8 1 0 - Th[i][j] = 0, x = 0, y= 0, label=4
9 1 1 - Th[i][j] = 4, x = 0.5, y= 0.5, label=0
10 1 2 - Th[i][j] = 3, x = 0, y= 0.5, label=4
11 2 0 - Th[i][j] = 1, x = 0.5, y= 0, label=1
12 2 1 - Th[i][j] = 2, x = 1, y= 0, label=2
13 2 2 - Th[i][j] = 5, x = 1, y= 0.5, label=2
14 3 0 - Th[i][j] = 1, x = 0.5, y= 0, label=1
15 3 1 - Th[i][j] = 5, x = 1, y= 0.5, label=2
16 3 2 - Th[i][j] = 4, x = 0.5, y= 0.5, label=0
17 4 0 - Th[i][j] = 3, x = 0, y= 0.5, label=4
18 4 1 - Th[i][j] = 4, x = 0.5, y= 0.5, label=0
19 4 2 - Th[i][j] = 7, x = 0.5, y= 1, label=3
20 5 0 - Th[i][j] = 3, x = 0, y= 0.5, label=4
21 5 1 - Th[i][j] = 7, x = 0.5, y= 1, label=3
22 5 2 - Th[i][j] = 6, x = 0, y= 1, label=4
23 6 0 - Th[i][j] = 4, x = 0.5, y= 0.5, label=0
24 6 1 - Th[i][j] = 5, x = 1, y= 0.5, label=2
25 6 2 - Th[i][j] = 8, x = 1, y= 1, label=3
26 7 0 - Th[i][j] = 4, x = 0.5, y= 0.5, label=0
27 7 1 - Th[i][j] = 8, x = 1, y= 1, label=3
28 7 2 - Th[i][j] = 7, x = 0.5, y= 1, label=3
29 // Hack to get vertex coordinates
30 Number of vertices = 9
31 Th(0) : 0 0 4
32 old method: 0 0
33 Th(1) : 0.5 0 1
34 old method: 0.5 0
35 Th(2) : 1 0 2
36 old method: 1 0
37 Th(3) : 0 0.5 4
38 old method: 0 0.5
39 Th(4) : 0.5 0.5 0
(continues on next page)

3.2. Mesh Generation 111


FreeFEM Documentation, Release 4.8

(continued from previous page)


40 old method: 0.5 0.5
41 Th(5) : 1 0.5 2
42 old method: 1 0.5
43 Th(6) : 0 1 4
44 old method: 0 1
45 Th(7) : 0.5 1 3
46 old method: 0.5 1
47 Th(8) : 1 1 3
48 old method: 1 1
49 // Method to find the information of point (0.55,0.6)
50 Triangle number in point (0.55, 0.6): 7
51 Region label in point (0.55, 0.6): 0
52 // Information of a triangle
53 Area of triangle 7: 0.125
54 Region of triangle 7: 0
55 Label of triangle 7: 0
56 // Hack to get a triangle containing point x, y or region number (old method)
57 Point (0.55,0,6) :
58 Triangle number = 7
59 Region number = 0
60 // New method to get boundary information and mesh adjacent
61 Number of boundary element = 8
62 Boundary element 0 = 0
63 Vertex 1 of boundary element 0 = 1
64 Triangle containing the boundary element 0 = 0
65 Triangle edge number containing the boundary element 0 = 2
66 Adjacent triangle of the triangle 0 by edge 1 = 1
67 Corresponding edge = 2
68 // Print mesh connectivity
69 0 : 0 1 4, label 0
70 1 : 0 4 3, label 0
71 2 : 1 2 5, label 0
72 3 : 1 5 4, label 0
73 4 : 3 4 7, label 0
74 5 : 3 7 6, label 0
75 6 : 4 5 8, label 0
76 7 : 4 8 7, label 0
77 0 0 <=> 3 1, adj: 1
78 0 1 <=> 1 2, adj: 1
79 0 2 <=> 0 2, adj: 0
80 1 0 <=> 4 2, adj: 1
81 1 1 <=> 1 1, adj: 0
82 1 2 <=> 0 1, adj: 1
83 2 0 <=> 2 0, adj: 0
84 2 1 <=> 3 2, adj: 1
85 2 2 <=> 2 2, adj: 0
86 3 0 <=> 6 2, adj: 1
87 3 1 <=> 0 0, adj: 1
88 3 2 <=> 2 1, adj: 1
89 4 0 <=> 7 1, adj: 1
90 4 1 <=> 5 2, adj: 1
91 4 2 <=> 1 0, adj: 1
(continues on next page)

112 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


92 5 0 <=> 5 0, adj: 0
93 5 1 <=> 5 1, adj: 0
94 5 2 <=> 4 1, adj: 1
95 6 0 <=> 6 0, adj: 0
96 6 1 <=> 7 2, adj: 1
97 6 2 <=> 3 0, adj: 1
98 7 0 <=> 7 0, adj: 0
99 7 1 <=> 4 0, adj: 1
100 7 2 <=> 6 1, adj: 1
101 0 : 0 1 , label 1, triangle 0 2
102 1 : 1 2 , label 1, triangle 2 2
103 2 : 2 5 , label 2, triangle 2 0
104 3 : 5 8 , label 2, triangle 6 0
105 4 : 6 7 , label 3, triangle 5 0
106 5 : 7 8 , label 3, triangle 7 0
107 6 : 0 3 , label 4, triangle 1 1
108 7 : 3 6 , label 4, triangle 5 1
109 boundingbox:
110 xmin = 0, xmax = 1, ymin = 0, ymax = 1

The real characteristic function of a mesh Th is chi(Th) in 2D and 3D where:


chi(Th)(P)=1 if 𝑃 ∈ 𝑇 ℎ
chi(Th)(P)=0 if 𝑃 ̸∈ 𝑇 ℎ

The keyword “triangulate”

FreeFEM is able to build a triangulation from a set of points. This triangulation is a Delaunay mesh of the convex hull
of the set of points. It can be useful to build a mesh from a table function.
The coordinates of the points and the value of the table function are defined separately with rows of the form: x y
f(x,y) in a file such as:
1 0.51387 0.175741 0.636237
2 0.308652 0.534534 0.746765
3 0.947628 0.171736 0.899823
4 0.702231 0.226431 0.800819
5 0.494773 0.12472 0.580623
6 0.0838988 0.389647 0.456045
7 ...............

The third column of each line is left untouched by the triangulate command. But you can use this third value to
define a table function with rows of the form: x y f(x,y).
The following example shows how to make a mesh from the file xyf with the format stated just above. The command
triangulate only uses the 1st and 2nd columns.
1 // Build the Delaunay mesh of the convex hull
2 mesh Thxy=triangulate("xyf"); //points are defined by the first 2 columns of file `xyf`
3

4 // Plot the created mesh


5 plot(Thxy);
6

(continues on next page)

3.2. Mesh Generation 113


FreeFEM Documentation, Release 4.8

(a) Delaunay mesh of the convex hull of point set in file xy


(b) Isolvalue of table function

Fig. 3.5: Triangulate

(continued from previous page)


7 // Fespace
8 fespace Vhxy(Thxy, P1);
9 Vhxy fxy;
10

11 // Reading the 3rd column to define the function fxy


12 {
13 ifstream file("xyf");
14 real xx, yy;
15 for(int i = 0; i < fxy.n; i++)
16 file >> xx >> yy >> fxy[][i]; //to read third row only.
17 //xx and yy are just skipped
18 }
19

20 // Plot
21 plot(fxy);

One new way to build a mesh is to have two arrays: one for the 𝑥 values and the other for the 𝑦 values.

1 //set two arrays for the x's and y's


2 Vhxy xx=x, yy=y;
3 //build the mesh
4 mesh Th = triangulate(xx[], yy[]);

114 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) The empty mesh with boundary (b) An empty mesh defined from a pseudo region numbering
of triangle

Fig. 3.6: Empty mesh

2d Finite Element space on a boundary

To define a Finite Element space on a boundary, we came up with the idea of a mesh with no internal points (called
empty mesh). It can be useful to handle Lagrange multipliers in mixed and mortar methods.
So the function emptymesh removes all the internal points of a mesh except points on internal boundaries.

1 {
2 border a(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
3 mesh Th = buildmesh(a(20));
4 Th = emptymesh(Th);
5 plot(Th);
6 }

It is also possible to build an empty mesh of a pseudo subregion with emptymesh(Th, ssd) using the set of edges
from the mesh Th; an edge 𝑒 is in this set when, with the two adjacent triangles 𝑒 = 𝑡1 ∩ 𝑡2 and 𝑠𝑠𝑑[𝑇 1] ̸= 𝑠𝑠𝑑[𝑇 2]
where 𝑠𝑠𝑑 refers to the pseudo region numbering of triangles, they are stored in the int[int] array of size “the number
of triangles”.

1 {
2 mesh Th = square(10, 10);
3 int[int] ssd(Th.nt);
4 //build the pseudo region numbering
5 for(int i = 0; i < ssd.n; i++){
6 int iq = i/2; //because 2 triangles per quad
7 int ix = iq%10;
8 int iy = iq/10;
9 ssd[i] = 1 + (ix>=5) + (iy>=5)*2;
10 }
11 //build emtpy with all edges $e=T1 \cap T2$ and $ssd[T1] \neq ssd[T2]$
12 Th = emptymesh(Th, ssd);
13 //plot
14 plot(Th);
15 savemesh(Th, "emptymesh.msh");
16 }

3.2. Mesh Generation 115


FreeFEM Documentation, Release 4.8

Remeshing

The command movemesh

Meshes can be translated, rotated, and deformed by movemesh; this is useful for elasticity to watch the deformation
due to the displacement Φ(𝑥, 𝑦) = (Φ1 (𝑥, 𝑦), Φ2 (𝑥, 𝑦)) of shape.
It is also useful to handle free boundary problems or optimal shape problems.
If Ω is triangulated as 𝑇ℎ (Ω), and Φ is a displacement vector then Φ(𝑇ℎ ) is obtained by:

1 mesh Th = movemesh(Th,[Phi1, Phi2]);

Sometimes the transformed mesh is invalid because some triangles have flipped over (meaning it now has a negative
area). To spot such problems, one may check the minimum triangle area in the transformed mesh with checkmovemesh
before any real transformation.
For example:

Φ1 (𝑥, 𝑦) = 𝑥 + 𝑘 * sin(𝑦 * 𝜋)/10)


Φ2 (𝑥, 𝑦) = 𝑦 + 𝑘 * cos(𝑦𝜋)/10)

for a big number 𝑘 > 1.

1 verbosity = 4;
2

3 // Parameters
4 real coef = 1;
5

6 // Mesh
7 border a(t=0, 1){x=t; y=0; label=1;};
8 border b(t=0, 0.5){x=1; y=t; label=1;};
9 border c(t=0, 0.5){x=1-t; y=0.5; label=1;};
10 border d(t=0.5, 1){x=0.5; y=t; label=1;};
11 border e(t=0.5, 1){x=1-t; y=1; label=1;};
12 border f(t=0, 1){x=0; y=1-t; label=1;};
13 mesh Th = buildmesh(a(6) + b(4) + c(4) + d(4) + e(4) + f(6));
14 plot(Th, wait=true, fill=true, ps="Lshape.eps");
15

16 // Function
17 func uu = sin(y*pi)/10;
18 func vv = cos(x*pi)/10;
19

20 // Checkmovemesh
21 real minT0 = checkmovemesh(Th, [x, y]); //return the min triangle area
22 while(1){ // find a correct move mesh
23 real minT = checkmovemesh(Th, [x+coef*uu, y+coef*vv]);
24 if (minT > minT0/5) break; //if big enough
25 coef /= 1.5;
26 }
27

28 // Movemesh
29 Th = movemesh(Th, [x+coef*uu, y+coef*vv]);
30 plot(Th, wait=true, fill=true, ps="MovedMesh.eps");

116 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) L-shape (b) Moved L-shape

Fig. 3.7: Move mesh

Note: Consider a function 𝑢 defined on a mesh Th. A statement like Th=movemesh(Th...) does not change 𝑢 and
so the old mesh still exists. It will be destroyed when no function uses it. A statement like 𝑢 = 𝑢 redefines 𝑢 on the
new mesh Th with interpolation and therefore destroys the old Th, if 𝑢 was the only function using it.

Now, we give an example of moving a mesh with a Lagrangian function 𝑢 defined on the moving mesh.

1 // Parameters
2 int nn = 10;
3 real dt = 0.1;
4

5 // Mesh
6 mesh Th = square(nn, nn);
7

8 // Fespace
9 fespace Vh(Th, P1);
10 Vh u=y;
11

12 // Loop
13 real t=0;
14 for (int i = 0; i < 4; i++){
15 t = i*dt;
16 Vh f=x*t;
17 real minarea = checkmovemesh(Th, [x, y+f]);
18 if (minarea > 0) //movemesh will be ok
19 Th = movemesh(Th, [x, y+f]);
20

21 cout << " Min area = " << minarea << endl;
22

23 real[int] tmp(u[].n);
24 tmp = u[]; //save the value
(continues on next page)

3.2. Mesh Generation 117


FreeFEM Documentation, Release 4.8

(continued from previous page)


25 u = 0;//to change the FEspace and mesh associated with u
26 u[] = tmp;//set the value of u without any mesh update
27 plot(Th, u, wait=true);
28 }
29 // In this program, since u is only defined on the last mesh, all the
30 // previous meshes are deleted from memory.

The command hTriangle

This section presents the way to obtain a regular triangulation with FreeFEM.
For a set 𝑆, we define the diameter of 𝑆 by

diam(𝑆) = sup{|x − y|; x, y ∈ 𝑆}

The sequence {𝒯ℎ }ℎ→0 of Ω is called regular if they satisfy the following:
1. limℎ→0 max{diam(𝑇𝑘 )| 𝑇𝑘 ∈ 𝒯ℎ } = 0
𝜌(𝑇𝑘 )
2. There is a number 𝜎 > 0 independent of ℎ such that diam(𝑇𝑘 ) ≥𝜎 for all 𝑇𝑘 ∈ 𝒯ℎ where 𝜌(𝑇𝑘 ) are the diameter
of the inscribed circle of 𝑇𝑘 .
We put ℎ(𝒯ℎ ) = max{diam(𝑇𝑘 )| 𝑇𝑘 ∈ 𝒯ℎ }, which is obtained by

1 mesh Th = ......;
2 fespace Ph(Th, P0);
3 Ph h = hTriangle;
4 cout << "size of mesh = " << h[].max << endl;

The command adaptmesh

The function:

𝑓 (𝑥, 𝑦) = 10.0𝑥3 + 𝑦 3 + tan−1 [𝜀/(sin(5.0𝑦) − 2.0𝑥)], 𝜀 = 0.0001

sharply varies in value and the initial mesh given by one of the commands in the Mesh Generation part cannot reflect
its sharp variations.

1 // Parameters
2 real eps = 0.0001;
3 real h = 1;
4 real hmin = 0.05;
5 func f = 10.0*x^3 + y^3 + h*atan2(eps, sin(5.0*y)-2.0*x);
6

7 // Mesh
8 mesh Th = square(5, 5, [-1+2*x, -1+2*y]);
9

10 // Fespace
11 fespace Vh(Th,P1);
12 Vh fh = f;
13 plot(fh);
14

(continues on next page)

118 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


15 // Adaptmesh
16 for (int i = 0; i < 2; i++){
17 Th = adaptmesh(Th, fh);
18 fh = f; //old mesh is deleted
19 plot(Th, fh, wait=true);
20 }

Fig. 3.8: 3D graphs for the initial mesh and 1st and 2nd mesh adaptations

FreeFEM uses a variable metric/Delaunay automatic meshing algorithm.


The command:

1 mesh ATh = adaptmesh(Th, f);

create the new mesh ATh adapted to the Hessian

𝐷2 𝑓 = (𝜕 2 𝑓 /𝜕𝑥2 , 𝜕 2 𝑓 /𝜕𝑥𝜕𝑦, 𝜕 2 𝑓 /𝜕𝑦 2 )

of a function (formula or FE-function).


Mesh adaptation is a very powerful tool when the solution of a problem varies locally and sharply.
Here we solve the Poisson’s problem, when 𝑓 = 1 and Ω is an L-shape domain.

Tip: The solution has the singularity 𝑟3/2 , 𝑟 = |𝑥 − 𝛾| at the point 𝛾 of the intersection of two lines 𝑏𝑐 and 𝑏𝑑 (see
Fig. 3.9a).

3.2. Mesh Generation 119


FreeFEM Documentation, Release 4.8

(b) Final solution after 4-times adaptation


(a) L-shape domain and its boundary name

Fig. 3.9: Mesh adaptation

1 // Parameters
2 real error = 0.1;
3

4 // Mesh
5 border ba(t=0, 1){x=t; y=0; label=1;}
6 border bb(t=0, 0.5){x=1; y=t; label=1;}
7 border bc(t=0, 0.5){x=1-t; y=0.5; label=1;}
8 border bd(t=0.5, 1){x=0.5; y=t; label=1;}
9 border be(t=0.5, 1){x=1-t; y=1; label=1;}
10 border bf(t=0, 1){x=0; y=1-t; label=1;}
11 mesh Th = buildmesh(ba(6) + bb(4) + bc(4) + bd(4) + be(4) + bf(6));
12

13 // Fespace
14 fespace Vh(Th, P1);
15 Vh u, v;
16

17 // Function
18 func f = 1;
19

20 // Problem
21 problem Poisson(u, v, solver=CG, eps=1.e-6)
22 = int2d(Th)(
23 dx(u)*dx(v)
24 + dy(u)*dy(v)
25 )
26 - int2d(Th)(
27 f*v
28 )
29 + on(1, u=0);
30

(continues on next page)

120 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


31 // Adaptmesh loop
32 for (int i = 0; i < 4; i++){
33 Poisson;
34 Th = adaptmesh(Th, u, err=error);
35 error = error/2;
36 }
37

38 // Plot
39 plot(u);

To speed up the adaptation, the default parameter err of adaptmesh is changed by hand; it specifies the required
precision, so as to make the new mesh finer or coarser.
The problem is coercive and symmetric, so the linear system can be solved with the conjugate gradient method (pa-
rameter solver=CG) with the stopping criteria on the residual, here eps=1.e-6).
By adaptmesh, the slope of the final solution is correctly computed near the point of intersection of 𝑏𝑐 and 𝑏𝑑 as in
Fig. 3.9b.
This method is described in detail in [HECHT1998]. It has a number of default parameters which can be modified.
If f1,f2 are functions and thold, Thnew are meshes:

1 Thnew = adaptmesh(Thold, f1 ... );


2 Thnew = adaptmesh(Thold, f1,f2 ... ]);
3 Thnew = adaptmesh(Thold, [f1,f2] ... );

The additional parameters of adaptmesh are:


See Reference part for more inforamtions
• hmin= Minimum edge size. Its default is related to the size of the domain to be meshed and the precision of the
mesh generator.
• hmax= Maximum edge size. It defaults to the diameter of the domain to be meshed.
• err= 𝑃1 interpolation error level (0.01 is the default).

• errg= Relative geometrical error. By default this error is 0.01, and in any case it must be lower than 1/ 2.
Meshes created with this option may have some edges smaller than the -hmin due to geometrical con-
straints.
• nbvx= Maximum number of vertices generated by the mesh generator (9000 is the default).
• nbsmooth= number of iterations of the smoothing procedure (5 is the default).
• nbjacoby= number of iterations in a smoothing procedure during the metric construction, 0 means no smoothing,
6 is the default.
• ratio= ratio for a prescribed smoothing on the metric. If the value is 0 or less than 1.1 no smoothing is
done on the metric. 1.8 is the default. If ratio > 1.1, the speed of mesh size variations is bounded
by 𝑙𝑜𝑔(ratio).

Note: As ratio gets closer to 1, the number of generated vertices increases. This may be useful to control
the thickness of refined regions near shocks or boundary layers.

• omega= relaxation parameter for the smoothing procedure. 1.0 is the default.
• iso= If true, forces the metric to be isotropic. false is the default.

3.2. Mesh Generation 121


FreeFEM Documentation, Release 4.8

• abserror= If false, the metric is evaluated using the criteria of equi-repartion of relative error. false is
the default. In this case the metric is defined by:
(︂ )︂𝑝
1 |ℋ|
ℳ=
err coef2 𝑚𝑎𝑥(CutOff, |𝜂|)

Otherwise, the metric is evaluated using the criteria of equi-distribution of errors. In this case the metric is
defined by:
(︂ )︂𝑝
1 |ℋ|
ℳ= .
err coef2 sup(𝜂) − inf(𝜂)

• cutoff= lower limit for the relative error evaluation. 1.0e-6 is the default.
• verbosity= informational messages level (can be chosen between 0 and ∞). Also changes the value of the
global variable verbosity (obsolete).
• inquire= To inquire graphically about the mesh. false is the default.
• splitpbedge= If true, splits all internal edges in half with two boundary vertices. true is the default.
• maxsubdiv= Changes the metric such that the maximum subdivision of a background edge is bound by val.
Always limited by 10, and 10 is also the default.
• rescaling= if true, the function, with respect to which the mesh is adapted, is rescaled to be between 0 and 1.
true is the default.
• keepbackvertices= if true, tries to keep as many vertices from the original mesh as possible. true is
the default.
• IsMetric= if true, the metric is defined explicitly. false is the default. If the 3 functions 𝑚11 , 𝑚12 , 𝑚22 are
given, they directly define a symmetric matrix field whose Hessian is computed to define a metric. If only
one function is given, then it represents the isotropic mesh size at every point.
For example, if the partial derivatives fxx (= 𝜕 2 𝑓 /𝜕𝑥2 ), fxy (= 𝜕 2 𝑓 /𝜕𝑥𝜕𝑦), fyy (= 𝜕 2 𝑓 /𝜕𝑦 2 ) are given,
we can set Th = adaptmesh(Th, fxx, fxy, fyy, IsMetric=1, nbvx=10000, hmin=hmin);
• power= exponent power of the Hessian used to compute the metric. 1 is the default.
• thetamax= minimum corner angle in degrees. Default is 10∘ where the corner is 𝐴𝐵𝐶 and the angle is the
angle of the two vectors 𝐴𝐵, 𝐵𝐶, (0 imply no corner, 90 imply perpendicular corner, . . . ).
• splitin2= boolean value. If true, splits all triangles of the final mesh into 4 sub-triangles.
• metric= an array of 3 real arrays to set or get metric data information. The size of these three arrays must
be the number of vertices. So if m11,m12,m22 are three P1 finite elements related to the mesh to adapt,
you can write: metric=[m11[],m12[],m22[]] (see file convect-apt.edp for a full example)
• nomeshgeneration= If true, no adapted mesh is generated (useful to compute only a metric).
• periodic= Writing periodic=[[4,y],[2,y],[1,x],[3,x]]; builds an adapted periodic mesh. The
sample builds a biperiodic mesh of a square. (see periodic finite element spaces, and see the Sphere
example for a full example)
We can use the command adaptmesh to build a uniform mesh with a constant mesh size. To build a mesh with a
constant mesh size equal to 30
1
try:

1 mesh Th=square(2, 2); //the initial mesh


2 plot(Th, wait=true, ps="square-0.eps");
3

4 Th = adaptmesh(Th, 1./30., IsMetric=1, nbvx=10000);


(continues on next page)

122 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


5 plot(Th, wait=true, ps="square-1.eps");
6

7 Th = adaptmesh(Th, 1./30., IsMetric=1, nbvx=10000); //More the one time du to


8 Th = adaptmesh(Th, 1./30., IsMetric=1, nbvx=10000); //Adaptation bound `maxsubdiv=`
9 plot(Th, wait=true, ps="square-2.eps");

The command trunc

Two operators have been introduced to remove triangles from a mesh or to divide them. Operator trunc has the
following parameters:
• boolean function to keep or remove elements
• label= sets the label number of new boundary item, one by default.
• split= sets the level 𝑛 of triangle splitting. Each triangle is split in 𝑛 × 𝑛, one by default.
To create the mesh Th3 where all triangles of a mesh Th are split in 3×3, just write:

1 mesh Th3 = trunc(Th, 1, split=3);

The following example construct all “trunced” meshes to the support of the basic function of the space Vh
(cf. abs(u)>0), split all the triangles in 5×5, and put a label number to 2 on a new boundary.

1 // Mesh
2 mesh Th = square(3, 3);
3

4 // Fespace
5 fespace Vh(Th, P1);
6 Vh u=0;
7

8 // Loop on all degrees of freedom


9 int n=u.n;
10 for (int i = 0; i < n; i++){
11 u[][i] = 1; // The basis function i
12 plot(u, wait=true);
13 mesh Sh1 = trunc(Th, abs(u)>1.e-10, split=5, label=2);
14 plot(Th, Sh1, wait=true, ps="trunc"+i+".eps");
15 u[][i] = 0; // reset
16 }

The command change

This command changes the label of elements and border elements of a mesh.
Changing the label of elements and border elements will be done using the keyword change. The parameters for this
command line are for two dimensional and three dimensional cases:
• refe= is an array of integers to change the references on edges
• reft= is an array of integers to change the references on triangles
• label= is an array of integers to change the 4 default label numbers
• region= is an array of integers to change the default region numbers

3.2. Mesh Generation 123


FreeFEM Documentation, Release 4.8

(a) Initial mesh (b) First iteration

(c) Last iteration

Fig. 3.10: Mesh adaptation

(a) Mesh of support the function P1 number 0, split in 5×5 (b) Mesh of support the function P1 number 6, split in 5×5

Fig. 3.11: Trunc

124 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• renumv= is an array of integers, which explicitly gives the new numbering of vertices in the new mesh. By
default, this numbering is that of the original mesh
• renumt= is an array of integers, which explicitly gives the new numbering of elements in the new mesh, according
the new vertices numbering given by renumv=. By default, this numbering is that of the original mesh
• flabel= is an integer function given the new value of the label
• fregion= is an integer function given the new value of the region
• rmledges= is an integer to remove edges in the new mesh, following a label
• rmInternalEdges= is a boolean, if equal to true to remove the internal edges. By default, the internal edges
are stored
These vectors are composed of 𝑛𝑙 successive pairs of numbers 𝑂, 𝑁 where 𝑛𝑙 is the number (label or region) that we
want to change. For example, we have :

label = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]


(3.1)
region = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]

An application example is given here:

1 // Mesh
2 mesh Th1 = square(10, 10);
3 mesh Th2 = square(20, 10, [x+1, y]);
4

5 int[int] r1=[2,0];
6 plot(Th1, wait=true);
7

8 Th1 = change(Th1, label=r1); //change the label of Edges 2 in 0.


9 plot(Th1, wait=true);
10

11 // boundary label: 1 -> 1 bottom, 2 -> 1 right, 3->1 top, 4->1 left boundary label is 1
12 int[int] re=[1,1, 2,1, 3,1, 4,1]
13 Th2=change(Th2,refe=re);
14 plot(Th2,wait=1) ;

The command splitmesh

Another way to split mesh triangles is to use splitmesh, for example:

1 // Mesh
2 border a(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
3 mesh Th = buildmesh(a(20));
4 plot(Th, wait=true, ps="NotSplittedMesh.eps");
5

6 // Splitmesh
7 Th = splitmesh(Th, 1 + 5*(square(x-0.5) + y*y));
8 plot(Th, wait=true, ps="SplittedMesh.eps");

3.2. Mesh Generation 125


FreeFEM Documentation, Release 4.8

(a) Initial mesh (b) All left mesh triangle is split conformaly in
int(1+5*(square(x-0.5)+y*y)^2 triangles

Fig. 3.12: Split mesh

Meshing Examples

Tip: Two rectangles touching by a side

1 border a(t=0, 1){x=t; y=0;};


2 border b(t=0, 1){x=1; y=t;};
3 border c(t=1, 0){x=t; y=1;};
4 border d(t=1, 0){x=0; y=t;};
5 border c1(t=0, 1){x=t; y=1;};
6 border e(t=0, 0.2){x=1; y=1+t;};
7 border f(t=1, 0){x=t; y=1.2;};
8 border g(t=0.2, 0){x=0; y=1+t;};
9 int n=1;
10 mesh th = buildmesh(a(10*n) + b(10*n) + c(10*n) + d(10*n));
11 mesh TH = buildmesh(c1(10*n) + e(5*n) + f(10*n) + g(5*n));
12 plot(th, TH, ps="TouchSide.esp");

126 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Fig. 3.13: Two rectangles touching by a side

Tip: NACA0012 Airfoil

1 border upper(t=0, 1){x=t; y=0.17735*sqrt(t) - 0.075597*t - 0.212836*(t^2) + 0.17363*(t^


˓→3) - 0.06254*(t^4);}

2 border lower(t=1, 0){x = t; y=-(0.17735*sqrt(t) -0.075597*t - 0.212836*(t^2) + 0.


˓→17363*(t^3) - 0.06254*(t^4));}

3 border c(t=0, 2*pi){x=0.8*cos(t) + 0.5; y=0.8*sin(t);}


4 mesh Th = buildmesh(c(30) + upper(35) + lower(35));
5 plot(Th, ps="NACA0012.eps", bw=true);

Fig. 3.14: NACA0012 Airfoil

Tip: Cardioid

3.2. Mesh Generation 127


FreeFEM Documentation, Release 4.8

1 real b = 1, a = b;
2 border C(t=0, 2*pi){x=(a+b)*cos(t)-b*cos((a+b)*t/b); y=(a+b)*sin(t)-b*sin((a+b)*t/b);}
3 mesh Th = buildmesh(C(50));
4 plot(Th, ps="Cardioid.eps", bw=true);

Fig. 3.15: Domain with Cardioid curve boundary

Tip: Cassini Egg

1 border C(t=0, 2*pi) {x=(2*cos(2*t)+3)*cos(t); y=(2*cos(2*t)+3)*sin(t);}


2 mesh Th = buildmesh(C(50));
3 plot(Th, ps="Cassini.eps", bw=true);

Fig. 3.16: Domain with Cassini egg curve boundary

Tip: By cubic Bezier curve

128 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

1 // A cubic Bezier curve connecting two points with two control points
2 func real bzi(real p0, real p1, real q1, real q2, real t){
3 return p0*(1-t)^3 + q1*3*(1-t)^2*t + q2*3*(1-t)*t^2 + p1*t^3;
4 }
5

6 real[int] p00 = [0, 1], p01 = [0, -1], q00 = [-2, 0.1], q01 = [-2, -0.5];
7 real[int] p11 = [1,-0.9], q10 = [0.1, -0.95], q11=[0.5, -1];
8 real[int] p21 = [2, 0.7], q20 = [3, -0.4], q21 = [4, 0.5];
9 real[int] q30 = [0.5, 1.1], q31 = [1.5, 1.2];
10 border G1(t=0, 1){
11 x=bzi(p00[0], p01[0], q00[0], q01[0], t);
12 y=bzi(p00[1], p01[1], q00[1], q01[1], t);
13 }
14 border G2(t=0, 1){
15 x=bzi(p01[0], p11[0], q10[0], q11[0], t);
16 y=bzi(p01[1], p11[1], q10[1], q11[1], t);
17 }
18 border G3(t=0, 1){
19 x=bzi(p11[0], p21[0], q20[0], q21[0], t);
20 y=bzi(p11[1], p21[1], q20[1], q21[1], t);
21 }
22 border G4(t=0, 1){
23 x=bzi(p21[0], p00[0], q30[0], q31[0], t);
24 y=bzi(p21[1], p00[1], q30[1], q31[1], t);
25 }
26 int m = 5;
27 mesh Th = buildmesh(G1(2*m) + G2(m) + G3(3*m) + G4(m));
28 plot(Th, ps="Bezier.eps", bw=true);

Fig. 3.17: Boundary drawn by Bezier curves

Tip: Section of Engine

1 real a = 6., b = 1., c = 0.5;


2

3 border L1(t=0, 1){x=-a; y=1+b-2*(1+b)*t;}


4 border L2(t=0, 1){x=-a+2*a*t; y=-1-b*(x/a)*(x/a)*(3-2*abs(x)/a );}
5 border L3(t=0, 1){x=a; y=-1-b+(1+b)*t; }
(continues on next page)

3.2. Mesh Generation 129


FreeFEM Documentation, Release 4.8

(continued from previous page)


6 border L4(t=0, 1){x=a-a*t; y=0;}
7 border L5(t=0, pi){x=-c*sin(t)/2; y=c/2-c*cos(t)/2;}
8 border L6(t=0, 1){x=a*t; y=c;}
9 border L7(t=0, 1){x=a; y=c+(1+b-c)*t;}
10 border L8(t=0, 1){x=a-2*a*t; y=1+b*(x/a)*(x/a)*(3-2*abs(x)/a);}
11 mesh Th = buildmesh(L1(8) + L2(26) + L3(8) + L4(20) + L5(8) + L6(30) + L7(8) + L8(30));
12 plot(Th, ps="Engine.eps", bw=true);

Fig. 3.18: Section of Engine

Tip: Domain with U-shape channel

1 real d = 0.1; //width of U-shape


2 border L1(t=0, 1-d){x=-1; y=-d-t;}
3 border L2(t=0, 1-d){x=-1; y=1-t;}
4 border B(t=0, 2){x=-1+t; y=-1;}
5 border C1(t=0, 1){x=t-1; y=d;}
6 border C2(t=0, 2*d){x=0; y=d-t;}
7 border C3(t=0, 1){x=-t; y=-d;}
8 border R(t=0, 2){x=1; y=-1+t;}
9 border T(t=0, 2){x=1-t; y=1;}
10 int n = 5;
11 mesh Th = buildmesh(L1(n/2) + L2(n/2) + B(n) + C1(n) + C2(3) + C3(n) + R(n) + T(n));
12 plot(Th, ps="U-shape.eps", bw=true);

Fig. 3.19: Domain with U-shape channel changed by d

130 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Tip: Domain with V-shape cut

1 real dAg = 0.02; //angle of V-shape


2 border C(t=dAg, 2*pi-dAg){x=cos(t); y=sin(t);};
3 real[int] pa(2), pb(2), pc(2);
4 pa[0] = cos(dAg);
5 pa[1] = sin(dAg);
6 pb[0] = cos(2*pi-dAg);
7 pb[1] = sin(2*pi-dAg);
8 pc[0] = 0;
9 pc[1] = 0;
10 border seg1(t=0, 1){x=(1-t)*pb[0]+t*pc[0]; y=(1-t)*pb[1]+t*pc[1];};
11 border seg2(t=0, 1){x=(1-t)*pc[0]+t*pa[0]; y=(1-t)*pc[1]+t*pa[1];};
12 mesh Th = buildmesh(seg1(20) + C(40) + seg2(20));
13 plot(Th, ps="V-shape.eps", bw=true);

Fig. 3.20: Domain with V-shape cut changed by dAg

Tip: Smiling face

1 real d=0.1; int m = 5; real a = 1.5, b = 2, c = 0.7, e = 0.01;


2

3 border F(t=0, 2*pi){x=a*cos(t); y=b*sin(t);}


4 border E1(t=0, 2*pi){x=0.2*cos(t)-0.5; y=0.2*sin(t)+0.5;}
5 border E2(t=0, 2*pi){x=0.2*cos(t)+0.5; y=0.2*sin(t)+0.5;}
6 func real st(real t){
7 return sin(pi*t) - pi/2;
8 }
9 border C1(t=-0.5, 0.5){x=(1-d)*c*cos(st(t)); y=(1-d)*c*sin(st(t));}
(continues on next page)

3.2. Mesh Generation 131


FreeFEM Documentation, Release 4.8

(continued from previous page)


10 border C2(t=0, 1){x=((1-d)+d*t)*c*cos(st(0.5)); y=((1-d)+d*t)*c*sin(st(0.5));}
11 border C3(t=0.5, -0.5){x=c*cos(st(t)); y=c*sin(st(t));}
12 border C4(t=0, 1){x=(1-d*t)*c*cos(st(-0.5)); y=(1-d*t)*c*sin(st(-0.5));}
13 border C0(t=0, 2*pi){x=0.1*cos(t); y=0.1*sin(t);}
14

15 mesh Th=buildmesh(F(10*m) + C1(2*m) + C2(3) + C3(2*m) + C4(3)


16 + C0(m) + E1(-2*m) + E2(-2*m));
17 plot(Th, ps="SmileFace.eps", bw=true);

Fig. 3.21: Smiling face (Mouth is changeable)

Tip: 3 points bending

1 // Square for Three-Point Bend Specimens fixed on Fix1, Fix2


2 // It will be loaded on Load.
3 real a = 1, b = 5, c = 0.1;
4 int n = 5, m = b*n;
5 border Left(t=0, 2*a){x=-b; y=a-t;}
6 border Bot1(t=0, b/2-c){x=-b+t; y=-a;}
7 border Fix1(t=0, 2*c){x=-b/2-c+t; y=-a;}
8 border Bot2(t=0, b-2*c){x=-b/2+c+t; y=-a;}
9 border Fix2(t=0, 2*c){x=b/2-c+t; y=-a;}
10 border Bot3(t=0, b/2-c){x=b/2+c+t; y=-a;}
11 border Right(t=0, 2*a){x=b; y=-a+t;}
(continues on next page)

132 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


12 border Top1(t=0, b-c){x=b-t; y=a;}
13 border Load(t=0, 2*c){x=c-t; y=a;}
14 border Top2(t=0, b-c){x=-c-t; y=a;}
15 mesh Th = buildmesh(Left(n) + Bot1(m/4) + Fix1(5) + Bot2(m/2)
16 + Fix2(5) + Bot3(m/4) + Right(n) + Top1(m/2) + Load(10) + Top2(m/2));
17 plot(Th, ps="ThreePoint.eps", bw=true);

Fig. 3.22: Domain for three-point bending test

3.2.2 The type mesh3 in 3 dimension

Note: Up to the version 3, FreeFEM allowed to consider a surface problem such as the PDE is treated like boundary
conditions on the boundary domain (on triangles describing the boundary domain). With the version 4, in particular
4.2.1, a completed model for surface problem is possible, with the definition of a surface mesh and a surface problem
with a variational form on domain ( with triangle elements) and application of boundary conditions on border domain
(describing by edges). The keywords to define a surface mesh is meshS.

3d mesh generation

Note: For 3D mesh tools, put load "msh3" at the top of the .edp script.

The command cube

The function cube like its 2d function square is a simple way to build cubic objects, it is contained in plugin msh3
(import with load "msh3").
The following code generates a 3 × 4 × 5 grid in the unit cube [0, 1]3 .

1 mesh3 Th = cube(3, 4, 5);

By default the labels are :


1. face 𝑦 = 0,

3.2. Mesh Generation 133


FreeFEM Documentation, Release 4.8

2. face 𝑥 = 1,
3. face 𝑦 = 1,
4. face 𝑥 = 0,
5. face 𝑧 = 0,
6. face 𝑧 = 1
and the region number is 0.
A full example of this function to build a mesh of cube ]−1, 1[3 with face label given by (𝑖𝑥+4*(𝑖𝑦 +1)+16*(𝑖𝑧 +1))
where (𝑖𝑥, 𝑖𝑦, 𝑖𝑧) are the coordinates of the barycenter of the current face, is given below.

1 load "msh3"
2

3 int[int] l6 = [37, 42, 45, 40, 25, 57];


4 int r11 = 11;
5 mesh3 Th = cube(4, 5, 6, [x*2-1, y*2-1, z*2-1], label=l6, flags =3, region=r11);
6

7 cout << "Volume = " << Th.measure << ", border area = " << Th.bordermeasure << endl;
8

9 int err = 0;
10 for(int i = 0; i < 100; ++i){
11 real s = int2d(Th,i)(1.);
12 real sx = int2d(Th,i)(x);
13 real sy = int2d(Th,i)(y);
14 real sz = int2d(Th,i)(z);
15

16 if(s){
17 int ix = (sx/s+1.5);
18 int iy = (sy/s+1.5);
19 int iz = (sz/s+1.5);
20 int ii = (ix + 4*(iy+1) + 16*(iz+1) );
21 //value of ix,iy,iz => face min 0, face max 2, no face 1
22 cout << "Label = " << i << ", s = " << s << " " << ix << iy << iz << " : " << ii
˓→<< endl;

23 if( i != ii ) err++;
24 }
25 }
26 real volr11 = int3d(Th,r11)(1.);
27 cout << "Volume region = " << 11 << ": " << volr11 << endl;
28 if((volr11 - Th.measure )>1e-8) err++;
29 plot(Th, fill=false);
30 cout << "Nb err = " << err << endl;
31 assert(err==0);

The output of this script is:

1 Enter: BuildCube: 3
2 kind = 3 n tet Cube = 6 / n slip 6 19
3 Cube nv=210 nt=720 nbe=296
4 Out: BuildCube
5 Volume = 8, border area = 24
6 Label = 25, s = 4 110 : 25
7 Label = 37, s = 4 101 : 37
(continues on next page)

134 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


8 Label = 40, s = 4 011 : 40
9 Label = 42, s = 4 211 : 42
10 Label = 45, s = 4 121 : 45
11 Label = 57, s = 4 112 : 57
12 Volume region = 11: 8
13 Nb err = 0

Fig. 3.23: The 3D mesh of function cube(4, 5, 6, flags=3)

The command buildlayers

This mesh is obtained by extending a two dimensional mesh in the 𝑧-axis.


The domain Ω3𝑑 defined by the layer mesh is equal to Ω3𝑑 = Ω2𝑑 × [𝑧𝑚𝑖𝑛, 𝑧𝑚𝑎𝑥] where Ω2𝑑 is the domain defined
by the two dimensional meshes. 𝑧𝑚𝑖𝑛 and 𝑧𝑚𝑎𝑥 are functions of Ω2𝑑 in R that defines respectively the lower surface
and upper surface of Ω3𝑑 .
For a vertex of a two dimensional mesh 𝑉𝑖2𝑑 = (𝑥𝑖 , 𝑦𝑖 ), we introduce the number of associated vertices in the 𝑧−axis
𝑀𝑖 + 1.
We denote by 𝑀 the maximum of 𝑀𝑖 over the vertices of the two dimensional mesh. This value is called the number
of layers (if ∀𝑖, 𝑀𝑖 = 𝑀 then there are 𝑀 layers in the mesh of Ω3𝑑 ). 𝑉𝑖2𝑑 generated 𝑀 + 1 vertices which are defined
by:
3𝑑
∀𝑗 = 0, . . . , 𝑀, 𝑉𝑖,𝑗 = (𝑥𝑖 , 𝑦𝑖 , 𝜃𝑖 (𝑧𝑖,𝑗 )),

where (𝑧𝑖,𝑗 )𝑗=0,...,𝑀 are the 𝑀 + 1 equidistant points on the interval [𝑧𝑚𝑖𝑛(𝑉𝑖2𝑑 ), 𝑧𝑚𝑎𝑥(𝑉𝑖2𝑑 )]:

𝑧𝑚𝑎𝑥(𝑉𝑖2𝑑 ) − 𝑧𝑚𝑖𝑛(𝑉𝑖2𝑑 )
𝑧𝑖,𝑗 = 𝑗 𝛿𝛼 + 𝑧𝑚𝑖𝑛(𝑉𝑖2𝑑 ), 𝛿𝛼 = .
𝑀
The function 𝜃𝑖 , defined on [𝑧𝑚𝑖𝑛(𝑉𝑖2𝑑 ), 𝑧𝑚𝑎𝑥(𝑉𝑖2𝑑 )], is given by:

𝜃𝑖,0 if 𝑧 = 𝑧𝑚𝑖𝑛(𝑉𝑖2𝑑 ),
{︂
𝜃𝑖 (𝑧) =
𝜃𝑖,𝑗 if 𝑧 ∈]𝜃𝑖,𝑗−1 , 𝜃𝑖,𝑗 ],

3.2. Mesh Generation 135


FreeFEM Documentation, Release 4.8

Fig. 3.24: Example of Layer mesh in three dimensions.

with (𝜃𝑖,𝑗 )𝑗=0,...,𝑀𝑖 are the 𝑀𝑖 + 1 equidistant points on the interval [𝑧𝑚𝑖𝑛(𝑉𝑖2𝑑 ), 𝑧𝑚𝑎𝑥(𝑉𝑖2𝑑 )].
Set a triangle 𝐾 = (𝑉𝑖12𝑑 , 𝑉𝑖22𝑑 , 𝑉𝑖32𝑑 ) of the two dimensional mesh. 𝐾 is associated with a triangle on the upper surface
(resp. on the lower surface) of layer mesh:
3𝑑
(𝑉𝑖1,𝑀 3𝑑
, 𝑉𝑖2,𝑀 3𝑑
, 𝑉𝑖3,𝑀 ) (resp. (𝑉𝑖1,0
3𝑑 3𝑑
, 𝑉𝑖2,0 3𝑑
, 𝑉𝑖3,0 )).
Also 𝐾 is associated with 𝑀 volume prismatic elements which are defined by:
3𝑑 3𝑑 3𝑑 3𝑑 3𝑑 3𝑑
∀𝑗 = 0, . . . , 𝑀, 𝐻𝑗 = (𝑉𝑖1,𝑗 , 𝑉𝑖2,𝑗 , 𝑉𝑖3,𝑗 , 𝑉𝑖1,𝑗+1 , 𝑉𝑖2,𝑗+1 , 𝑉𝑖3,𝑗+1 ).

Theses volume elements can have some merged point:


• 0 merged point : prism
• 1 merged points : pyramid
• 2 merged points : tetrahedra
• 3 merged points : no elements
The elements with merged points are called degenerate elements. To obtain a mesh with tetrahedra, we decompose the
pyramid into two tetrahedra and the prism into three tetrahedra. These tetrahedra are obtained by cutting the quadrilat-
eral face of pyramid and prism with the diagonal which have the vertex with the maximum index (see [HECHT1992]
for the reason of this choice).
The triangles on the middle surface obtained with the decomposition of the volume prismatic elements are the triangles
generated by the edges on the border of the two dimensional mesh. The label of triangles on the border elements and
tetrahedra are defined with the label of these associated elements.
The arguments of buildlayers is a two dimensional mesh and the number of layers 𝑀 .
The parameters of this command are:
• zbound= [𝑧𝑚𝑖𝑛, 𝑧𝑚𝑎𝑥] where 𝑧𝑚𝑖𝑛 and 𝑧𝑚𝑎𝑥 are functions expression. Theses functions define the lower
surface mesh and upper mesh of surface mesh.

136 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• coef= A function expression between [0,1]. This parameter is used to introduce degenerate element in mesh.
The number of associated points or vertex 𝑉𝑖2𝑑 is the integer part of 𝑐𝑜𝑒𝑓 (𝑉𝑖2𝑑 )𝑀 .
• region= This vector is used to initialize the region of tetrahedra.
This vector contains successive pairs of the 2d region number at index 2𝑖 and the corresponding 3d region number
at index 2𝑖 + 1, like change.
• labelmid= This vector is used to initialize the 3d labels number of the vertical face or mid face from the 2d
label number.
This vector contains successive pairs of the 2d label number at index 2𝑖 and the corresponding 3d label number
at index 2𝑖 + 1, like change.
• labelup= This vector is used to initialize the 3d label numbers of the upper/top face from the 2d region number.
This vector contains successive pairs of the 2d region number at index 2𝑖 and the corresponding 3d label number
at index 2𝑖 + 1, like change.
• labeldown= Same as the previous case but for the lower/down face label.
Moreover, we also add post processing parameters that allow to moving the mesh. These parameters correspond to
parameters transfo, facemerge and ptmerge of the command line movemesh.
The vector region, labelmid, labelup and labeldown These vectors are composed of 𝑛𝑙 successive pairs of number
𝑂𝑖 , 𝑁𝑙 where 𝑛𝑙 is the number (label or region) that we want to get.
An example of this command is given in the Build layer mesh example.

Tip: Cube

1 //Cube.idp
2 load "medit"
3 load "msh3"
4

5 func mesh3 Cube (int[int] &NN, real[int, int] &BB, int[int, int] &L){
6 real x0 = BB(0,0), x1 = BB(0,1);
7 real y0 = BB(1,0), y1 = BB(1,1);
8 real z0 = BB(2,0), z1 = BB(2,1);
9

10 int nx = NN[0], ny = NN[1], nz = NN[2];


11

12 // 2D mesh
13 mesh Thx = square(nx, ny, [x0+(x1-x0)*x, y0+(y1-y0)*y]);
14

15 // 3D mesh
16 int[int] rup = [0, L(2,1)], rdown=[0, L(2,0)];
17 int[int] rmid=[1, L(1,0), 2, L(0,1), 3, L(1,1), 4, L(0,0)];
18 mesh3 Th = buildlayers(Thx, nz, zbound=[z0,z1],
19 labelmid=rmid, labelup = rup, labeldown = rdown);
20

21 return Th;
22 }

Tip: Unit cube

3.2. Mesh Generation 137


FreeFEM Documentation, Release 4.8

1 include "Cube.idp"
2

3 int[int] NN = [10,10,10]; //the number of step in each direction


4 real [int, int] BB = [[0,1],[0,1],[0,1]]; //the bounding box
5 int [int, int] L = [[1,2],[3,4],[5,6]]; //the label of the 6 face left,right, front,␣
˓→back, down, right

6 mesh3 Th = Cube(NN, BB, L);


7 medit("Th", Th);

Fig. 3.25: The mesh of a cube made with cube.edp

Tip: Cone
An axisymtric mesh on a triangle with degenerateness

1 load "msh3"
2 load "medit"
3

4 // Parameters
5 real RR = 1;
6 real HH = 1;
7

8 int nn=10;
9

10 // 2D mesh
11 border Taxe(t=0, HH){x=t; y=0; label=0;}
12 border Hypo(t=1, 0){x=HH*t; y=RR*t; label=1;}
13 border Vert(t=0, RR){x=HH; y=t; label=2;}
14 mesh Th2 = buildmesh(Taxe(HH*nn) + Hypo(sqrt(HH*HH+RR*RR)*nn) + Vert(RR*nn));
15 plot(Th2, wait=true);
(continues on next page)

138 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


16

17 // 3D mesh
18 real h = 1./nn;
19 int MaxLayersT = (int(2*pi*RR/h)/4)*4;//number of layers
20 real zminT = 0;
21 real zmaxT = 2*pi; //height 2*pi
22 func fx = y*cos(z);
23 func fy = y*sin(z);
24 func fz = x;
25 int[int] r1T = [0,0], r2T = [0,0,2,2], r4T = [0,2];
26 //trick function:
27 //The function defined the proportion
28 //of number layer close to axis with reference MaxLayersT
29 func deg = max(.01, y/max(x/HH, 0.4)/RR);
30 mesh3 Th3T = buildlayers(Th2, coef=deg, MaxLayersT,
31 zbound=[zminT, zmaxT], transfo=[fx, fy, fz],
32 facemerge=0, region=r1T, labelmid=r2T);
33 medit("cone", Th3T);

Fig. 3.26: The mesh of a cone made with cone.edp

Tip: Buildlayer mesh

1 load "msh3"
2 load "TetGen"
3 load "medit"
4

5 // Parameters
6 int C1 = 99;
7 int C2 = 98;
8

9 // 2D mesh
(continues on next page)

3.2. Mesh Generation 139


FreeFEM Documentation, Release 4.8

(continued from previous page)


10 border C01(t=0, pi){x=t; y=0; label=1;}
11 border C02(t=0, 2*pi){ x=pi; y=t; label=1;}
12 border C03(t=0, pi){ x=pi-t; y=2*pi; label=1;}
13 border C04(t=0, 2*pi){ x=0; y=2*pi-t; label=1;}
14

15 border C11(t=0, 0.7){x=0.5+t; y=2.5; label=C1;}


16 border C12(t=0, 2){x=1.2; y=2.5+t; label=C1;}
17 border C13(t=0, 0.7){x=1.2-t; y=4.5; label=C1;}
18 border C14(t=0, 2){x=0.5; y=4.5-t; label=C1;}
19

20 border C21(t=0, 0.7){x=2.3+t; y=2.5; label=C2;}


21 border C22(t=0, 2){x=3; y=2.5+t; label=C2;}
22 border C23(t=0, 0.7){x=3-t; y=4.5; label=C2;}
23 border C24(t=0, 2){x=2.3; y=4.5-t; label=C2;}
24

25 mesh Th = buildmesh(C01(10) + C02(10) + C03(10) + C04(10)


26 + C11(5) + C12(5) + C13(5) + C14(5)
27 + C21(-5) + C22(-5) + C23(-5) + C24(-5));
28

29 mesh Ths = buildmesh(C01(10) + C02(10) + C03(10) + C04(10)


30 + C11(5) + C12(5) + C13(5) + C14(5));
31

32 // Construction of a box with one hole and two regions


33 func zmin = 0.;
34 func zmax = 1.;
35 int MaxLayer = 10;
36

37 func XX = x*cos(y);
38 func YY = x*sin(y);
39 func ZZ = z;
40

41 int[int] r1 = [0, 41], r2 = [98, 98, 99, 99, 1, 56];


42 int[int] r3 = [4, 12]; //the triangles of uppper surface mesh
43 //generated by the triangle in the 2D region
44 //of mesh Th of label 4 as label 12
45 int[int] r4 = [4, 45]; //the triangles of lower surface mesh
46 //generated by the triangle in the 2D region
47 //of mesh Th of label 4 as label 45.
48

49 mesh3 Th3 = buildlayers(Th, MaxLayer, zbound=[zmin, zmax], region=r1,


50 labelmid=r2, labelup=r3, labeldown=r4);
51 medit("box 2 regions 1 hole", Th3);
52

53 // Construction of a sphere with TetGen


54 func XX1 = cos(y)*sin(x);
55 func YY1 = sin(y)*sin(x);
56 func ZZ1 = cos(x);
57

58 real[int] domain = [0., 0., 0., 0, 0.001];


59 string test = "paACQ";
60 cout << "test = " << test << endl;
61 mesh3 Th3sph = tetgtransfo(Ths, transfo=[XX1, YY1, ZZ1],
(continues on next page)

140 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


62 switch=test, nbofregions=1, regionlist=domain);
63 medit("sphere 2 regions", Th3sph);

Remeshing

Note: if an operation on a mesh3 is performed then the same operation is applyed on its surface part (its meshS
associated)

The command change

This command changes the label of elements and border elements of a mesh. It’s the equivalent command in 2d mesh
case.
Changing the label of elements and border elements will be done using the keyword change. The parameters for this
command line are for two dimensional and three dimensional cases:
• reftet= is a vector of integer that contains successive pairs of the old label number to the new label number.
• refface= is a vector of integer that contains successive pairs of the old region number to new region number.
• flabel= is an integer function given the new value of the label.
• fregion= is an integer function given the new value of the region.
• rmInternalFaces= is a boolean, equal true to remove the internal faces.
• rmlfaces= is a vector of integer, where triangle’s label given are remove of the mesh
These vectors are composed of 𝑛𝑙 successive pairs of numbers 𝑂, 𝑁 where 𝑛𝑙 is the number (label or region) that we
want to change. For example, we have:

label = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]


region = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]

An example of use:

1 // Mesh
2 mesh3 Th1 = cube(10, 10);
3 mesh3 Th2 = cube(20, 10, [x+1, y,z]);
4

5 int[int] r1=[2,0];
6 plot(Th1, wait=true);
7

8 Th1 = change(Th1, label=r1); //change the label of Edges 2 in 0.


9 plot(Th1, wait=true);
10

11 // boundary label: 1 -> 1 bottom, 2 -> 1 right, 3->1 top, 4->1 left boundary label is 1
12 int[int] re=[1,1, 2,1, 3,1, 4,1]
13 Th2=change(Th2,refe=re);
14 plot(Th2,wait=1) ;

3.2. Mesh Generation 141


FreeFEM Documentation, Release 4.8

The command trunc

This operator have been introduce to remove a piece of mesh or/and split all element or for a particular label element
The three named parameter - boolean function to keep or remove elements - split= sets the level n of triangle splitting.
each triangle is splitted in n × n ( one by default) - freefem:label= sets the label number of new boundary item (1 by
default)
An example of use

1 load "msh3"
2 load "medit"
3 int nn=8;
4 mesh3 Th=cube(nn,nn,nn);
5 // remove the small cube $]1/2,1[^2$
6 Th= trunc(Th,((x<0.5) |(y< 0.5)| (z<0.5)), split=3, label=3);
7 medit("cube",Th);

The command movemesh

3D meshes can be translated, rotated, and deformed using the command line movemesh as in the 2D case (see section
movemesh). If Ω is tetrahedrized as 𝑇ℎ (Ω), and Φ(𝑥, 𝑦) = (Φ1(𝑥, 𝑦, 𝑧), Φ2(𝑥, 𝑦, 𝑧), Φ3(𝑥, 𝑦, 𝑧)) is the transformation
vector then Φ(𝑇ℎ ) is obtained by:

1 mesh3 Th = movemesh(Th, [Phi1, Phi2, Phi3], ...);


2 mesh3 Th = movemesh3(Th, transfo=[Phi1, Phi2, Phi3], ...); (syntax with transfo=)

The parameters of movemesh in three dimensions are:


• transfo= sets the geometric transformation Φ(𝑥, 𝑦) = (Φ1(𝑥, 𝑦, 𝑧), Φ2(𝑥, 𝑦, 𝑧), Φ3(𝑥, 𝑦, 𝑧))
• region= sets the integer labels of the tetrahedra. 0 by default.
• label= sets the labels of the border faces. This parameter is initialized as the label for the keyword change.
• facemerge= An integer expression. When you transform a mesh, some faces can be merged. This parameter
equals to one if the merges’ faces is considered. Otherwise it equals to zero. By default, this parameter is
equal to 1.
• ptmerge = A real expression. When you transform a mesh, some points can be merged. This parameter is the
criteria to define two merging points. By default, we use

𝑝𝑡𝑚𝑒𝑟𝑔𝑒 = 1𝑒 − 7 𝑉 𝑜𝑙(𝐵),

where 𝐵 is the smallest axis parallel boxes containing the discretion domain of Ω and 𝑉 𝑜𝑙(𝐵) is the volume of
this box.
• orientation = An integer expression equal 1, give the oientation of the triangulation, elements must be in the
reference orientation (counter clock wise) equal -1 reverse the orientation of the tetrahedra

Note: The orientation of tetrahedra are checked by the positivity of its area and automatically corrected during the
building of the adjacency.

An example of this command can be found in the Poisson’s equation 3D example.

142 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

1 load "medit"
2 include "cube.idp"
3 int[int] Nxyz=[20,5,5];
4 real [int,int] Bxyz=[[0.,5.],[0.,1.],[0.,1.]];
5 int [int,int] Lxyz=[[1,2],[2,2],[2,2]];
6 real E = 21.5e4;
7 real sigma = 0.29;
8 real mu = E/(2*(1+sigma));
9 real lambda = E*sigma/((1+sigma)*(1-2*sigma));
10 real gravity = -0.05;
11 real sqrt2=sqrt(2.);
12

13 mesh3 Th=Cube(Nxyz,Bxyz,Lxyz);
14 fespace Vh(Th,[P1,P1,P1]);
15 Vh [u1,u2,u3], [v1,v2,v3];
16

17 macro epsilon(u1,u2,u3) [dx(u1),dy(u2),dz(u3),(dz(u2)+dy(u3))/sqrt2,(dz(u1)+dx(u3))/


˓→sqrt2,(dy(u1)+dx(u2))/sqrt2] // EOM

18 macro div(u1,u2,u3) ( dx(u1)+dy(u2)+dz(u3) ) // EOM


19

20 solve Lame([u1,u2,u3],[v1,v2,v3])=
21 int3d(Th)(
22 lambda*div(u1,u2,u3)*div(v1,v2,v3)
23 +2.*mu*( epsilon(u1,u2,u3)'*epsilon(v1,v2,v3) )
24 )
25 - int3d(Th) (gravity*v3)
26 + on(1,u1=0,u2=0,u3=0);
27

28 real dmax= u1[].max;


29 real coef= 0.1/dmax;
30

31 int[int] ref2=[1,0,2,0]; // array


32 mesh3 Thm=movemesh(Th,[x+u1*coef,y+u2*coef,z+u3*coef],label=ref2);
33 // mesh3 Thm=movemesh3(Th,transfo=[x+u1*coef,y+u2*coef,z+u3*coef],label=ref2); older␣
˓→syntax

34 Thm=change(Thm,label=ref2);
35 plot(Th,Thm, wait=1,cmm="coef amplification = "+coef );

movemesh doesn’t use the prefix tranfo= [.,.,.], the geometric transformation is directly given by [.,.,.] in the arguments
list

The command extract

This command offers the possibility to extract a boundary part of a mesh3


• refface , is a vector of integer that contains a list of triangle face references, where the extract function must
be apply.
• label , is a vector of integer that contains a list of tetrahedra label

1 load"msh3"
2 int nn = 30;
3 int[int] labs = [1, 2, 2, 1, 1, 2]; // Label numbering
(continues on next page)

3.2. Mesh Generation 143


FreeFEM Documentation, Release 4.8

(continued from previous page)


4 mesh3 Th = cube(nn, nn, nn, label=labs);
5 // extract the surface (boundary) of the cube
6 int[int] llabs = [1, 2];
7 meshS ThS = extract(Th,label=llabs);

The command buildSurface

This new function allows to build the surface mesh of a volume mesh, under the condition the surface is the boundary of
the volume. By definition, a mesh3 is defined by a list of vertices, tetrahedron elements and triangle border elements.
buildSurface function create the meshS corresponding, given the list vertices which are on the border domain, the
triangle elements and build the list of edges. Remark, for a closed surface mesh, the edges list is empty.

The command movemesh23

A simple method to tranform a 2D mesh in 3D Surface mesh. The principe is to project a two dimensional domain in
a three dimensional space, 2d surface in the (x,y,z)-space to create a surface mesh 3D, meshS.

Warning: Since the release 4.2.1, the FreeFEM function movemesh23 returns a meshS type.

This corresponds to translate, rotate or deforme the domain by a displacement vector of this form Φ(x, y) =
(Φ1(𝑥, 𝑦), Φ2(𝑥, 𝑦), Φ3(𝑥, 𝑦)).
The result of moving a two dimensional mesh Th2 by this three dimensional displacement is obtained using:

1 **meshS** Th3 = movemesh23(Th2, transfo=[Phi(1), Phi(2), Phi(3)]);

The parameters of this command line are:


• transfo= [Φ1, Φ2, Φ3] sets the displacement vector of transformation Φ(x, y) =
[Φ1(𝑥, 𝑦), Φ2(𝑥, 𝑦), Φ3(𝑥, 𝑦)].
• label= sets an integer label of triangles.
• orientation= sets an integer orientation to give the global orientation of the surface of mesh. Equal 1, give a
triangulation in the reference orientation (counter clock wise) equal -1 reverse the orientation of the triangles
• ptmerge= A real expression. When you transform a mesh, some points can be merged. This parameter is the
criteria to define two merging points. By default, we use

𝑝𝑡𝑚𝑒𝑟𝑔𝑒 = 1𝑒 − 7 𝑉 𝑜𝑙(𝐵),

where 𝐵 is the smallest axis, parallel boxes containing the discretized domain of Ω and 𝑉 𝑜𝑙(𝐵) is the volume
of this box.
We can do a “gluing” of surface meshes using the process given in Change section. An example to obtain a three
dimensional mesh using the command line tetg and movemesh23 is given below.

1 load "msh3"
2 load "tetgen"
3

4 // Parameters
(continues on next page)

144 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


5 real x10 = 1.;
6 real x11 = 2.;
7 real y10 = 0.;
8 real y11 = 2.*pi;
9

10 func ZZ1min = 0;
11 func ZZ1max = 1.5;
12 func XX1 = x;
13 func YY1 = y;
14

15 real x20 = 1.;


16 real x21 = 2.;
17 real y20=0.;
18 real y21=1.5;
19

20 func ZZ2 = y;
21 func XX2 = x;
22 func YY2min = 0.;
23 func YY2max = 2*pi;
24

25 real x30=0.;
26 real x31=2*pi;
27 real y30=0.;
28 real y31=1.5;
29

30 func XX3min = 1.;


31 func XX3max = 2.;
32 func YY3 = x;
33 func ZZ3 = y;
34

35 // Mesh
36 mesh Thsq1 = square(5, 35, [x10+(x11-x10)*x, y10+(y11-y10)*y]);
37 mesh Thsq2 = square(5, 8, [x20+(x21-x20)*x, y20+(y21-y20)*y]);
38 mesh Thsq3 = square(35, 8, [x30+(x31-x30)*x, y30+(y31-y30)*y]);
39

40 // Mesh 2D to 3D surface
41 meshS Th31h = movemesh23(Thsq1, transfo=[XX1, YY1, ZZ1max], orientation=1);
42 meshS Th31b = movemesh23(Thsq1, transfo=[XX1, YY1, ZZ1min], orientation=-1);
43

44 meshS Th32h = movemesh23(Thsq2, transfo=[XX2, YY2max, ZZ2], orientation=-1);


45 meshS Th32b = movemesh23(Thsq2, transfo=[XX2, YY2min, ZZ2], orientation=1);
46

47 meshS Th33h = movemesh23(Thsq3, transfo=[XX3max, YY3, ZZ3], orientation=1);


48 meshS Th33b = movemesh23(Thsq3, transfo=[XX3min, YY3, ZZ3], orientation=-1);
49

50 // Gluing surfaces
51 meshS Th33 = Th31h + Th31b + Th32h + Th32b + Th33h + Th33b;
52 plot(Th33, cmm="Th33");
53

54 // Tetrahelize the interior of the cube with TetGen


55 real[int] domain =[1.5, pi, 0.75, 145, 0.0025];
56 meshS Thfinal = tetg(Th33, switch="paAAQY", regionlist=domain);
(continues on next page)

3.2. Mesh Generation 145


FreeFEM Documentation, Release 4.8

(continued from previous page)


57 plot(Thfinal, cmm="Thfinal");
58

59 // Build a mesh of a half cylindrical shell of interior radius 1, and exterior radius 2␣
˓→and a height of 1.5

60 func mv2x = x*cos(y);


61 func mv2y = x*sin(y);
62 func mv2z = z;
63 meshS Thmv2 = movemesh(Thfinal, transfo=[mv2x, mv2y, mv2z], facemerge=0);
64 plot(Thmv2, cmm="Thmv2");

3d Meshing examples

Tip: Lake

1 load "msh3"
2 load "medit"
3

4 // Parameters
5 int nn = 5;
6

7 // 2D mesh
8 border cc(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
9 mesh Th2 = buildmesh(cc(100));
10

11 // 3D mesh
12 int[int] rup = [0, 2], rlow = [0, 1];
13 int[int] rmid = [1, 1, 2, 1, 3, 1, 4, 1];
14 func zmin = 2-sqrt(4-(x*x+y*y));
15 func zmax = 2-sqrt(3.);
16

17 mesh3 Th = buildlayers(Th2, nn,


18 coef=max((zmax-zmin)/zmax, 1./nn),
19 zbound=[zmin,zmax],
20 labelmid=rmid,
21 labelup=rup,
22 labeldown=rlow);
23

24 medit("Th", Th);

Tip: Hole region

1 load "msh3"
2 load "TetGen"
3 load "medit"
4

5 // 2D mesh
6 mesh Th = square(10, 20, [x*pi-pi/2, 2*y*pi]); // ]-pi/2, pi/2[X]0,2pi[
7

(continues on next page)

146 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


8 // 3D mesh
9 //parametrization of a sphere
10 func f1 = cos(x)*cos(y);
11 func f2 = cos(x)*sin(y);
12 func f3 = sin(x);
13 //partial derivative of the parametrization
14 func f1x = sin(x)*cos(y);
15 func f1y = -cos(x)*sin(y);
16 func f2x = -sin(x)*sin(y);
17 func f2y = cos(x)*cos(y);
18 func f3x = cos(x);
19 func f3y = 0;
20 //M = DF^t DF
21 func m11 = f1x^2 + f2x^2 + f3x^2;
22 func m21 = f1x*f1y + f2x*f2y + f3x*f3y;
23 func m22 = f1y^2 + f2y^2 + f3y^2;
24

25 func perio = [[4, y], [2, y], [1, x], [3, x]];
26 real hh = 0.1;
27 real vv = 1/square(hh);
28 verbosity = 2;
29 Th = adaptmesh(Th, m11*vv, m21*vv, m22*vv, IsMetric=1, periodic=perio);
30 Th = adaptmesh(Th, m11*vv, m21*vv, m22*vv, IsMetric=1, periodic=perio);
31 plot(Th, wait=true);
32

33 //construction of the surface of spheres


34 real Rmin = 1.;
35 func f1min = Rmin*f1;
36 func f2min = Rmin*f2;
37 func f3min = Rmin*f3;
38

39 meshS ThSsph = movemesh23(Th, transfo=[f1min, f2min, f3min]);


40

41 real Rmax = 2.;


42 func f1max = Rmax*f1;
43 func f2max = Rmax*f2;
44 func f3max = Rmax*f3;
45

46 meshS ThSsph2 = movemesh23(Th, transfo=[f1max, f2max, f3max]);


47

48 //gluing meshes
49 meshS ThS = ThSsph + ThSsph2;
50

51 cout << " TetGen call without hole " << endl;
52 real[int] domain2 = [1.5, 0., 0., 145, 0.001, 0.5, 0., 0., 18, 0.001];
53 mesh3 Th3fin = tetg(ThS, switch="paAAQYY", nbofregions=2, regionlist=domain2);
54 medit("Sphere with two regions", Th3fin);
55

56 cout << " TetGen call with hole " << endl;
57 real[int] hole = [0.,0.,0.];
58 real[int] domain = [1.5, 0., 0., 53, 0.001];
59 mesh3 Th3finhole = tetg(ThS, switch="paAAQYY",
(continues on next page)

3.2. Mesh Generation 147


FreeFEM Documentation, Release 4.8

(continued from previous page)


60 nbofholes=1, holelist=hole, nbofregions=1, regionlist=domain);
61 medit("Sphere with a hole", Th3finhole);

Tip: Build a 3d mesh of a cube with a balloon

1 load "msh3"
2 load "TetGen"
3 load "medit"
4 include "MeshSurface.idp"
5

6 // Parameters
7 real hs = 0.1; //mesh size on sphere
8 int[int] N = [20, 20, 20];
9 real [int,int] B = [[-1, 1], [-1, 1], [-1, 1]];
10 int [int,int] L = [[1, 2], [3, 4], [5, 6]];
11

12 // Meshes
13 meshS ThH = SurfaceHex(N, B, L, 1);
14 meshS ThS = Sphere(0.5, hs, 7, 1);
15

16 meshS ThHS = ThH + ThS;


17 medit("Hex-Sphere", ThHS);
18

19 real voltet = (hs^3)/6.;


20 cout << "voltet = " << voltet << endl;
21 real[int] domain = [0, 0, 0, 1, voltet, 0, 0, 0.7, 2, voltet];
22 mesh3 Th = tetg(ThHS, switch="pqaAAYYQ", nbofregions=2, regionlist=domain);
23 medit("Cube with ball", Th);

3.2.3 The type meshS in 3 dimension

Warning: Since the release 4.2.1, the surface mesh3 object (list of vertices and border elements, without tetahedra
elements) is remplaced by meshS type.

Commands for 3d surface mesh generation

The command square3

The function square3 like the function square in 2d is the simple way to a build the unit square plan in the space
R3 . To use this command, it is necessary to load the pluging msh3 (need load "msh3"). A square in 3d consists in
building a 2d square which is projected from R2 to R3 . The parameters of this command line are:
• n,m generates a n×m grid in the unit square
• [.,.,.] is [ Φ1, Φ2, Φ3 ] is the geometric transformation from R2 to R3 . By default, [ Φ1, Φ2, Φ3 ] = [x,y,0]
• orientation= equal 1, gives the orientation of the triangulation, elements are in the reference orientation
(counter clock wise) equal -1 reverse the orientation of the triangles it’s the global orientation of the surface

148 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) The surface mesh of the hex with internal sphere (b) The tetrahedral mesh of the cube with internal ball

Fig. 3.27: Cube sphere

1 extern (-1 intern)

1 real R = 3, r=1;
2 real h = 0.2; //
3 int nx = R*2*pi/h;
4 int ny = r*2*pi/h;
5 func torex= (R+r*cos(y*pi*2))*cos(x*pi*2);
6 func torey= (R+r*cos(y*pi*2))*sin(x*pi*2);
7 func torez= r*sin(y*pi*2);
8

10 meshS ThS=square3(nx,ny,[torex,torey,torez],orientation=-1) ;

The following code generates a 3 × 4 × 5 grid in the unit cube [0, 1]3 with a clock wise triangulation.

surface mesh builders

Adding at the top of a FreeFEM script include "MeshSurface.idp", constructors of sphere, ellipsoid, surface mesh
of a 3d box are available.
• SurfaceHex(N, B, L, orient)
– this operator allows to build the surface mesh of a 3d box
– int[int] N=[nx,ny,nz]; // the number of seg in the 3 direction
– real [int,int] B=[[xmin,xmax],[ymin,ymax],[zmin,zmax]]; // bounding bax
– int [int,int] L=[[1,2],[3,4],[5,6]]; // the label of the 6 face left,right, front, back, down, right
– orient the global orientation of the surface 1 extern (-1 intern),
– returns a meshS type
• Ellipsoide (RX, RY, RZ, h, L, OX, OY, OZ, orient)

3.2. Mesh Generation 149


FreeFEM Documentation, Release 4.8

– h is the mesh size


– L is the label
– orient the global orientation of the surface 1 extern (-1 intern)
– OX, OY, OZ are real numbers to give the Ellipsoide center ( optinal, by default is (0,0,0) )
– where RX, RY, RZ are real numbers such as the parametric equations of the ellipsoid is:
– returns a meshS type


𝜋 𝜋 ⃒ 𝑥=Rx 𝑐𝑜𝑠(𝑢)𝑐𝑜𝑠(𝑣)+Ox
∀𝑢 ∈ [− , [ and 𝑣 ∈ [0, 2𝜋], ⃒⃒ 𝑦=Ry 𝑐𝑜𝑠(𝑢)𝑠𝑖𝑛(𝑣)+Oy
2 2 𝑧=Rz 𝑠𝑖𝑛(𝑣)+Oz

• Sphere(R, h, L, OX, OY, OZ, orient)


– where R is the raduis of the sphere,
– OX, OY, OZ are real numbers to give the Ellipsoide center ( optinal, by default is (0,0,0) )
– h is the mesh size of the shpere
– L is the label the the sphere
– orient the global orientation of the surface 1 extern (-1 intern)
– returns a meshS type
1 func meshS SurfaceHex(int[int] & N,real[int,int] &B ,int[int,int] & L,int orientation){
2 real x0=B(0,0),x1=B(0,1);
3 real y0=B(1,0),y1=B(1,1);
4 real z0=B(2,0),z1=B(2,1);
5

6 int nx=N[0],ny=N[1],nz=N[2];
7

8 mesh Thx = square(ny,nz,[y0+(y1-y0)*x,z0+(z1-z0)*y]);


9 mesh Thy = square(nx,nz,[x0+(x1-x0)*x,z0+(z1-z0)*y]);
10 mesh Thz = square(nx,ny,[x0+(x1-x0)*x,y0+(y1-y0)*y]);
11

12 int[int] refx=[0,L(0,0)],refX=[0,L(0,1)]; // Xmin, Ymax faces labels renumbering


13 int[int] refy=[0,L(1,0)],refY=[0,L(1,1)]; // Ymin, Ymax faces labesl renumbering
14 int[int] refz=[0,L(2,0)],refZ=[0,L(2,1)]; // Zmin, Zmax faces labels renumbering
15

16 meshS Thx0 = movemesh23(Thx,transfo=[x0,x,y],orientation=-orientation,label=refx);


17 meshS Thx1 = movemesh23(Thx,transfo=[x1,x,y],orientation=+orientation,label=refX);
18 meshS Thy0 = movemesh23(Thy,transfo=[x,y0,y],orientation=+orientation,label=refy);
19 meshS Thy1 = movemesh23(Thy,transfo=[x,y1,y],orientation=-orientation,label=refY);
20 meshS Thz0 = movemesh23(Thz,transfo=[x,y,z0],orientation=-orientation,label=refz);
21 meshS Thz1 = movemesh23(Thz,transfo=[x,y,z1],orientation=+orientation,label=refZ);
22 meshS Th= Thx0+Thx1+Thy0+Thy1+Thz0+Thz1;
23

24 return Th;
25 }
26

27 func meshS Ellipsoide (real RX,real RY,real RZ,real h,int L,real Ox,real Oy,real Oz,int␣
˓→orientation) {
28 mesh Th=square(10,20,[x*pi-pi/2,2*y*pi]); // $]\frac{-pi}{2},frac{-pi}{2}[\
˓→times]0,2\pi[ $
(continues on next page)

150 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


29 // a parametrization of a sphere
30 func f1 =RX*cos(x)*cos(y);
31 func f2 =RY*cos(x)*sin(y);
32 func f3 =RZ*sin(x);
33 // partiel derivative
34 func f1x= -RX*sin(x)*cos(y);
35 func f1y= -RX*cos(x)*sin(y);
36 func f2x= -RY*sin(x)*sin(y);
37 func f2y= +RY*cos(x)*cos(y);
38 func f3x=-RZ*cos(x);
39 func f3y=0;
40 // the metric on the sphere $ M = DF^t DF $
41 func m11=f1x^2+f2x^2+f3x^2;
42 func m21=f1x*f1y+f2x*f2y+f3x*f3y;
43 func m22=f1y^2+f2y^2+f3y^2;
44 func perio=[[4,y],[2,y],[1,x],[3,x]]; // to store the periodic condition
45 real hh=h;// hh mesh size on unite sphere
46 real vv= 1/square(hh);
47 Th=adaptmesh(Th,m11*vv,m21*vv,m22*vv,IsMetric=1,periodic=perio);
48 Th=adaptmesh(Th,m11*vv,m21*vv,m22*vv,IsMetric=1,periodic=perio);
49 Th=adaptmesh(Th,m11*vv,m21*vv,m22*vv,IsMetric=1,periodic=perio);
50 Th=adaptmesh(Th,m11*vv,m21*vv,m22*vv,IsMetric=1,periodic=perio);
51 int[int] ref=[0,L];
52 meshS ThS=movemesh23(Th,transfo=[f1,f2,f3],orientation=orientation,refface=ref);
53 ThS=mmgs(ThS,hmin=h,hmax=h,hgrad=2.);
54 return ThS;
55 }
56

57 func meshS Ellipsoide (real RX,real RY,real RZ,real h,int L,int orientation) {
58 return Ellipsoide (RX,RY,RZ,h,L,0.,0.,0.,orientation);
59 }
60 func meshS Sphere(real R,real h,int L,int orientation) {
61 return Ellipsoide(R,R,R,h,L,orientation);
62 }
63 func meshS Sphere(real R,real h,int L,real Ox,real Oy,real Oz,int orientation) {
64 return Ellipsoide(R,R,R,h,L,Ox,Oy,Oz,orientation);
65 }

2D mesh generators combined with movemesh23

FreeFEM ‘s meshes can be built by the composition of the movemesh23 command from a 2d mesh generation. The
operation is a projection of a 2d plane in R3 following the geometric transformation [ Φ1, Φ2, Φ3 ].

1 load "msh3"
2 real l = 3;
3 border a(t=-l,l){x=t; y=-l;label=1;};
4 border b(t=-l,l){x=l; y=t;label=1;};
5 border c(t=l,-l){x=t; y=l;label=1;};
6 border d(t=l,-l){x=-l; y=t;label=1;};
7 int n = 100;
8 border i(t=0,2*pi){x=1.1*cos(t);y=1.1*sin(t);label=5;};
(continues on next page)

3.2. Mesh Generation 151


FreeFEM Documentation, Release 4.8

(continued from previous page)


9 mesh th= buildmesh(a(n)+b(n)+c(n)+d(n)+i(-n));
10 meshS Th= movemesh23(th,transfo=[x,y,cos(x)^2+sin(y)^2]);

Remeshing

The command trunc

This operator allows to define a meshS by truncating another one, i.e. by removing triangles, and/or by splitting each
triangle by a given positive integer s. In a FreeFEM script, this function must be called as follows:
meshS TS2= trunc (TS1, boolean function to keep or remove elements, split = s, label = . . . )
The command has the following arguments:
• boolean function to keep or remove elements
• split= sets the level n of triangle splitting. each triangle is splitted in n × n ( one by default)
• label= sets the label number of new boundary item (1 by default)
An example of how to call the function

1 real R = 3, r=1;
2 real h = 0.2; //
3 int nx = R*2*pi/h;
4 int ny = r*2*pi/h;
5 func torex= (R+r*cos(y*pi*2))*cos(x*pi*2);
6 func torey= (R+r*cos(y*pi*2))*sin(x*pi*2);
7 func torez= r*sin(y*pi*2);
8 // build a tore
9 meshS ThS=square3(nx,ny,[torex,torey,torez]) ;
10 ThS=trunc(ThS, (x < 0.5) | (y < 0.5) | (z > 1.), split=4);

The command movemesh

Like 2d and 3d type meshes in FreeFEM, meshS can be translated, rotated or deformated by an application [Φ1, Φ2,
Φ3]. The image 𝑇ℎ (Ω) is obtained by the command movemeshS.
The parameters of movemeshS are:
• transfo= sets the geometric transformation Φ(𝑥, 𝑦) = (Φ1(𝑥, 𝑦, 𝑧), Φ2(𝑥, 𝑦, 𝑧), Φ3(𝑥, 𝑦, 𝑧))
• region= sets the integer labels of the triangles. 0 by default.
• label= sets the labels of the border edges. This parameter is initialized as the label for the keyword change.
• edgemerge= An integer expression. When you transform a mesh, some triangles can be merged and fix the
parameter to 1, else 0 By default, this parameter is equal to 1.
• ptmerge = A real expression. When you transform a mesh, some points can be merged. This parameter is the
criteria to define two merging points. By default, we use

𝑝𝑡𝑚𝑒𝑟𝑔𝑒 = 1𝑒 − 7 𝑉 𝑜𝑙(𝐵),

152 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

where 𝐵 is the smallest axis parallel boxes containing the discretion domain of Ω and 𝑉 𝑜𝑙(𝐵) is
the volume of this box.
• orientation = An integer expression equal 1, give the oientation of the triangulation, elements must be in
the reference orientation (counter clock wise) equal -1 reverse the orientation of the triangles. It’s the global
orientation of the normals at the surface 1 extern (-1 intern)
Example of using

1 meshS Th1 = square3(n,n,[2*x,y,1],orientation=-1);


2 meshS Th2=movemeshS(Th1, transfo=[x,y,z]);
3 meshS Th3=movemesh(Th1, [x,y,z]);

The command change

Equivalent for a 2d or 3d mesh, the command change changes the label of elements and border elements of a meshS.
The parameters for this command line are:
• reftri= is a vector of integer that contains successive pairs of the old label number to the new label number for
elements.
• refedge= is a vector of integer that contains successive pairs of the old region number to new region number
for boundary elements.
• flabel= is an integer function given the new value of the label.
• fregion= is an integer function given the new value of the region.
• rmledges= is a vector of integer, where edge’s label given are remove of the mesh
These vectors are composed of 𝑛𝑙 successive pairs of numbers 𝑂, 𝑁 where 𝑛𝑙 is the number (label or region) that we
want to change. For example, we have:

label = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]


region = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]

Link with a mesh3

In topology and mathematics, the boundary of a subset S of a topological space X is the set of points which can be
approached both from S and from the outside of S. The general definitions to the boundary of a subset S of a topological
space X are:
• the closure of S without the interior of S 𝜕𝑆 = 𝑆∖𝑆.
˚

• the intersection of the closure of S with the closure of its complement 𝜕𝑆 = 𝑆 ∩ (𝑋∖𝑆).
• the set of points p of X such that every neighborhood of p contains at least one point of S and at least one point
not of S.
More concretely in FreeFEM, the gestion of a 3D mesh is as follows. Let be Ω a subset of R3 and 𝜕Ω is boundary, the
finite element discretization Ωℎ of this domain gives:
• a mesh3 type, denotes Th3, meshing the volume domain. It contains all the nodes, the tetrahedrons Ω𝑖 such as
Ωℎ = ∪𝑖 Ω𝑖 and the list of triangles describing the boundary domain
• a meshS type, denotes ThS, meshing the boundary of the volume domain. Typically, containing the nodes be-
longing to the boundary of Th3 and, if it exists the boundary triangles and the edges.
Remark: Condition of meshS existence | In FreeFEM, a meshS can be defined in 2 cases such as:

3.2. Mesh Generation 153


FreeFEM Documentation, Release 4.8

• Th3 ⊂ ThS where it exactly describes the bounder of Th3.


• a mehS is an explicite surface mesh given by a list of vertices, triangle finite elements and boundary edge elements
(can be optional follows the geometry domain)

Note: Hence, if an input mesh (.msh freefem or .mesh format) contains a list of vertices, tetrahedra, triangles and
edges, FreeFEM builds a mesh3 whitch contains explicitly a surface mesh type meshS.

The command Gamma

The command Gamma allows to build and manipulate the border mesh independly of a volume mesh such as the surface
is described by triangle elements and edges border elements in 3d. Use this function, suppose that the mesh3 object
even contains the geometric description of its surface. That means, the input mesh explicitly contains the list of vertices,
tetrahedra, triangles and edges. In case where the surface mesh doesn’t exist, before calling Gamma, must build it by
calling the buildSurface function (see the next function description).

1 load "msh3"
2 int n= 10;
3 int nvb = (n+1)^3 - (n-1)^3;// Nb boundary vertices
4 int ntb = n*n*12; // Nb of Boundary triangle
5 mesh3 Th=cube(n,n,n);
6 Th = buildBdMesh(Th); // build the surface mesh
7 // build Th1, the surface of Th, defined by triangles elements and edges border␣
˓→elements list

8 meshS Th1 = Th.Gamma;

The command buildBdMesh

Let Th3 a volume mesh (mesh3 type) ; such as the geometry description is a list of vertices, tetrahedra elements
and triangle border elements. FreeFEM can generate the surface mesh associated to Th3. The intern mechanism of
FreeFEM created directly the meshS associated to Th3 and accessible by the command meshS ThS = Th3.Gamma;.

The command savesurfacemesh

Available for 3d meshes, the command savesurfacemesh saves the entire surface of a 3d volume mesh3 at the format
.mesh. Two possibilies about the mesh3 surface:
• the geometric surface isn’t explicite, that means the mesh3 doesn’t contain surface elements (triangles) and border
surface elements (edge). The surface is defined by the border of the volume. Hence, savesurfacemesh returns
the list of vertices and faces of the volume mesh, according to a local numbring at the border mesh.
• the geometric surface is explicite and known by the mesh3 type. This may be due to the nature of the data
mesh (list of vertices, tetrahedra, triangles, edges) or a surface building by FreeFEM with the calling of
buildSurface operator. In this case, savesurfacemesh allows to save the real geometry of the surface 3d
mesh (list of vertices, triangles, edges)
Example of use

1 load "msh3"
2 mesh3 Th3=cube(10,15,5);
3 savemesh(Th3, "surf.mesh");
(continues on next page)

154 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


4 savesurfacemesh(Th3, "surfreal.mesh");
5 mesh3 ThS3 = trunc(Th3, 1, split=3);
6 meshS ThSS = ThS3.Gamma;
7 savesurfacemesh(ThS3, "surfacesplit.mesh");
8 savemesh(ThSS,"GammaSplit.mesh" );

volume mesh and meshS=NULL


savesurfmesh(Th,filename_mesh) write in the file the vertices list and the triangle list (face of the volum mesh) accord-
ing to a numbering in local surface
savesurfmesh(Th,filename_points,filename_faces) The operation does the same thing that the first exept to

Glue of meshS meshes

A surface 3d mesh can be the result of the generation of several assembled meshes, with caution of the right orientation
at the merged interfaces.

1 meshS Th1 = square3(n,n,[2*x,y,1],orientation=-1);


2 meshS Th2 = square3(n,n,[2*x,y,0],orientation=1);
3 meshS Th11 = square3(n,n,[2*x,1,y],orientation=1);
4 meshS Th22 = square3(n,n,[2*x,0,y],orientation=-1);
5 meshS Th5 = square3(n,n,[1,y,x]);
6 meshS Th6 = square3(n,n,[2,y,x],orientation=1);
7 meshS Th = Th1+Th2+Th11+Th22+Th5+Th6;
8 assert(Th.nbnomanifold==40);

Warning: For the moment, the case of no manifold mesh are not considered in FreeFEM. To check if the meshS
contains no manifold elements, the command nbnomanifold.

3.2.4 The type meshL in 3 dimension

Commands for 3d curve mesh generation

The command segment

The function segment is a basic command to define a curve in 3D space.


The parameters of this command line are:
• n generates a n subsegments from the unit line
• [.,.,.] is [ Φ1, Φ2, Φ3 ] is the geometric transformation from R1 to R3 . By default, [ Φ1, Φ2,
Φ3 ] = [x,0,0]
• orientation= equal 1, gives the orientation of the triangulation, elements are in the reference orien-
tation (counter clock wise) equal -1 reverse the orientation of the triangles it’s the global orientation
of the surface 1 extern (-1 intern)
• cleanmesh= is a boolean, allowing remove the duplicated nodes
• removeduplicate= is a boolean, allowing remove the duplicated elements and border elements

3.2. Mesh Generation 155


FreeFEM Documentation, Release 4.8

• precismesh this parameter is the criteria to define two merging points. By default, it value is
1e-7 and define the smallest axis parallel boxes containing the discretion domain of Ω
By defaut, the border points are marked by label 1 and 2.

1 real R = 3, r=1;
2 real h = 0.1; //
3 int nx = R*2*pi/h;
4 func torex= (R+r*cos(y*pi*2))*cos(x*pi*2);
5 func torey= (R+r*cos(y*pi*2))*sin(x*pi*2);
6 func torez= r*sin(y*pi*2);
7 meshL Th=segment(nx,[torex,torey,torez],removeduplicate=true) ;

The following code generates a 10 subsegments from the unit line with a clock wise triangulation, according to the
geometric transformation [torex,torey,torez] and removing the duplicated points/elements

The command buildmesh

This operator allows to define a curve mesh from multi-borders. The domain can be defined by a parametrized curve
(keyword border), such as Th1 in the following example or piecewise by parametrized curves, such as the construction
of the mesh Th2.
The pieces can only intersect at their endpoints, but it is possible to join more than two endpoints.

1 load "msh3"
2

3 // conical helix
4 border E1(t=0, 10.*pi){x=(1.)*t*cos(t); y=-(1.)*t*sin(t); z=t;}
5 meshL Th1=buildmeshL(E1(1000));
6

7 int upper = 1, others = 2, inner = 3, n = 10;


8 border D01(t=0, 1) {x=0; y=-1+t; }
9 border D02(t=0, 1){x=1.5-1.5*t; y=-1; z=3;label=upper;}
10 border D03(t=0, 1){x=1.5; y=-t; z=3;label=upper;}
11 border D04(t=0, 1){x=1+0.5*t; y=0; z=3;label=others;}
12 border D05(t=0, 1){x=0.5+0.5*t; y=0; z=3;label=others;}
13 border D06(t=0, 1){x=0.5*t; y=0; z=3;label=others;}
14 border D11(t=0, 1){x=0.5; y=-0.5*t; z=3;label=inner;}
15 border D12(t=0, 1){x=0.5+0.5*t; y=-0.5; z=3;label=inner;}
16 border D13(t=0, 1){x=1; y=-0.5+0.5*t; z=3;label=inner;}
17

18 meshL Th2=buildmeshL(D01(-n) + D02(-n) + D03(-n) + D04(-n) + D05(-n)


19 + D06(-n) + D11(n) + D12(n) + D13(n));

156 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Remeshing

The command trunc

This operator allows to define a meshL by truncating another one, i.e. by removing segments, and/or by splitting each
element by a given positive integer s. Here, an example to use this function:
meshL ThL2= trunc (ThL1, boolean function to keep or remove elements, split = s, label = . . . )
The command has the following arguments:
• boolean function to keep or remove elements
• split= sets the level n of edge splitting, each edge is splitted in n subpart( one by default)
• label= sets the label number of new boundary item (1 by default)
• new2old
• old2new
• renum
• orientation= equal 1, gives the orientation of the triangulation, elements are in the reference orientation
(counter clock wise) equal -1 reverse the orientation of the triangles it’s the global orientation of the surface
1 extern (-1 intern)
• cleanmesh= is a boolean, allowing remove the duplicated nodes
• removeduplicate= is a boolean, allowing remove the duplicated elements and border elements
• precismesh this parameter is the criteria to define two merging points. By default, it value is 1e-7 and de-
fine the smallest axis parallel boxes containing the discretion domain of Ω
An example of how to call this function

1 int nx=10;
2 meshL Th=segment(nx,[5.*x,cos(pi*x),sin(pi*x)]);
3 Th=trunc(Th, (x < 0.5) | (y < 0.5) | (z > 1.), split=4);

The command movemesh

This is the classical mesh transformation FreeFEM function, meshL can be deformed by an application [ Φ1, Φ2, Φ3
]. The image 𝑇ℎ (Ω) is obtained by the command movemeshL.
The parameters of movemesh are:
• transfo= sets the geometric transformation Φ(𝑥, 𝑦) = (Φ1(𝑥, 𝑦, 𝑧), Φ2(𝑥, 𝑦, 𝑧), Φ3(𝑥, 𝑦, 𝑧))
• refedge= sets the integer labels of the triangles. 0 by default.
• refpoint= sets the labels of the border points. This parameter is initialized as the label for the keyword
change.
• precismesh this parameter is the criteria to define two merging points. By default, it value is 1e-7 and de-
fine the smallest axis parallel boxes containing the discretion domain of Ω
• orientation = An integer expression equal 1, give the oientation of the triangulation, elements must be in
the reference orientation (counter clock wise) equal -1 reverse the orientation of the triangles. It’s the global
orientation of the normals at the surface 1 extern (-1 intern)
• cleanmesh= is a boolean, allowing remove the duplicated nodes

3.2. Mesh Generation 157


FreeFEM Documentation, Release 4.8

• removeduplicate= is a boolean, allowing remove the duplicated elements and border elements

Note: The definition of the geometric transformation depends on the space dimension of the studied problem. It means
that, with curve FEM, it’s possible to treat a real 1D problem (space coordinate is x) then the transformation is given
by x: ->F(x), that means [F_x] and F_y=F_z=0 in FreeFEM function.

Example of using

1 int nx=100;
2 meshL Th=Sline(nx);
3 meshL Th31=movemesh(Th, [x]);
4 meshL Th32=movemesh(Th, [x,-x*(x-1)]);
5 meshL Th3=Th31+Th32;

The command change

The command change changes the label of elements and border elements of a meshL.
The parameters for this command line are:
• refedge= is a vector of integer that contains successive pairs of the old label number to the new label number
for elements.
• refpoint= is a vector of integer that contains successive pairs of the old region number to new region number
for boundary elements.
• flabel= is an integer function given the new value of the label.
• fregion= is an integer function given the new value of the region.
• rmlpoint= is a vector of integer, where edge’s label given are remove of the mesh
These vectors are composed of 𝑛𝑙 successive pairs of numbers 𝑂, 𝑁 where 𝑛𝑙 is the number (label or region) that we
want to change. For example, we have:

label = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]


region = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]

The commands buildBdMesh and Gamma

The command Gamma allows to extract the border mesh independly of a surface mesh. With this function, the con-
structed border mesh contains the full geometric description of th eboundary surface. In case where the border mesh
doesn’t exist, before calling Gamma, must build it by calling the buildBdMesh function (see the next function descrip-
tion).

1 load "msh3"
2 int n= 10;
3 meshS Th=square3(n,n);
4 Th = buildBdMesh(Th); // build the border mesh
5 // build Th1, the border of Th, defined by edges elements and point border elements
6 meshL Th1 = Th.Gamma;

158 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Glue of meshL meshes

An assembling of meshL is possible thanks to the operator +. The result returns a meshL, with caution of the right
orientation at the merged interfaces. Here, the function checkmesh can be called.

1 int n=10;
2 meshL Th1 = segment(n);
3 meshL Th2 = segment(n,[0,x,0],orientation=1);
4 meshL Th3 = segment(n,[x,0,1],orientation=1);
5 meshL Th4 = segment(n,[0,0,x],orientation=-1);
6

7 meshL Th = Th1+Th2+Th3+Th4;
8 Th=rebuildBorder(Th, ridgeangledetection=pi/2.+0.0001);

Warning: For the moment, the case of no manifold mesh are not considered in FreeFEM. To check if the meshL
contains no manifold elements, the command nbnomanifold.

The command extract

This operator allows to extract a labeled piece or the entire border of a 2D mesh and project it in 3D. Optionally, a
geometic transformation can be applied.

1 mesh Th=square(10,10);
2 int[int] ll=[4];
3 meshL ThL = extract(Th,[x+2,y*5],refedge=ll);

The commands rebuildBorder

This operator, used in the last example, allows to reconstruted the border elements following a special criteria
ridgeangledetection. By default, it value is 98 * 𝑎𝑟𝑐𝑡𝑎𝑛(1) ≈ 40, the diedral angle for a decahedron.

The commands checkmesh

This function is avalaible for all 3D meshes. It checkes and validates the a given mesh, allows to remove duplicate
vertices and/or elements and border elements. The possible arguments are
• precismesh= this parameter is the criteria to define two merging points. By default, it value is 1e-7 and
define the smallest axis parallel boxes containing the discretion domain of Ω
• removeduplicate= is a boolean, allowing remove the duplicated elements and border elements
• rebuildboundary= is a boolean, allowing rebuild the border elements (in case of incomplete list given by the
mesh)
Example:

1 mesh3 Th = checkmesh(Th);

3.2. Mesh Generation 159


FreeFEM Documentation, Release 4.8

TetGen: A tetrahedral mesh generator

TetGen is a software developed by Dr. Hang Si of Weierstrass Institute for Applied Analysis and Stochastics in Berlin,
Germany [HANG2006]. TetGen is free for research and non-commercial use. For any commercial license utilization,
a commercial license is available upon request to Hang Si.
This software is a tetrahedral mesh generator of a three dimensional domain defined by its boundary (a surface). The
input domain takes into account a polyhedral or a piecewise linear complex. This tetrahedralization is a constrained
Delaunay tetrahedralization.
The method used in TetGen to control the quality of the mesh is a Delaunay refinement due to Shewchuk
[SHEWCHUK1998]. The quality measure of this algorithm is the Radius-Edge Ratio (see Section 1.3.1 [HANG2006]
for more details). A theoretical bound of this ratio of the Shewchuk algorithm is obtained for a given complex of
vertices, constrained segments and facets of surface mesh, with no input angle less than 90 degrees. This theoretical
bound is 2.0.
The launch of TetGen is done with the keyword tetg. The parameters of this command line is:
• reftet= sets the label of tetrahedra.
• label= is a vector of integers that contains the old labels number at index 2𝑖 and the new labels number at index 2𝑖 + 1 of
This parameter is initialized as a label for the keyword change.
• switch= A string expression. This string corresponds to the command line switch of TetGen see Section 3.2
of [HANG2006].
• nbofholes= Number of holes (default value: “size of holelist / 3”).
• holelist= This array corresponds to holelist of TetGenio data structure [HANG2006]. A real vector
of size 3 * nbofholes. In TetGen, each hole is associated with a point inside this domain. This vec-
tor is 𝑥ℎ1 , 𝑦1ℎ , 𝑧1ℎ , 𝑥ℎ2 , 𝑦2ℎ , 𝑧2ℎ , · · · , where 𝑥ℎ𝑖 , 𝑦𝑖ℎ , 𝑧𝑖ℎ is the associated point with the 𝑖th hole.
• nbofregions= Number of regions (default value: “size of regionlist / 5”).
• regionlist= This array corresponds to regionlist of TetGenio data structure [HANG2006].
The attribute and the volume constraint of region are given in this real vector of size 5 * nbofregions. The
𝑖th region is described by five elements: 𝑥−coordinate, 𝑦−coordinate and 𝑧−coordinate of a point inside this
domain (𝑥𝑖 , 𝑦𝑖 , 𝑧𝑖 ); the attribute (𝑎𝑡𝑖 ) and the maximum volume for tetrahedra (𝑚𝑣𝑜𝑙𝑖 ) for this region.
The regionlist vector is: 𝑥1 , 𝑦1 , 𝑧1 , 𝑎𝑡1 , 𝑚𝑣𝑜𝑙1 , 𝑥2 , 𝑦2 , 𝑧2 , 𝑎𝑡2 , 𝑚𝑣𝑜𝑙2 , · · ·.
• nboffacetcl= Number of facets constraints “size of facetcl / 2”).
• facetcl= This array corresponds to facetconstraintlist of TetGenio data structure [HANG2006].
The 𝑖𝑡ℎ facet constraint is defined by the facet marker 𝑅𝑒𝑓𝑖𝑓 𝑐 and the maximum area for faces 𝑚𝑎𝑟𝑒𝑎𝑓𝑖 𝑐 . The
facetcl array is: 𝑅𝑒𝑓1𝑓 𝑐 , 𝑚𝑎𝑟𝑒𝑎𝑓1 𝑐 , 𝑅𝑒𝑓2𝑓 𝑐 , 𝑚𝑎𝑟𝑒𝑎𝑓2 𝑐 , · · ·.
This parameters has no effect if switch q is not selected.
Principal switch parameters in TetGen:
• p Tetrahedralization of boundary.
• q Quality mesh generation. The bound of Radius-Edge Ratio will be given after the option q. By default, this
value is 2.0.
• a Constructs with the volume constraints on tetrahedra. These volumes constraints are defined with the
bound of the previous switch q or in the parameter regionlist.
• A Attributes reference to region given in the regionlist. The other regions have label 0.
The option AA gives a different label at each region. This switch works with the option p. If option r is used,
this switch has no effect.

160 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• r Reconstructs and Refines a previously generated mesh. This character is only used with the command line
tetgreconstruction.
• Y This switch preserves the mesh on the exterior boundary.
This switch must be used to ensure a conformal mesh between two adjacent meshes.
• YY This switch preserves the mesh on the exterior and interior boundary.
• C The consistency of the result’s mesh is testing by TetGen.
• CC The consistency of the result’s mesh is testing by TetGen and also constrained checks of Delaunay mesh (if
p switch is selected) or the consistency of Conformal Delaunay (if q switch is selected).
• V Give information of the work of TetGen. More information can be obtained in specified VV or VVV.
• Q Quiet: No terminal output except errors
• M The coplanar facets are not merging.
• T Sets a tolerance for coplanar test. The default value is 1𝑒 − 8.
• d Intersections of facets are detected.
To obtain a tetrahedral mesh with TetGen, we need the surface mesh of a three dimensional domain. We now give the
command line in FreeFEM to construct these meshes.
The keyword tetgtransfo
This keyword corresponds to a composition of command line tetg and movemesh23.

1 tetgtransfo(Th2, transfo=[Phi(1), Phi(2), Phi(3)]), ...) = tetg(Th3surf, ...),

where Th3surf = movemesh23(Th2, transfo=[Phi(1), Phi(2), Phi(3)]) and Th2 is the input two dimen-
sional mesh of tetgtransfo.
The parameters of this command line are, on one hand, the parameters label, switch, regionlist, nboffacetcl,
facetcl of keyword tetg and on the other hand, the parameter ptmerge of keyword movemesh23.

Note: To use tetgtransfo, the result’s mesh of movemesh23 must be a closed surface and define one region only.
Therefore, the parameter regionlist is defined for one region.
An example of this keyword can be found in line 61 of the Build layer mesh example.

The keyword tetgconvexhull


FreeFEM, using TetGen, is able to build a tetrahedralization from a set of points. This tetrahedralization is a Delaunay
mesh of the convex hull of the set of points.
The coordinates of the points can be initialized in two ways. The first is a file that contains the coordinate of points
𝑋𝑖 = (𝑥𝑖 , 𝑦𝑖 , 𝑧𝑖 ). This file is organized as follows:

𝑛𝑣
𝑥1 𝑦1 𝑧1
𝑥2 𝑦2 𝑧2
.. .. ..
. . .
𝑥 𝑛𝑣 𝑦 𝑛𝑣 𝑧𝑛𝑣

The second way is to give three arrays that correspond respectively to the 𝑥−coordinates, 𝑦−coordinates and
𝑧−coordinates.
The parameters of this command line are :

3.2. Mesh Generation 161


FreeFEM Documentation, Release 4.8

• switch= A string expression. This string corresponds to the command line switch of TetGen see Section 3.2
of [HANG2006].
• reftet= An integer expression. Set the label of tetrahedra.
• label= An integer expression. Set the label of triangles.
In the string switch, we can’t used the option p and q of TetGen.

Reconstruct/Refine a 3d mesh with TetGen

Meshes in three dimension can be refined using TetGen with the command line tetgreconstruction.
The parameter of this keyword are
• region= an integer array that changes the region number of tetrahedra. This array is defined as the pa-
rameter reftet in the keyword change.
• label= an integer array that changes the label of boundary triangles. This array is defined as the parameter
label in the keyword change.
• sizeofvolume= a reel function. This function constraints the volume size of the tetrahedra in the domain (see
Isotrope mesh adaption section to build a 3d adapted mesh).
The parameters switch, nbofregions, regionlist, nboffacetcl and facetcl of the command line which call
TetGen (tetg) is used for tetgrefine.
In the parameter switch=, the character r should be used without the character p.
For instance, see the manual of TetGen [HANG2006] for effect of r to other character.
The parameter regionlist defines a new volume constraint in the region. The label in the regionlist will be the
previous label of region.
This parameter and nbofregions can’t be used with the parameter sizeofvolume.
**Example refinesphere.edp**

1 load "msh3"
2 load "tetgen"
3 load "medit"
4

5 mesh Th=square(10,20,[x*pi-pi/2,2*y*pi]); // $]\frac{-pi}{2},frac{-pi}{2}[\times]0,2\


˓→pi[ $

6 // a parametrization of a sphere
7 func f1 =cos(x)*cos(y);
8 func f2 =cos(x)*sin(y);
9 func f3 = sin(x);
10 // partiel derivative of the parametrization DF
11 func f1x=sin(x)*cos(y);
12 func f1y=-cos(x)*sin(y);
13 func f2x=-sin(x)*sin(y);
14 func f2y=cos(x)*cos(y);
15 func f3x=cos(x);
16 func f3y=0;
17 // $ M = DF^t DF $
18 func m11=f1x^2+f2x^2+f3x^2;
19 func m21=f1x*f1y+f2x*f2y+f3x*f3y;
20 func m22=f1y^2+f2y^2+f3y^2;
(continues on next page)

162 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


21

22 func perio=[[4,y],[2,y],[1,x],[3,x]];
23 real hh=0.1;
24 real vv= 1/square(hh);
25 verbosity=2;
26 Th=adaptmesh(Th,m11*vv,m21*vv,m22*vv,IsMetric=1,periodic=perio);
27 Th=adaptmesh(Th,m11*vv,m21*vv,m22*vv,IsMetric=1,periodic=perio);
28 plot(Th,wait=1);
29

30 verbosity=2;
31

32 // construction of the surface of spheres


33 real Rmin = 1.;
34 func f1min = Rmin*f1;
35 func f2min = Rmin*f2;
36 func f3min = Rmin*f3;
37

38 meshS ThS=movemesh23(Th,transfo=[f1min,f2min,f3min]);
39

40 real[int] domain = [0.,0.,0.,145,0.01];


41 mesh3 Th3sph=tetg(ThS,switch="paAAQYY",nbofregions=1,regionlist=domain);
42

43 int[int] newlabel = [145,18];


44 real[int] domainrefine = [0.,0.,0.,145,0.0001];
45 mesh3 Th3sphrefine=tetgreconstruction(Th3sph,switch="raAQ",region=newlabel,nbofregions=1,
˓→regionlist=domainrefine,sizeofvolume=0.0001);

46

47 int[int] newlabel2 = [145,53];


48 func fsize = 0.01/(( 1 + 5*sqrt( (x-0.5)^2+(y-0.5)^2+(z-0.5)^2) )^3);
49 mesh3 Th3sphrefine2=tetgreconstruction(Th3sph,switch="raAQ",region=newlabel2,
˓→sizeofvolume=fsize);

50

51 medit("sphere",Th3sph,wait=1);
52 medit("sphererefinedomain",wait=1,Th3sphrefine);
53 medit("sphererefinelocal",wait=1,Th3sphrefine2);
54

55 // FFCS: testing 3d plots


56 plot(Th3sph);
57 plot(Th3sphrefine);
58 plot(Th3sphrefine2);

3.2.5 Read/Write Statements for meshes

2d case

format of mesh data

Users who want to read a triangulation made elsewhere should see the structure of the file generated below:

3.2. Mesh Generation 163


FreeFEM Documentation, Release 4.8

1 border C(t=0, 2*pi){x=cos(t); y=sin(t);}


2 mesh Th1 = buildmesh(C(10));
3 savemesh(Th1, "mesh.msh");
4 mesh Th2=readmesh("mesh.msh");

The mesh is shown on Fig. 3.28.


The information about Th are saved in the file mesh.msh whose structure is shown on Table 3.1. An external file
contains a mesh at format .mesh can be read by the ommand readmesh(file_name).
There, 𝑛𝑣 denotes the number of vertices, 𝑛𝑡 the number of triangles and 𝑛𝑠 the number of edges on boundary.
For each vertex 𝑞 𝑖 , 𝑖 = 1, · · · , 𝑛𝑣 , denoted by (𝑞𝑥𝑖 , 𝑞𝑦𝑖 ) the 𝑥-coordinate and 𝑦-coordinate.
Each triangle 𝑇𝑘 , 𝑘 = 1, · · · , 𝑛𝑡 has three vertices 𝑞 𝑘1 , 𝑞 𝑘2 , 𝑞 𝑘3 that are oriented counter-clockwise.
The boundary consists of 10 lines 𝐿𝑖 , 𝑖 = 1, · · · , 10 whose end points are 𝑞 𝑖1 , 𝑞 𝑖2 .

Fig. 3.28: Mesh by buildmesh(C(10))

In the Fig. 3.28, we have the following.


𝑛𝑣 = 14, 𝑛𝑡 = 16, 𝑛𝑠 = 10
𝑞 1 = (−0.309016994375, 0.951056516295)
...
𝑞 14 = (−0.309016994375, −0.951056516295)
The vertices of 𝑇1 are 𝑞 9 , 𝑞 12 , 𝑞 10 .
...
The vertices of 𝑇16 are 𝑞 9 , 𝑞 10 , 𝑞 6 .
The edge of the 1st side 𝐿1 are 𝑞 6 , 𝑞 5 .
...
The edge of the 10th side 𝐿10 are 𝑞 10 , 𝑞 6 .

164 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Table 3.1: The structure of mesh_sample.msh


Content of the file Explanation
14 16 10 𝑛𝑣 𝑛𝑡 𝑛𝑒
-0.309016994375 0.951056516295 1 𝑞𝑥1 𝑞𝑦1 boundary label = 1
0.309016994375 0.951056516295 1 𝑞𝑥2 𝑞𝑦2 boundary label = 1
... ...
-0.309016994375 -0.951056516295 1 𝑞𝑥14 𝑞𝑦14 boundary label = 1
9 12 10 0 11 12 13 region label = 0
5960 21 22 23 region label = 0
... ...
9 10 6 0 161 162 163 region label = 0
651 11 12 boundary label = 1
521 21 22 boundary label = 1
... ...
10 6 1 101 102 boundary label = 1

In FreeFEM there are many mesh file formats available for communication with other tools such as emc2, modulef,
. . . (see Mesh format chapter ).
The extension of a file implies its format. More details can be found on the file format .msh in the article by F. Hecht
“bamg : a bidimensional anisotropic mesh generator” [HECHT1998_2].
A mesh file can be read into FreeFEM except that the names of the borders are lost and only their reference numbers
are kept. So these borders have to be referenced by the number which corresponds to their order of appearance in the
program, unless this number is overwritten by the keyword label. Here are some examples:

1 // Parameters
2 int n = 10;
3

4 // Mesh
5 border floor(t=0, 1){x=t; y=0; label=1;};
6 border right(t=0, 1){x=1; y=t; label=5;};
7 border ceiling(t=1, 0){x=t; y=1; label=5;};
8 border left(t=1, 0){x=0; y=t; label=5;};
9

10 mesh th = buildmesh(floor(n) + right(n) + ceiling(n) + left(n));


11

12 //save mesh in different formats


13 savemesh(th, "toto.am_fmt"); // format "formated Marrocco"
14 savemesh(th, "toto.Th"); // format database db mesh "bamg"
15 savemesh(th, "toto.msh"); // format freefem
16 savemesh(th, "toto.nopo"); // modulef format
17

18 // Fespace
19 fespace femp1(th, P1);
20 femp1 f = sin(x)*cos(y);
21 femp1 g;
22

23 //save the fespace function in a file


24 {
25 ofstream file("f.txt");
26 file << f[] << endl;
27 } //the file is automatically closed at the end of the block
(continues on next page)

3.2. Mesh Generation 165


FreeFEM Documentation, Release 4.8

(continued from previous page)


28 //read a file and put it in a fespace function
29 {
30 ifstream file("f.txt");
31 file >> g[] ;
32 }//the file is equally automatically closed
33

34 // Plot
35 plot(g);
36

37 // Mesh 2
38 //read the mesh for freefem format saved mesh
39 mesh th2 = readmesh("toto.msh");
40

41 // Fespace 2
42 fespace Vh2(th2, P1);
43 Vh2 u, v;
44

45 // Problem
46 //solve:
47 // $u + \Delta u = g$ in $\Omega $
48 // $u=0$ on $\Gamma_1$
49 // $\frac{\partial u }{\partial n} = g$ on $\Gamma_2$
50 solve Problem(u, v)
51 = int2d(th2)(
52 u*v
53 - dx(u)*dx(v)
54 - dy(u)*dy(v)
55 )
56 + int2d(th2)(
57 - g*v
58 )
59 + int1d(th2, 5)(
60 g*v
61 )
62 + on(1, u=0)
63 ;
64

65 // Plot
66 plot(th2, u);

Input/output for a mesh

• the command readmesh


The function readmesh allows to build a mesh from a data file

1 mesh Th=readmeshS("Th.mesh");
2 mesh Thff = readmesh("Thff.msh"); // FreeFEM format

• the command savemesh


The function savemesh allows to export a mesh

166 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

1 savemesh(Th,"Th.mesh")
2 savemesh(Thff,"Thff.msh") // FreeFEM format
3

4 savemesh(th, "toto.msh"); // format freefem savemesh(th, "toto.am_fmt"); // format


˓→"formated Marrocco"

5 savemesh(th, "toto.Th"); // format database db mesh "bamg"


6 savemesh(th, "toto.nopo"); // modulef format
7 // allows to save the 2d mesh with the 3rd space coordinate as a scalar solution␣
˓→for visualise

8 savemesh(Th,"mm",[x,y,u]); // save surface mesh for medit, see for example minimal-
˓→surf.edp

9 exec("medit mm;rm mm.bb mm.faces mm.points");

• the command vtkloadS


The function vtkload allows to build a mesh from a data mesh at vtk format mesh

1 load "iovtk"
2 mesh Th=vtkloadS("mymesh.vtk");

• the command savevtk


The function savevtk allows to export a mesh to a data mesh at vtk format mesh

1 load "iovtk"
2 savevtk("Th.vtk",Th);

• the command gmshload


The function gmshloadS allows to build a mesh from a data mesh file at formatmsh (GMSH)

1 load "gmsh"
2 mesh Th=gmshload("mymesh.msh");

• the command savegmsh


The function savegmsh allows to export a mesh to a data mesh msh (GMSH)

1 load "gmsh"
2 savegmsh(Th, "Th");

3d case

format of mesh data

In three dimensions, the file mesh format supported for input and output files by FreeFEM are the extension .msh and
.mesh. These formats are described in the Mesh Format section.

3.2. Mesh Generation 167


FreeFEM Documentation, Release 4.8

Extension file .msh The structure of the files with extension .msh in 3D is given by:

𝑛𝑣 𝑛𝑡𝑒𝑡 𝑛𝑡𝑟𝑖
𝑞𝑥1 𝑞𝑦1 𝑞𝑧1 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
𝑞𝑥2 𝑞𝑦2 𝑞𝑧2 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
.. .. .. ..
. . . .
𝑞𝑥𝑛𝑣 𝑞𝑦𝑛𝑣 𝑞𝑧𝑛𝑣 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
11 12 13 14 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
21 22 23 24 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
.. .. .. .. ..
. . . . .
(𝑛𝑡𝑒𝑡 )1 (𝑛𝑡𝑒𝑡 )2 (𝑛𝑡𝑒𝑡 )3 (𝑛𝑡𝑒𝑡 )4 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
11 12 13 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
21 22 23 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
.. .. .. ..
. . . .
(𝑛𝑡 𝑟𝑖)1 (𝑛𝑡𝑟𝑖 )2 (𝑛𝑡𝑟𝑖 )3 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙

In this structure, 𝑛𝑣 denotes the number of vertices, 𝑛𝑡𝑒𝑡 the number of tetrahedra and 𝑛𝑡𝑟𝑖 the number of triangles.
For each vertex 𝑞 𝑖 , 𝑖 = 1, · · · , 𝑛𝑣 , we denote by (𝑞𝑥𝑖 , 𝑞𝑦𝑖 , 𝑞𝑧𝑖 ) the 𝑥-coordinate, the 𝑦-coordinate and the 𝑧-coordinate.
Each tetrahedra 𝑇𝑘 , 𝑘 = 1, · · · , 𝑛𝑡𝑒𝑡 has four vertices 𝑞 𝑘1 , 𝑞 𝑘2 , 𝑞 𝑘3 , 𝑞 𝑘4 .
The boundary consists of a union of triangles. Each triangle 𝑡𝑟𝑖𝑗 , 𝑗 = 1, · · · , 𝑛𝑡𝑟𝑖 has three vertices 𝑞 𝑗1 , 𝑞 𝑗2 , 𝑞 𝑗3 .
extension file .mesh The data structure for a three dimensional mesh is composed of the data structure presented in
Mesh Format section and a data structure for the tetrahedra. The tetrahedra of a three dimensional mesh are referred
using the following field:

1 Tetrahedra
2 NbTetrahedra
3 Vertex1 Vertex2 Vertex3 Vertex4 Label
4 ...
5 Vertex1 Vertex2 Vertex3 Vertex4 Label
6 Triangles
7 NbTriangles
8 Vertex1 Vertex2 Vertex3 Label
9 ...
10 Vertex1 Vertex2 Vertex3 Label

This field is express with the notation of Mesh Format section.

Input/output for a mesh3

• the command readmesh3


The function readmesh3 allows to build a mesh3 from a data file

1 mesh3 Th3=readmesh3("Th3.mesh");
2 mesh3 Th3ff = readmesh3("Th3ff.msh"); // FreeFEM format

• the command savemesh


The function savemesh allows to export a mesh3

168 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

1 savemesh(Th3,"Th3.mesh")
2 savemesh(Th3ff,"Th3ff.msh") // FreeFEM format

• the command vtkload3


The function vtkload3 allows to build a mesh3 from a data mesh at vtk format mesh

1 load "iovtk"
2 mesh3 Th3=vtkloadS("mymesh.vtk");

• the command savevtk


The function savevtk allows to export a mesh3 to a data mesh at vtk format mesh

1 load "iovtk"
2 savevtk("Th3.vtk",Th3);

• the command gmshload3


The function gmshload3 allows to build a mesh3 from a data mesh file at formatmsh (GMSH)

1 load "gmsh"
2 mesh3 Th3=gmshload3("mymesh.msh");

• the command savegmsh


The function savegmsh allows to export a mesh3 to a data mesh msh (GMSH)

1 load "gmsh"
2 savegmsh(Th3, "Th3");

Surface 3d case

format of mesh data

Like 2d and 3d, the input and output format files supported by FreeFEM are the extension .msh and .mesh. These
formats are described in the Mesh Format section.
Extension file .msh The structure of the files with extension .msh in surface 3D is given by:

𝑛𝑣 𝑛𝑡𝑟𝑖 𝑛𝑒𝑑𝑔𝑒𝑠
𝑞𝑥1 𝑞𝑦1 𝑞𝑧1 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
𝑞𝑥2 𝑞𝑦2 𝑞𝑧2 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
.. .. .. ..
. . . .
𝑞𝑥𝑛𝑣 𝑞𝑦𝑛𝑣 𝑞𝑧𝑛𝑣 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
11 12 13 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
21 22 23 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
.. .. .. ..
. . . .
(𝑛𝑡𝑟𝑖 )1 (𝑛𝑡𝑟𝑖 )2 (𝑛𝑡𝑟𝑖 )3 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
11 12 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
21 22 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
.. .. ..
. . .
(𝑛𝑒 𝑑𝑔𝑒)1 (𝑛𝑒𝑑𝑔𝑒 )2 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙

3.2. Mesh Generation 169


FreeFEM Documentation, Release 4.8

In this structure, 𝑛𝑣 denotes the number of vertices, 𝑛𝑡𝑒𝑡 the number of tetrahedra and 𝑛𝑡𝑟𝑖 the number of triangles.
For each vertex 𝑞 𝑖 , 𝑖 = 1, · · · , 𝑛𝑣 , we denote by (𝑞𝑥𝑖 , 𝑞𝑦𝑖 , 𝑞𝑧𝑖 ) the 𝑥-coordinate, the 𝑦-coordinate and the 𝑧-coordinate.
Each tetrahedra 𝑇𝑘 , 𝑘 = 1, · · · , 𝑛𝑡𝑒𝑡 has four vertices 𝑞 𝑘1 , 𝑞 𝑘2 , 𝑞 𝑘3 , 𝑞 𝑘4 .
The boundary consists of a union of triangles. Each triangle 𝑏𝑒𝑗 , 𝑗 = 1, · · · , 𝑛𝑡𝑟𝑖 has three vertices 𝑞 𝑗1 , 𝑞 𝑗2 , 𝑞 𝑗3 .
extension file .mesh The data structure for a three dimensional mesh is composed of the data structure presented in
Mesh Format section and a data structure for the tetrahedra. The tetrahedra of a three dimensional mesh are referred
using the following field:

1 MeshVersionFormatted 2
2 Dimension 3
3

4 Vertices
5 NbVertices
6 (v0)x (v0)y (v0)z
7 ...
8 (vn)x (vn)y (vn)z
9

10 Triangles
11 NbTriangles
12 Vertex1 Vertex2 Vertex3 Label
13 ...
14 Vertex1 Vertex2 Vertex3 Label
15

16 Edges
17 NbEdges
18 Vertex1 Vertex2 Label
19 ...
20 Vertex1 Vertex2 Label
21

22 End

This field is express with the notation of Mesh Format section.

Input/output for a meshS

• the command readmesh3


The function readmeshS allows to build a meshS from a data file

1 meshS ThS=readmeshS("ThS.mesh");
2 meshS Th3ff = readmeshS("ThSff.msh"); // FreeFEM format

• the command savemesh


The function savemesh allows to export a meshS

1 savemesh(ThS,"ThS.mesh")
2 savemesh(ThSff,"ThSff.msh") // FreeFEM format

• the command vtkloadS


The function vtkloadS allows to build a meshS from a data mesh at vtk format mesh

170 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

1 load "iovtk"
2 meshS ThS=vtkloadS("mymesh.vtk");

• the command savevtk


The function savevtk allows to export a meshS to a data mesh at vtk format mesh

1 load "iovtk"
2 savevtk("ThS.vtk",ThS);

• the command gmshloadS


The function gmshloadS allows to build a meshS from a data mesh file at formatmsh (GMSH)

1 load "gmsh"
2 meshS ThS=gmshloadS("mymesh.msh");

• the command savegmsh


The function savegmsh allows to export a meshS to a data mesh msh (GMSH)

1 load "gmsh"
2 savegmsh(ThS, "ThS");

3.2.6 Medit

The keyword medit allows to display a mesh alone or a mesh and one or several functions defined on the mesh using
the Pascal Frey’s freeware medit. medit opens its own window and uses OpenGL extensively. Naturally to use this
command medit must be installed.
A vizualisation with medit of scalar solutions 𝑓 1 and 𝑓 2 continuous, piecewise linear and known at the vertices of the
mesh Th is obtained using:

1 medit("sol1 sol2", Th, f1, f2, order=1);

The first plot named sol1 display f1. The second plot names sol2 display f2.
The arguments of the function medit are the name of the differents scenes (separated by a space) of medit, a mesh
and solutions.
Each solution is associated with one scene. The scalar, vector and symmetric tensor solutions are specified in the format
described in the section dealing with the keyword savesol.
The parameters of this command line are :
• order= 0 if the solution is given at the center of gravity of elements. 1 is the solution is given at the vertices
of elements.
• meditff= set the name of execute command of medit. By default, this string is medit.
• save= set the name of a file .sol or .solb to save solutions.
This command line allows also to represent two differents meshes and solutions on them in the same windows. The
nature of solutions must be the same. Hence, we can vizualize in the same window the different domains in a domain
decomposition method for instance. A vizualisation with medit of scalar solutions ℎ1 and ℎ2 at vertices of the mesh
Th1 and Th2 respectively are obtained using:

3.2. Mesh Generation 171


FreeFEM Documentation, Release 4.8

1 medit("sol2domain", Th1, h1, Th2, h2, order=1);

Tip: Medit
1 load "medit"
2

3 // Initial Problem:
4 // Resolution of the following EDP:
5 // -Delta u_s = f on \Omega = { (x,y) | 1 <= sqrt(x^2+y^2) <= 2 }
6 // -Delta u_1 = f1 on \Omega_1 = { (x,y) | 0.5 <= sqrt(x^2+y^2) <= 1. }
7 // u = 1 on Gamma
8 // Null Neumman condition on Gamma_1 and on Gamma_2
9 // We find the solution u by solving two EDP defined on domain Omega and Omega_1
10 // This solution is visualize with medit
11

12 verbosity=3;
13

14 // Mesh
15 border Gamma(t=0, 2*pi){x=cos(t); y=sin(t); label=1;};
16 border Gamma1(t=0, 2*pi){x=2*cos(t); y=2*sin(t); label=2;};
17 border Gamma2(t=0, 2*pi){x=0.5*cos(t); y=0.5*sin(t); label=3;};
18

19 mesh Th = buildmesh(Gamma1(40) + Gamma(-40)); //Omega


20 mesh Th1 = buildmesh(Gamma(40) + Gamma2(-40)); //Omega_1
21

22 // Fespace
23 fespace Vh(Th, P2);
24 func f = sqrt(x*x + y*y);
25 Vh us, v;
26

27 fespace Vh1(Th1, P2);


28 func f1 = 10*sqrt(x*x+y*y);
29 Vh1 u1, v1;
30

31 // Macro
32 macro Grad2(us) [dx(us), dy(us)] // EOM
33

34 // Problem
35 problem Lap2dOmega (us, v, init=false)
36 = int2d(Th)(
37 Grad2(v)' * Grad2(us)
38 )
39 - int2d(Th)(
40 f*v
41 )
42 +on(1, us=1)
43 ;
44

45 problem Lap2dOmega1 (u1, v1, init=false)


46 = int2d(Th1)(
47 Grad2(v1)' * Grad2(u1)
48 )
(continues on next page)

172 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


49 - int2d(Th1)(
50 f1*v1
51 )
52 + on(1, u1=1)
53 ;
54

55 // Solve
56 Lap2dOmega;
57 Lap2dOmega1;
58

59 // Plot with medit


60 medit("solution", Th, us, Th1, u1, order=1, save="testsavemedit.solb");

3.2.7 Mshmet

Mshmet is a software developed by P. Frey that allows to compute an anisotropic metric based on solutions (i.e. Hessian-
based). This software can return also an isotropic metric. Moreover, mshmet can also construct a metric suitable for
levelset interface capturing. The solution can be defined on 2D or 3D structured/unstructured meshes. For example,
the solution can be an error estimate of a FE solution.
Solutions for mshmet are given as an argument. The solution can be a func, a vector func, a symmetric tensor, a
fespace function, a fespace vector function and a fespace symmetric tensor. The symmetric tensor argument is
defined as this type of data for datasol argument. This software accepts more than one solution.
For example, the metric 𝑀 computed with mshmet for the solution 𝑢 defined on the mesh 𝑇 ℎ is obtained by writing:

1 fespace Vh(Th, P1);


2 Vh u; //a scalar fespace function
3 real[int] M = mshmet(Th, u);

The parameters of the keyword mshmet are :


• normalization = (b) do a normalization of all solution in [0, 1].
• aniso = (b) build anisotropic metric if 1 (default 0: isotropic)
• levelset = (b) build metric for levelset method (default: false)
• verbosity = (l) level of verbosity
• nbregul = (l) number of regularization’s iteration of solutions given (default 0).
• hmin = (d)
• hmax = (d)
• err = (d) level of error.
• width = (d) the width
• metric = a vector of double. This vector contains an initial metric given to mshmet. The structure of the
metric vector is described in the next paragraph.
• loptions = a vector of integer of size 7. This vector contains the integer parameters of mshmet (for expert
only).
– loptions(0): normalization (default 1).

3.2. Mesh Generation 173


FreeFEM Documentation, Release 4.8

– loptions(1): isotropic parameters (default 0). 1 for isotropic metric results otherwise 0.
– loptions(2): level set parameters (default 0). 1 for building level set metric otherwise 0.
– loptions(3): debug parameters (default 0). 1 for turning on debug mode otherwise 0.
– loptions(4): level of verbosity (default 10).
– loptions(5): number of regularization’s iteration of solutions given (default 0).
– loptions(6): previously metric parameter (default 0). 1 for using previous metric otherwise 0.
• doptions= a vector of double of size 4. This vector contains the real parameters of mshmet (for expert only).
– doptions(0): hmin : min size parameters (default 0.01).
– doptions(1): hmax : max size parameters (default 1.0).
– doptions(2): eps : tolerance parameters (default 0.01).
– doptions(2): width : relative width for Level Set (0 < 𝑤 < 1) (default 0.05).
The result of the keyword mshmet is a real[int] which contains the metric computed by mshmet at the different
vertices 𝑉𝑖 of the mesh.
With 𝑛𝑣 is the number of vertices, the structure of this vector is:

𝑀𝑖𝑠𝑜 = (𝑚(𝑉0 ), 𝑚(𝑉1 ), . . . , 𝑚(𝑉𝑛𝑣 ))𝑡


⎛ ⎞
𝑚11 𝑚12 𝑚13
for a isotropic metric 𝑚. For a symmetric tensor metric ℎ = ⎝ 𝑚21 𝑚22 𝑚23 ⎠ , the parameters metric is:
𝑚31 𝑚32 𝑚33

𝑀𝑎𝑛𝑖𝑠𝑜 = (𝐻(𝑉0 ), . . . , 𝐻(𝑉𝑛𝑣 ))𝑡

where 𝐻(𝑉𝑖 ) is the vector of size 6 defined by [𝑚11, 𝑚21, 𝑚22, 𝑚31, 𝑚32, 𝑚33]

Tip: mshmet

1 load "mshmet"
2 load "medit"
3 load "msh3"
4

5 // Parameters
6 real error = 0.01;
7 func zmin = 0;
8 func zmax = 1;
9 int MaxLayer = 10;
10

11 // Mesh
12 border a(t=0, 1.0){x=t; y=0; label=1;};
13 border b(t=0, 0.5){x=1; y=t; label=2;};
14 border c(t=0, 0.5){x=1-t; y=0.5; label=3;};
15 border d(t=0.5, 1){x=0.5; y=t; label=4;};
16 border e(t=0.5, 1){x=1-t; y=1; label=5;};
17 border f(t=0.0, 1){x=0; y=1-t; label=6;};
18 mesh Th = buildmesh(a(6) + b(4) + c(4) + d(4) + e(4) + f(6));
19 mesh3 Th3 = buildlayers(Th, MaxLayer, zbound=[zmin, zmax]);
20

(continues on next page)

174 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


21 // Fespace
22 fespace Vh3(Th3, P2);
23 Vh3 u3, v3;
24

25 fespace Vh3P1(Th3, P1);


26 Vh3P1 usol;
27

28 // Problem
29 problem Problem2(u3, v3, solver=sparsesolver)
30 = int3d(Th3)(
31 u3*v3*1.0e-10
32 + dx(u3)*dx(v3)
33 + dy(u3)*dy(v3)
34 + dz(u3)*dz(v3)
35 )
36 - int3d(Th3)(
37 v3
38 )
39 +on(0, 1, 2, 3, 4, 5, 6, u3=0)
40 ;
41

42 // Solve
43 Problem2;
44 cout << u3[].min << " " << u3[].max << endl;
45

46 medit("Sol", Th3, u3);


47

48 real[int] bb = mshmet(Th3,u3);
49 cout << "Metric:" << bb << endl;
50 for (int ii = 0; ii < Th3.nv; ii++)
51 usol[][ii] = bb[ii];
52

53 medit("Metric", Th3, usol);

3.2.8 FreeYams

FreeYams is a surface mesh adaptation software which is developed by P. Frey. This software is a new version of
yams. The adapted surface mesh is constructed with a geometric metric tensor field. This field is based on the intrinsic
properties of the discrete surface.
Also, this software allows to construct a simplification of a mesh. This decimation is based on the Hausdorff distance
between the initial and the current triangulation. Compared to the software yams, FreeYams can be used also to produce
anisotropic triangulations adapted to levelset simulations. A technical report on freeYams documentation is available
here.
To call FreeYams in FreeFEM, we used the keyword freeyams. The arguments of this function are the initial mesh
and/or metric. The metric with freeyams are a func, a fespace function, a symmetric tensor function, a symmetric
tensor fespace function or a vector of double (real[int]). If the metric is a vector of double, this data must be given
in metric parameter. Otherwise, the metric is given in the argument.
For example, the adapted mesh of Thinit defined by the metric 𝑢 defined as fespace function is obtained by writing:

3.2. Mesh Generation 175


FreeFEM Documentation, Release 4.8

1 fespace Vh(Thinit, P1);


2 Vh u;
3 mesh3 Th = freeyams(Thinit, u);

The symmetric tensor argument for freeyams keyword is defined as this type of data for datasol argument.
• aniso= (b) aniso or iso metric (default 0, iso)
• mem= (l) memory of for freeyams in Mb (default -1, freeyams choose)
• hmin= (d)
• hmax= (d)
• gradation= (d)
• option= (l)
– 0 : mesh optimization (smoothing+swapping)
– 1 : decimation+enrichment adaptated to a metric map. (default)
– -1 : decimation adaptated to a metric map.
– 2 : decimation+enrichment with a Hausdorff-like method
– -2 : decimation with a Hausdorff-like method
– 4 : split triangles recursively.
– 9 : No-Shrinkage Vertex Smoothing
• ridgeangle= (d)
• absolute= (b)
• verbosity= (i)
• metric= vector expression. This parameters contains the metric at the different vertices on the initial mesh.
With 𝑛𝑣 is the number of vertices, this vector is:

𝑀𝑖𝑠𝑜 = (𝑚(𝑉0 ), 𝑚(𝑉1 ), . . . , 𝑚(𝑉𝑛𝑣 ))𝑡


⎛ ⎞
𝑚11 𝑚12 𝑚13
for a scalar metric 𝑚. For a symmetric tensor metric ℎ = ⎝ 𝑚21 𝑚22 𝑚23 ⎠, the parameters metric is:
𝑚31 𝑚32 𝑚33

𝑀𝑎𝑛𝑖𝑠𝑜 = (𝐻(𝑉0 ), . . . , 𝐻(𝑉𝑛𝑣 ))𝑡

where 𝐻(𝑉𝑖 ) is the vector of size 6 defined by [𝑚11, 𝑚21, 𝑚22, 𝑚31, 𝑚32, 𝑚33]
• loptions= a vector of integer of size 13. This vectors contains the integer options of FreeYams. (just for the
expert)
– loptions(0): anisotropic parameter (default 0). If you give an anisotropic metric 1 otherwise 0.
– loptions(1): Finite Element correction parameter (default 0). 1 for no Finite Element correction
otherwise 0.
– loptions(2): Split multiple connected points parameter (default 1). 1 for splitting multiple con-
nected points otherwise 0.
– loptions(3): maximum value of memory size in Mbytes (default -1: the size is given by freeyams).

176 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

– loptions(4): set the value of the connected component which we want to obtain. (Remark:
freeyams give an automatic value at each connected component).
– loptions(5): level of verbosity
– loptions(6): Create point on straight edge (no mapping) parameter (default 0). 1 for creating
point on straight edge otherwise 0.
– loptions(7): validity check during smoothing parameter. This parameter is only used with No-
Shrinkage Vertex Smoothing optimization (optimization option parameter 9). 1 for No validity
checking during smoothing otherwise 0.
– loptions(8): number of desired’s vertices (default -1).
– loptions(9): number of iteration of optimizations (default 30).
– loptions(10): no detection parameter (default 0). 1 for detecting the ridge on the mesh otherwise
0. The ridge definition is given in the parameter doptions(12).
– loptions(11): no vertex smoothing parameter (default 0). 1 for smoothing the vertices otherwise 0.
– loptions(12): Optimization level parameter (default 0).
– 0 : mesh optimization (smoothing+swapping)
– 1 : decimation+enrichment adaptated to a metric map.
– -1: decimation adaptated to a metric map.
– 2 : decimation+enrichment with a Hausdorff-like method
– -2: decimation with a Hausdorff-like method
– 4 : split triangles recursively.
– 9 : No-Shrinkage Vertex Smoothing
• doptions= a vector of double of size 11. This vectors contains the real options of freeyams.
– doptions(0): Set the geometric approximation (Tangent plane deviation) (default 0.01).
– doptions(1): Set the lamda parameter (default -1).
– doptions(2): Set the mu parmeter (default -1).
– doptions(3): Set the gradation value (Mesh density control) (default 1.3).
– doptions(4): Set the minimal size(hmin) (default -2.0: the size is automatically computed).
– doptions(5): Set the maximal size(hmax) (default -2.0: the size is automatically computed).
– doptions(6): Set the tolerance of the control of Chordal deviation (default -2.0).
– doptions(7): Set the quality of degradation (default 0.599).
– doptions(8): Set the declic parameter (default 2.0).
– doptions(9): Set the angular walton limitation parameter (default 45 degree).
– doptions(10): Set the angular ridge detection (default 45 degree).

Tip: freeyams

1 load "msh3"
2 load "medit"
3 load "freeyams"
(continues on next page)

3.2. Mesh Generation 177


FreeFEM Documentation, Release 4.8

(continued from previous page)


4

5 // Parameters
6 int nn = 20;
7 real zmin = 0;
8 real zmax = 1;
9

10 // Mesh
11 mesh Th2 = square(nn, nn);
12 int[int] rup = [0, 2], rdown = [0, 1];
13 int[int] rmid = [1, 1, 2, 1, 3, 1, 4, 1];
14 mesh3 Th = buildlayers(Th2, nn, zbound=[zmin, zmax], reffacemid=rmid, reffaceup=rup,␣
˓→reffacelow=rdown);

15 mesh3 Th3 = freeyams(Th);


16

17 medit("SurfaceMesh", Th3);

3.2.9 mmg3d

Todo: mmg3d-v4.0

Mmg3d is a 3D remeshing software developed by C. Dobrzynski and P. Frey.


This software allows to remesh an initial mesh made of tetrahedra. This initial mesh is adapted to a geometric metric
tensor field or to a displacement vector (moving rigid body). The metric can be obtained with mshmet.

Note:
• If no metric is given, an isotropic metric is computed by analyzing the size of the edges in the initial mesh.
• If a displacement is given, the vertices of the surface triangles are moved without verifying the geometrical
structure of the new surface mesh.

The parameters of mmg3d are :


• options= vector expression. This vector contains the option parameters of mmg3d. It is a vector of 6 values,
with the following meaning:
– Optimization parameters : (default 1)
0 : mesh optimization.
1 : adaptation with metric (deletion and insertion vertices) and optimization.
-1 : adaptation with metric (deletion and insertion vertices) without optimization.
4 : split tetrahedra (be careful modify the surface).
9 : moving mesh with optimization.
-9 : moving mesh without optimization.
– Debug mode : (default 0)
1 : turn on debug mode.

178 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

0 : otherwise.
– Specify the size of bucket per dimension (default 64)
– Swapping mode : (default 0)
1 : no edge or face flipping.
0 : otherwise.
– Insert points mode : (default 0)
1 : no edge splitting or collapsing and no insert points.
0 : otherwise.
5. Verbosity level (default 3)
• memory= integer expression. Set the maximum memory size of new mesh in Mbytes. By default the number
of maximum vertices, tetrahedra and triangles are respectively 500 000, 3000 000, 100000 which represent
approximately a memory of 100 Mo.
• metric= vector expression. This vector contains the metric given at mmg3d. It is a vector of size 𝑛𝑣 or 6 𝑛𝑣
respectively for an isotropic and anisotropic metric where 𝑛𝑣 is the number of vertices in the initial mesh.
The structure of metric vector is described in the mshmet.
• displacement= [Φ1, Φ2, Φ3] set the displacement vector of the initial mesh Φ(x, y) =
[Φ1(𝑥, 𝑦), Φ2(𝑥, 𝑦), Φ3(𝑥, 𝑦)].
• displVect= sets the vector displacement in a vector expression. This vector contains the displacement at
each point of the initial mesh. It is a vector of size 3 𝑛𝑣.

Tip: mmg3d

1 load "msh3"
2 load "medit"
3 load "mmg3d"
4 include "Cube.idp"
5

6 // Parameters
7 int n = 6;
8 int[int] Nxyz = [12, 12, 12];
9 real [int, int] Bxyz = [[0., 1.], [0., 1.], [0., 1.]];
10 int [int, int] Lxyz = [[1, 1], [2, 2], [2, 2]];
11

12 // Mesh
13 mesh3 Th = Cube(Nxyz, Bxyz, Lxyz);
14

15 real[int] isometric(Th.nv);
16 for (int ii = 0; ii < Th.nv; ii++)
17 isometric[ii] = 0.17;
18

19 mesh3 Th3 = mmg3d(Th, memory=100, metric=isometric);


20

21 // Plot
22 medit("Initial", Th);
23 medit("Isometric", Th3);

3.2. Mesh Generation 179


FreeFEM Documentation, Release 4.8

Tip: Falling spheres

1 load "msh3"
2 load "TetGen"
3 load "medit"
4 load "mmg3d"
5 include "MeshSurface.idp"
6

7 // Parameters
8 real hs = 0.8;
9 int[int] N = [4/hs, 8/hs, 11.5/hs];
10 real [int, int] B = [[-2, 2], [-2, 6], [-10, 1.5]];
11 int [int, int] L = [[311, 311], [311, 311], [311, 311]];
12

13 int[int] opt = [9, 0, 64, 0, 0, 3];


14 real[int] vit=[0, 0, -0.3];
15 func zero = 0.;
16 func dep = vit[2];
17

18 // Meshes
19 meshS ThH = SurfaceHex(N, B, L, 1);
20 meshS ThSg = Sphere(1, hs, 300, -1);
21 meshS ThSd = Sphere(1, hs, 310, -1);
22 ThSd = movemesh(ThSd, [x, 4+y, z]);
23 meshS ThHS = ThH + ThSg + ThSd;
24 medit("ThHS", ThHS);
25

26 real voltet = (hs^3)/6.;


27 real[int] domain = [0, 0, -4, 1, voltet];
28 real [int] holes = [0, 0, 0, 0, 4, 0];
29 mesh3 Th = tetg(ThHS, switch="pqaAAYYQ", nbofregions=1, regionlist=domaine, nbofholes=2,␣
˓→holelist=holes);

30 medit("Box-With-two-Ball", Th);
31

32 // Fespace
33 fespace Vh(Th, P1);
34 Vh uh,vh;
35

36 // Macro
37 macro Grad(u) [dx(u),dy(u),dz(u)]
38

39 // Problem
40 problem Lap (uh, vh, solver=CG)
41 = int3d(Th)(
42 Grad(uh)' * Grad(vh)
43 )
44 + on(310, 300, uh=dep)
45 + on(311, uh=0.)
46 ;
47

48 // Falling loop
49 for(int it = 0; it < 29; it++){
(continues on next page)

180 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


50 cout << " ITERATION " << it << endl;
51

52 // Solve
53 Lap;
54

55 // Plot
56 plot(Th, uh);
57

58 // Sphere falling
59 Th = mmg3d(Th, options=opt, displacement=[zero, zero, uh], memory=1000);
60 }

3.2.10 A first 3d isotrope mesh adaptation process

Tip: Adaptation 3D
1 load "msh3"
2 load "TetGen"
3 load "mshmet"
4 load "medit"
5

6 // Parameters
7 int nn = 6;
8 int[int] l1111 = [1, 1, 1, 1]; //labels
9 int[int] l01 = [0, 1];
10 int[int] l11 = [1, 1];
11

12 real errm = 1e-2; //level of error


13

14 // Mesh
15 mesh3 Th3 = buildlayers(square(nn, nn, region=0, label=l1111),
16 nn, zbound=[0, 1], labelmid=l11, labelup=l01, labeldown=l01);
17

18 Th3 = trunc(Th3, (x<0.5) | (y < 0.5) | (z < 0.5), label=1); //remove the ]0.5,1[^3 cube
19

20 // Fespace
21 fespace Vh(Th3, P1);
22 Vh u, v, usol, h;
23

24 // Macro
25 macro Grad(u) [dx(u), dy(u), dz(u)] // EOM
26

27 // Problem
28 problem Poisson (u, v, solver=CG)
29 = int3d(Th3)(
30 Grad(u)' * Grad(v)
31 )
32 - int3d(Th3)(
33 1*v
(continues on next page)

3.2. Mesh Generation 181


FreeFEM Documentation, Release 4.8

(continued from previous page)


34 )
35 + on(1, u=0)
36 ;
37

38 // Loop
39 for (int ii = 0; ii < 5; ii++){
40 // Solve
41 Poisson;
42 cout << "u min, max = " << u[].min << " "<< u[].max << endl;
43

44 h=0.; //for resizing h[] because the mesh change


45 h[] = mshmet(Th3, u, normalization=1, aniso=0, nbregul=1, hmin=1e-3, hmax=0.3,␣
˓→err=errm);

46 cout << "h min, max = " << h[].min << " "<< h[].max << " " << h[].n << " " << Th3.nv
˓→<< endl;

47 plot(u, wait=true);
48

49 errm *= 0.8; //change the level of error


50 cout << "Th3 " << Th3.nv < " " << Th3.nt << endl;
51 Th3 = tetgreconstruction(Th3, switch="raAQ", sizeofvolume=h*h*h/6.); //rebuild mesh
52 medit("U-adap-iso-"+ii, Th3, u, wait=true);
53 }

3.2.11 Build a 2d mesh from an isoline

The idea is to get the discretization of an isoline of fluid meshes, this tool can
√︀be useful to construct meshes from image.
First, we give an example of the isovalue meshes 0.2 of analytical function (𝑥 − 1/2)2 + (𝑦 − 1/2)2 , on unit square.

1 load "isoline"
2

3 real[int,int] xy(3, 1); //to store the isoline points


4 int[int] be(1); //to store the begin, end couple of lines
5 {
6 mesh Th = square(10, 10);
7 fespace Vh(Th, P1);
8 Vh u = sqrt(square(x-0.5) + square(y-0.5));
9 real iso = 0.2 ;
10 real[int] viso = [iso];
11 plot(u, viso=viso,Th);//to see the iso line
12

13 int nbc = isoline(Th, u, xy, close=1, iso=iso, beginend=be, smoothing=0.1);

The isoline parameters are Th the mesh, the expression 𝑢, the bidimentionnal array xy to store the list coordinate of
the points. The list of named parameter are :
• iso= value of the isoline to compute (0 is the default value)
• close= close the isoline with the border (default true), we add the part of the mesh border such the value is
less than the isovalue
• smoothing= number of smoothing process is the 𝑙𝑟 𝑠 where 𝑙 is the length of the current line component, 𝑟 the
ratio, 𝑠 is smoothing value. The smoothing default value is 0.

182 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• ratio= the ratio (1 by default).


• eps= relative 𝜀 (default 1e-10)
• beginend= array to get begin, end couple of each of sub line (resize automatically)
• file= to save the data curve in data file for gnuplot
In the array xy you get the list of vertices of the isoline, each connex line go from 𝑖 = 𝑖𝑐0 , . . . , 𝑖𝑐1 − 1 with 𝑖𝑐0 = 𝑏𝑒(2 * 𝑐)
𝑖𝑐1 = 𝑏𝑒(2 * 𝑐 + 1), and where 𝑥𝑖 = 𝑥𝑦(0, 𝑖), 𝑦𝑖 = 𝑦𝑥(1, 𝑖), 𝑙𝑖 = 𝑥𝑦(2, 𝑖).
Here 𝑙𝑖 is the length of the line (the origin of the line is point 𝑖𝑐0 ).
The sense of the isoline is such that the upper part is at the left size of the isoline. So here : the minimum is a point
0.5, 05 so the curve 1 turn in the clockwise sense, the order of each component are sort such that the number of point
by component is decreasing.

1 cout << "Number of the line component = " << nbc << endl;
2 cout << "Number of points = " << xy.m << endl;
3 cout << "be = " << be << endl;
4

5 // shows the lines component


6 for (int c = 0; c < nbc; ++c){
7 int i0 = be[2*c], i1 = be[2*c+1]-1;
8 cout << "Curve " << c << endl;
9 for(int i = i0; i <= i1; ++i)
10 cout << "x= " << xy(0,i) << " y= " << xy(1,i) << " s= " << xy(2, i) << endl;
11 plot([xy(0, i0:i1), xy(1, i0:i1)], wait=true, viso=viso, cmm=" curve "+c);
12 }
13 }
14

15 cout << "length of last curve = " << xy(2, xy.m-1) << endl;

We also have a new function to easily parametrize a discrete curve defined by the couple 𝑏𝑒, 𝑥𝑦.

1 border Curve0(t=0, 1){


2 int c=0; //component 0
3 int i0=be[2*c], i1=be[2*c+1]-1;
4 P=Curve(xy, i0, i1, t); //Curve 0
5 label=1;
6 }
7

8 border Curve1(t=0, 1){


9 int c=1; //component 1
10 int i0=be[2*c], i1=be[2*c+1]-1;
11 P=Curve(xy, i0, i1, t); //Curve 1
12 label=1;
13 }
14

15 plot(Curve1(100)); //show curve


16 mesh Th = buildmesh(Curve1(-100));
17 plot(Th, wait=true);

Secondly, we use this idea to build meshes from an image, we use the plugins ppm2rnm to read pgm a gray scale image
and then we extract the gray contour at level 0.25.

Tip: Leman lake

3.2. Mesh Generation 183


FreeFEM Documentation, Release 4.8

1 load "ppm2rnm"
2 load "isoline"
3

4 // Parameters
5 string leman = "LemanLake.pgm";
6 real AreaLac = 580.03; //in km^2
7 real hsize = 5;
8 real[int, int] Curves(3, 1);
9 int[int] be(1);
10 int nc; //nb of curve
11 {
12 real[int, int] ff1(leman); //read image
13 //and set it in a rect. array
14 int nx = ff1.n, ny = ff1.m;
15 //build a Cartesian mesh such that the origin is in the right place.
16 mesh Th = square(nx-1, ny-1, [(nx-1)*(x), (ny-1)*(1-y)]);
17 //warning the numbering of the vertices (x,y) is
18 //given by $i = x/nx + nx* y/ny $
19 fespace Vh(Th, P1);
20 Vh f1;
21 f1[] = ff1; //transform array in finite element functions.
22 nc = isoline(Th, f1, iso=0.25, close=1, Curves, beginend=be, smoothing=.1, ratio=0.
˓→5);

23 }
24

25 //The longest isoline: the lake


26 int ic0 = be(0), ic1 = be(1)-1;
27 plot([Curves(0, ic0:ic1), Curves(1, ic0:ic1)], wait=true);
28

29 int NC = Curves(2, ic1)/hsize;


30 real xl = Curves(0, ic0:ic1).max - 5;
31 real yl = Curves(1, ic0:ic1).min + 5;
32 border G(t=0, 1){P=Curve(Curves, ic0, ic1, t); label=1+(x>xl)*2+(y<yl);}
33 plot(G(-NC), wait=true);
34

35 mesh Th = buildmesh(G(-NC));
36 plot(Th, wait=true);
37

38 real scale = sqrt(AreaLac/Th.area);


39 Th = movemesh(Th, [x*scale, y*scale]);
40 cout << "Th.area = " << Th.area << " Km^2 " << " == " << AreaLac << " Km^2 " << endl;
41 plot(Th, wait=true, ps="leman.eps");

184 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) The image of the Leman lake meshes


(b) The mesh of the lake

Fig. 3.29: Isoline

3.3 Finite element

As stated in tutorials, FEM approximates all functions 𝑤 as:

𝑤(𝑥, 𝑦) ≃ 𝑤0 𝜑0 (𝑥, 𝑦) + 𝑤1 𝜑1 (𝑥, 𝑦) + · · · + 𝑤𝑀 −1 𝜑𝑀 −1 (𝑥, 𝑦)

with finite element basis functions 𝜑𝑘 (𝑥, 𝑦) and numbers 𝑤𝑘 (𝑘 = 0, · · · , 𝑀 − 1). The functions 𝜑𝑘 (𝑥, 𝑦) are con-
structed from the triangle 𝑇𝑖𝑘 , and called shape functions.
In FreeFEM, the finite element space:

𝑉ℎ = {𝑤 | 𝑤0 𝜑0 + 𝑤1 𝜑1 + · · · + 𝑤𝑀 −1 𝜑𝑀 −1 , 𝑤𝑖 ∈ R }

is easily created by
• in 2d

1 fespace IDspace(IDmesh,<IDFE>);

or with ℓ pairs of periodic boundary conditions in 2D:

1 fespace IDspace(IDmesh,<IDFE>,
2 periodic=[[la1, sa1], [lb1, sb1],
3 ...
4 [lak, sak], [lbk, sbl]]);

• in 3D:

1 fespace IDspace(IDmesh3,<IDFE>,
2 periodic=[[la1, sa1, ta1], [lb1, sb1, tb1],
3 ...
4 [lak, sak, tak], [lbk, sbl, tbl]]);

• in surface 3D:

1 fespace IDspace(IDmeshS,<IDFE>,
2 periodic=[[la1, sa1, ta1], [lb1, sb1, tb1],
3 ...
4 [lak, sak, tak], [lbk, sbl, tbl]]);

3.3. Finite element 185


FreeFEM Documentation, Release 4.8

where IDspace is the name of the space (e.g. Vh), IDmesh IDmesh3 IDmeshS `is respectly the name of the
associated :freefem:`mesh, mesh3, meshS and <IDFE> is an identifier of finite element type.
In 2D we have a pair of periodic boundary conditions, if [𝑙𝑎𝑖 , 𝑠𝑎𝑖 ], [𝑙𝑏𝑖 , 𝑠𝑏𝑖 ] is a pair of int, and the 2 labels 𝑙𝑎𝑖 and
𝑙𝑏𝑖 refer to 2 pieces of boundary to be in equivalence.
If [𝑙𝑎𝑖 , 𝑠𝑎𝑖 ], [𝑙𝑏𝑖 , 𝑠𝑏𝑖 ] is a pair of real, then 𝑠𝑎𝑖 and 𝑠𝑏𝑖 give two common abscissa on the two boundary curves, and
two points are identified as one if the two abscissa are equal.
In 2D, we have a pair of periodic boundary conditions, if [𝑙𝑎𝑖 , 𝑠𝑎𝑖 , 𝑡𝑎𝑖 ], [𝑙𝑏𝑖 , 𝑠𝑏𝑖 , 𝑡𝑏𝑖 ] is a pair of int, the 2 labels 𝑙𝑎𝑖
and 𝑙𝑏𝑖 define the 2 pieces of boundary to be in equivalence.
If [𝑙𝑎𝑖 , 𝑠𝑎𝑖 , 𝑡𝑎𝑖 ], [𝑙𝑏𝑖 , 𝑠𝑏𝑖 , 𝑡𝑏𝑖 ] is a pair of real, then 𝑠𝑎𝑖 , 𝑡𝑎𝑖 and 𝑠𝑏𝑖 , 𝑡𝑏𝑖 give two common parameters on the two
boundary surfaces, and two points are identified as one if the two parameters are equal.

Note: The 2D mesh of the two identified borders must be the same, so to be sure, use the parameter
fixedborder=true in buildmesh command (see fixedborder).

3.3.1 List of the types of finite elements

As of today, the known types of finite elements are:


• [P0] piecewise constant discontinuous finite element (2d, 3d, surface 3d), the degrees of freedom are the
barycenter element value.

P0ℎ = 𝑣 ∈ 𝐿2 (Ω) ⃒ for all 𝐾 ∈ 𝒯ℎ there is 𝛼𝐾 ∈ R : 𝑣|𝐾 = 𝛼𝐾 (3.2)


{︀ ⃒ }︀

• [P1] piecewise linear continuous finite element (2d, 3d, surface 3d), the degrees of freedom are the vertices
values.

P1ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃1 (3.3)


{︀ ⃒ }︀

• [P1dc] piecewise linear discontinuous finite element (2d, 3d with load”Element_P1dc1”)

P1𝑑𝑐|ℎ = 𝑣 ∈ 𝐿2 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃1 (3.4)


{︀ ⃒ }︀

Warning: Due to an interpolation problem, the degree of freedom is not the vertices but three vertices which
move inside 𝑇 (𝑋) = 𝐺 + .99(𝑋 − 𝐺) where 𝐺 is the barycenter.

• [P1b] piecewise linear continuous finite element plus bubble (2d, 3d)
The 2D Case:

P1𝑏|ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃1 ⊕ Span{𝜆𝐾 𝐾 𝐾


(3.5)
{︀ ⃒ }︀
0 𝜆1 𝜆2 }

The 3D Case:

186 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

P1𝑏|ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃1 ⊕ Span{𝜆𝐾 𝐾 𝐾 𝐾


(3.6)
{︀ ⃒ }︀
0 𝜆1 𝜆2 𝜆3 }

where 𝜆𝐾𝑖 , 𝑖 = 0, .., 𝑑 are the 𝑑 + 1 barycentric coordinate functions of the element 𝐾 (triangle or
tetrahedron).
• P1bl,P1bl3d piecewise linear continuous finite element plus linear bubble (with load”Element_P1bl” 2d, 3d).
The bubble is built by splitting the 𝐾, a barycenter in 𝑑 + 1 sub element. (need load "Element_P1bl")
• [P2, P2] piecewise 𝑃2 continuous finite element (2d, 3d, surface 3d)

P2ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃2


{︀ ⃒ }︀

where 𝑃2 is the set of polynomials of R2 of degrees ≤ 2.


• [P2b, P2b3d] piecewise 𝑃2 continuous finite element plus bubble (2d, 3d with load”Element_P2bulle3”)
The 2D Case:

P2ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃2 ⊕ Span{𝜆𝐾 𝐾 𝐾


{︀ ⃒ }︀
0 𝜆1 𝜆2 }

The 3D Case:

P2ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃2 ⊕ Span{𝜆𝐾 𝐾 𝐾 𝐾


{︀ ⃒ }︀
0 𝜆1 𝜆2 𝜆3 }

• [P2dc] piecewise 𝑃2 discontinuous finite element (2d)

P2𝑑𝑐|ℎ = 𝑣 ∈ 𝐿2 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃2


{︀ ⃒ }︀

Warning: Due to an interpolation problem, the degree of freedom is not the six P2 nodes but six nodes
which move inside 𝑇 (𝑋) = 𝐺 + .99(𝑋 − 𝐺) where 𝐺 is the barycenter.

• [P2h] quadratic homogeneous continuous (without P1).


• [P3] piecewise 𝑃3 continuous finite element (2d) (needs load "Element_P3")

P3ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃3


{︀ ⃒ }︀

where 𝑃3 is the set of polynomials of R2 of degrees ≤ 3.


• [P3dc] piecewise 𝑃3 discontinuous finite element (2d) (needs load "Element_P3dc")

P3𝑑𝑐|ℎ = 𝑣 ∈ 𝐿2 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃3


{︀ ⃒ }︀

where 𝑃3 is the set of polynomials of R2 of degrees ≤ 3.

3.3. Finite element 187


FreeFEM Documentation, Release 4.8

• [P4] piecewise 𝑃4 continuous finite element (2d) (needs load "Element_P4")

P4ℎ = 𝑣 ∈ 𝐻 1 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃4


{︀ ⃒ }︀

where 𝑃4 is the set of polynomials of R2 of degrees ≤ 4.


• [P4dc] piecewise 𝑃4 discontinuous finite element (2d) (needs load "Element_P4dc")

P4𝑑𝑐|ℎ = 𝑣 ∈ 𝐿2 (Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃3


{︀ ⃒ }︀

where 𝑃4 is the set of polynomials of R2 of degrees ≤ 3.


• [P0Edge] piecewise 𝑃0 discontinuous finite element (2d) contained on each edge of the mesh.
• [P1Edge] piecewise 𝑃1 discontinuous finite element (2d) (needs load "Element_PkEdge") 𝑃1 on each edge
of the mesh.
• [P2Edge] piecewise 𝑃2 discontinuous finite element (2d) (needs load "Element_PkEdge") 𝑃2 on each edge
of the mesh.
• [P3Edge] piecewise 𝑃3 discontinuous finite element (2d) (needs load "Element_PkEdge") 𝑃3 on each edge
of the mesh.
• [P4Edge] piecewise 𝑃4 discontinuous finite element (2d) (needs load "Element_PkEdge") 𝑃4 on each edge
of the mesh.
• [P5Edge] piecewise 𝑃5 discontinuous finite element (2d) (needs load "Element_PkEdge") 𝑃5 on each edge
of the mesh.
• [P2Morley] piecewise 𝑃2 non conform finite element (2d) (needs load "Morley")

𝑣 continuous at vertices,
{︂ ⃒ {︂ }︂
P2ℎ 2

= 𝑣 ∈ 𝐿 (Ω) ⃒⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃3 ,
𝜕𝑛 𝑣 continuous at middle of edge,

where 𝑃2 is the set of polynomials of R2 of degrees ≤ 2.

Warning: To build the interplant of a function 𝑢 (scalar) for this finite element, we need the func-
tion and 2 partial derivatives (𝑢, 𝑢𝑥 , 𝑢𝑦 ), creating this vectorial finite element with 3 components
(𝑢, 𝑢𝑥 , 𝑢𝑦 ).

See our example for solving the BiLaplacien problem:

1 load "Morley"
2

3 // Parameters
4 int nn = 10;
5 real h = 0.01;
6

7 real f = 1;
8

9 // Mesh
10 mesh Th = square(nn, nn);
(continues on next page)

188 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


11 Th = adaptmesh(Th, h, IsMetric=1);
12

13 // Fespace
14 fespace Vh(Th, P2Morley); //The Morley finite element space
15 Vh [u, ux, uy], [v, vx, vy];
16

17 // Macro
18 macro bilaplacien(u, v) (dxx(u)*dxx(v) + dyy(u)*dyy(v) + 2.*dxy(u)*dxy(v)) /
˓→/

19

20 // Problem
21 solve bilap ([u, ux, uy], [v, vx, vy])
22 = int2d(Th)(
23 bilaplacien(u, v)
24 )
25 - int2d(Th)(
26 f*v
27 )
28 + on(1, 2, 3, 4, u=0, ux=0, uy=0)
29 ;
30

31 // Plot
32 plot(u, cmm="u");

• [HCT] 𝑃3 𝐶 1 conforms finite element (2d) (needs load "Element_HCT") one 3 sub triangles.
Lets call 𝒯ℎ△ the sub mesh of 𝒯ℎ where all triangles are split in 3 at the barycenter.
{︁ ⃒ }︁

P𝐻𝐶𝑇 = 𝑣 ∈ 𝐶 1
(Ω) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃3

where 𝑃3 is the set of polynomials of R2 of degrees ≤ 3.


The degrees of freedom are the values of the normal derivative at the mid-point of each edge
[BERNADOU1980].

Warning: To build the interplant of a function 𝑢 (scalar) for this finite element, we need the func-
tion and 2 partial derivatives (𝑢, 𝑢𝑥 , 𝑢𝑦 ), creating this vectorial finite element with 3 components
(𝑢, 𝑢𝑥 , 𝑢𝑦 ) like in previous finite element.

• [P2BR] (needs load "BernadiRaugel") the Bernadi Raugel Finite Element is a Vectorial element (2d) with
2 components, see [BERNARDI1985].
It is a 2D coupled Finite Element, where the Polynomial space is 𝑃12 with 3 normal bubble edge
functions (𝑃2 ). There are 9 degrees of freedom:
– 2 components at each of the 3 vertices and
– the 3 flux on the 3 edges.
• [RT0, RT03d] Raviart-Thomas finite element of degree 0.
The 2D Case:
{︁ ⃒ ⃒ 1 }︁
⃒𝛼
𝑅𝑇 0ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | 𝑥𝑦 (3.7)

2
𝐾

3.3. Finite element 189


FreeFEM Documentation, Release 4.8

The 3D Case:
{︃ ⃒ ⃒ 1 }︃
⃒ ⃒ 𝛼𝐾 ⃒𝑥
𝑅𝑇 0ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 (𝑥, 𝑦, 𝑧) = ⃒ 𝛼𝐾 + 𝛽𝐾 ⃒ (3.8)
⃒ ⃒ 2 ⃒𝑦
⃒ ⃒ 𝛼3𝐾 𝑧

∑︀𝑑
where by writing div w = 𝑖=1𝜕𝑤𝑖 /𝜕𝑥𝑖 with w = (𝑤𝑖 )𝑑𝑖=1 :

𝐻(div) = w ∈ 𝐿2 (Ω)𝑑 ⃒div w ∈ 𝐿2 (Ω)


{︀ ⃒ }︀

and where 𝛼𝐾
1
, 𝛼𝐾
2
, 𝛼𝐾
3
, 𝛽𝐾 are real numbers.
• [RT0Ortho] Raviart-Thomas Orthogonal, or Nedelec finite element type I of degree 0 in dimension 2

{︁ ⃒ ⃒ 1 }︁
⃒𝛼
𝑅𝑇 0𝑂𝑟𝑡ℎ𝑜ℎ = v ∈ 𝐻(curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | −𝑦 (3.9)

2 𝑥
𝐾

• [Edge03d] 3d Nedelec finite element or Edge Element of degree 0.

{︃ ⃒ ⃒ 1 ⃒ 1 }︃
⃒ ⃒ 𝛼𝐾 ⃒ 𝛽𝐾 ⃒𝑥
𝐸𝑑𝑔𝑒0ℎ = v ∈ 𝐻(Curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 (𝑥, 𝑦, 𝑧) = ⃒ 𝛼𝐾 + ⃒ 𝛽𝐾 × ⃒ : 𝑙𝑎𝑏𝑒𝑙 : 𝑒𝑞 : 𝐸𝑑𝑔𝑒03𝑑
⃒ ⃒ 2 ⃒ 2 ⃒𝑦
⃒ ⃒ 𝛼3𝐾 3
⃒ 𝛽𝐾 𝑧


⃒ 𝜕𝑤2 /𝜕𝑥3 −𝜕𝑤3 /𝜕𝑥2
where by writing curlw = ⃒⃒ 𝜕𝑤3 /𝜕𝑥1 −𝜕𝑤1 /𝜕𝑥3 with w = (𝑤𝑖 )𝑑𝑖=1 :
𝜕𝑤1 /𝜕𝑥2 −𝜕𝑤2 /𝜕𝑥1

𝐻(curl) = w ∈ 𝐿2 (Ω)𝑑 ⃒curl w ∈ 𝐿2 (Ω)𝑑


{︀ ⃒ }︀

and 𝛼𝐾
1 2
, 𝛼𝐾 3
, 𝛼𝐾 1
, 𝛽𝐾 2
, 𝛽𝐾 3
, 𝛽𝐾 are real numbers.
• [Edge13d] (needs load "Element_Mixte3d") 3d Nedelec finite element or Edge Element of degree 1.
• [Edge23d] (needs load "Element_Mixte3d") 3d Nedelec finite element or Edge Element of degree 2.
• [P1nc] piecewise linear element continuous at the mid-point of the edge only in 2D (Crouzeix-Raviart Finite
Element 2D).
• [P2pnc] piecewise quadratic plus a P3 bubble element with the continuity of the 2 moments on each edge (needs
load "Element_P2pnc")
• [RT1] (needs load "Element_Mixte")

{︁ ⃒ ⃒ 1 }︁
⃒𝛼
1
𝑅𝑇 1ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 2
, 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃12 , 𝑃0 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | 𝑥𝑦 (3.10)

2
𝐾

• [RT1Ortho] (needs load "Element_Mixte")

{︁ ⃒ ⃒ 1 }︁
1 2 ⃒𝛼
𝑅𝑇 1ℎ = v ∈ 𝐻(curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 , 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃12 , 𝑃0 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽 | −𝑦

2 𝐾 𝑥
𝐾
(3.11)

• [RT2] (needs load "Element_Mixte")

{︁ ⃒ ⃒ 1 }︁
⃒𝛼
1
𝑅𝑇 2ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 2
, 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃22 , 𝑃1 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | 𝑥𝑦 (3.12)

2
𝐾

190 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• [RT2Ortho] (needs load "Element_Mixte")

{︁ ⃒ ⃒ 1 }︁
1 2 ⃒𝛼
𝑅𝑇 2ℎ = v ∈ 𝐻(curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 , 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃22 , 𝑃1 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | −𝑦

2 𝑥
𝐾
(3.13)

• [BDM1] (needs load "Element_Mixte") the Brezzi-Douglas-Marini finite element:

𝐵𝐷𝑀 1ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 ∈ 𝑃12 (3.14)


{︀ ⃒ }︀

• [BDM1Ortho] (needs load "Element_Mixte") the Brezzi-Douglas-Marini Orthogonal also call Nedelec of
type II , finite element

𝐵𝐷𝑀 1𝑂𝑟𝑡ℎ𝑜ℎ = v ∈ 𝐻(curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 ∈ 𝑃12 (3.15)


{︀ ⃒ }︀

• [FEQF] (needs load "Element_QF") the finite element to store functions at default quadrature points (so the
quadrature is qf5pT in 2D and is qfV5 in 3d).
For over quadrature you have the following corresponding finite element’s quadrature formula.
– FEQF1 ↦→ qf1pT,
– FEQF2 ↦→ qf2pT,
– FEQF5 ↦→ qf5pT,
– FEQF7 ↦→ qf7pT,
– FEQF9 ↦→ qf9pT,
– FEQF13d ↦→ qfV1,
– FEQF23d ↦→ qfV2,
– FEQF53d ↦→ qfV5
You can use this element to optimize the storage and reuse of functions with a long formula inside an integral for non
linear processes.

3.3.2 Use of fespace in 2D

With the 2D finite element spaces

𝑋ℎ = 𝑣 ∈ 𝐻 1 (]0, 1[2 )| ∀𝐾 ∈ 𝒯ℎ
{︀ }︀
𝑣|𝐾 ∈ 𝑃1
. .
𝑋𝑝ℎ = {𝑣 ∈ 𝑋ℎ | 𝑣 (| 0. ) = 𝑣 (| 1. ) , 𝑣 (| 0 ) = 𝑣 (| 1 )}

𝑀ℎ = 𝑣 ∈ 𝐻 1 (]0, 1[2 )| ∀𝐾 ∈ 𝒯ℎ 𝑣|𝐾 ∈ 𝑃2


{︀ }︀

⃒ 𝛼𝐾
𝑅ℎ = v ∈ 𝐻 1 (]0, 1[2 )2 | ∀𝐾 ∈ 𝒯ℎ v|𝐾 (𝑥, 𝑦) = ⃒ 𝛽𝐾 + 𝛾𝐾 | 𝑥𝑦
{︀ }︀

when 𝒯ℎ is a mesh 10 × 10 of the unit square ]0, 1[2 , we only write in FreeFEM:

3.3. Finite element 191


FreeFEM Documentation, Release 4.8

1 mesh Th = square(10, 10);


2 fespace Xh(Th, P1); //scalar FE
3 fespace Xph(Th,P1,
4 periodic=[[2, y], [4, y], [1, x], [3, x]]); //bi-periodic FE
5 fespace Mh(Th, P2); //scalar FE
6 fespace Rh(Th, RT0); //vectorial FE

where Xh, Mh, Rh expresses finite element spaces (called FE spaces) 𝑋ℎ , 𝑀ℎ , 𝑅ℎ , respectively.
To use FE-functions 𝑢ℎ , 𝑣ℎ ∈ 𝑋ℎ , 𝑝ℎ , 𝑞ℎ ∈ 𝑀ℎ and 𝑈ℎ , 𝑉ℎ ∈ 𝑅ℎ , we write:

1 Xh uh, vh;
2 Xph uph, vph;
3 Mh ph, qh;
4 Rh [Uxh, Uyh], [Vxh, Vyh];
5 Xh[int] Uh(10); //array of 10 functions in Xh
6 Rh[int] [Wxh, Wyh](10); //array of 10 functions in Rh
7 Wxh[5](0.5,0.5); //the 6th function at point (0.5, 0.5)
8 Wxh[5][]; //the array of the degree of freedom of the 6th function

The functions 𝑈ℎ , 𝑉ℎ have two components so we have

and
⃒ 𝑥ℎ ⃒
𝑈ℎ = ⃒ 𝑈
𝑈 𝑦ℎ 𝑉ℎ = ⃒ 𝑉𝑉 𝑥ℎ
𝑦ℎ

3.3.3 Use of fespace in 3D

With the 3D finite element spaces

𝑋ℎ = {𝑣 ∈ 𝐻 1 (]0, 1[3 )| ∀𝐾 ∈ 𝒯ℎ 𝑣|𝐾 ∈ 𝑃1 }


{︁ (︁⃒ 0 )︁ (︁⃒ 1 )︁ (︀⃒ . )︀ (︀⃒ . )︀ (︀⃒ . )︀ (︀⃒ . )︀}︁
𝑋𝑝ℎ = 𝑣 ∈ 𝑋ℎ | 𝑣 ⃒ . = 𝑣 ⃒ . , 𝑣 ⃒ 0. = 𝑣 ⃒ 1. , 𝑣 ⃒ 0. = 𝑣 ⃒ 1.
⃒ ⃒
. .

𝑀ℎ = {𝑣 ∈ 𝐻 (]0, 1[3 )| ∀𝐾 ∈ 𝒯ℎ
1
𝑣|𝐾 ∈ 𝑃2 }
{︁ ⃒ 𝛼𝐾 ⃒ 𝑥 }︁
𝑅ℎ = v ∈ 𝐻 1 (]0, 1[3 )2 | ∀𝐾 ∈ 𝒯ℎ v|𝐾 (𝑥, 𝑦, 𝑧) = ⃒ 𝛽𝐾 + 𝛿𝐾 ⃒ 𝑦
⃒ ⃒
𝛾
𝐾 𝑧

when 𝒯ℎ is a mesh 10 × 10 × 10 of the unit cubic ]0, 1[ , we write in FreeFEM:


2

1 //label: 0 up, 1 down, 2 front, 3 left, 4 back, 5 right


2 int nn=10;
3 mesh3 Th=buildlayers(square(nn,nn,region=0),nn,
4 zbound=[zmin,zmax], labelmid=rmid, reffaceup = rup,
5 reffacelow = rdown);
6

7 fespace Xh(Th, P1); //scalar FE


8 // a FE space with full periodic condition in 3 axes
9 fespace Xph(Th,P1,periodic=[[1,y,z],[2,y,z],
10 [3,x,z],[4,x,z],[5,x,y],[6,x,y]]);
11 fespace Mh(Th, P2); //scalar FE
12 fespace Rh(Th, RT03d); //vectorial FE

where Xh, Mh, Rh expresses finite element spaces (called FE spaces) 𝑋ℎ , 𝑀ℎ , 𝑅ℎ , respectively.
The functions 𝑈ℎ , 𝑉ℎ have two components so we have

192 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

3.3.4 Use of fespace in surface 3D

With the 3D finite element spaces

𝑋ℎ = {𝑣 ∈ 𝐻 1 (]0, 1[3 )| ∀𝐾 ∈ 𝒯ℎ 𝑣|𝐾 ∈ 𝑃1 }

1 meshS Th = square3(10, 10);


2 fespace Xh(Th, P1); //scalar FE

where Xh expresses finite element spaces (called FE spaces) 𝑋ℎ , respectively.


To use FE-functions 𝑢ℎ , 𝑣ℎ ∈ 𝑋ℎ , 𝑝ℎ , 𝑞ℎ ∈ 𝑀ℎ and 𝑈ℎ , 𝑉ℎ ∈ 𝑅ℎ , we write:

1 Xh uh, vh;
2 Xh[int] Uh(10); //array of 10 functions in Xh

3.3.5 Finite Element functions

To define and use FE-functions 𝑢ℎ , 𝑣ℎ ∈ 𝑋ℎ , 𝑝ℎ , 𝑞ℎ ∈ 𝑀ℎ and 𝑈ℎ , 𝑉ℎ ∈ 𝑅ℎ , we write:

1 Xh uh, vh;
2 Xph uph, vph;
3 Mh ph, qh;
4 Rh [Uxh, Uyh, Uyzh], [Vxh, Vyh, Vyzh];
5 Xh[int] Uh(10); //array of 10 functions in Xh
6 Rh[int] [Wxh,Wyh,Wzh](10); // array of 10 functions in Rh
7 Wxh[5](0.5,0.5,0.5); //the 6th function at point (0.5, 0.5, 0.5)
8 Wxh[5][]; //the array of the degree of freedom of the 6th function

The functions 𝑈ℎ , 𝑉ℎ have three components, so we have:


⃒ ⃒
⃒ (𝑈ℎ )𝑥 ⃒ (𝑉ℎ )𝑥
𝑈ℎ = ⃒⃒ (𝑈ℎ )𝑦 and 𝑉ℎ = ⃒⃒ (𝑉ℎ )𝑦
(𝑈ℎ )𝑧 (𝑉 )
ℎ 𝑧

Note: One challenge of the periodic boundary condition is that the mesh must have equivalent faces.
The buildlayers mesh generator splits each quadrilateral face with the diagonal passing through the vertex with
maximum number, so to be sure to have the same mesh one both face periodic the 2D numbering in corresponding
edges must be compatible (for example the same variation).
By Default, the numbering of square vertex is correct.
To change the mesh numbering you can use the change function like:

1 {
2 int[int] old2new(0:Th.nv-1); //array set on 0, 1, .., nv-1
3 fespace Vh2(Th, P1);
4 Vh2 sorder = x+y; //choose an order increasing on 4 square borders with x or y
5 sort(sorder[], old2new); //build the inverse permutation
6 int[int] new2old = old2new^-1; //inverse the permutation
7 Th = change(Th, renumv=new2old);
8 }

The full example is in examples.

3.3. Finite element 193


FreeFEM Documentation, Release 4.8

3.3.6 Lagrangian Finite Elements

P0-element

For each triangle (d=2) or tetrahedron (d=3) 𝑇𝑘 , the basis function 𝜑𝑘 in Vh(Th, P0) is given by:

1 if (x) ∈ 𝑇𝑘
{︂
𝜑𝑘 (x) =
0 if (x) ̸∈ 𝑇𝑘

If we write:

1 Vh(Th, P0);
2 Vh fh = f(x,y);

𝑞 𝑘𝑖
∑︁ ∑︀
then for vertices 𝑞 𝑘𝑖 , 𝑖 = 1, 2, ..𝑑 + 1 in Fig. 3.30, 𝑓ℎ is built as fh= 𝑓ℎ (𝑥, 𝑦) = 𝑓( 𝑖
)𝜑𝑘
𝑑+1
𝑘

See Fig. 3.31b for the projection of 𝑓 (𝑥, 𝑦) = sin(𝜋𝑥) cos(𝜋𝑦) on Vh(Th, P0) when the mesh Th is a 4 × 4-grid of
[−1, 1]2 as in Fig. 3.31a.

P1-element

Fig. 3.30: 𝑃1 and 𝑃2 degrees of freedom on triangle 𝑇𝑘

For each vertex 𝑞 𝑖 , the basis function 𝜑𝑖 in Vh(Th, P1) is given by:

𝜑𝑖 (𝑥, 𝑦) = 𝑎𝑘𝑖 + 𝑏𝑘𝑖 𝑥 + 𝑐𝑘𝑖 𝑦 for (𝑥, 𝑦) ∈ 𝑇𝑘 ,


𝜑𝑖 (𝑞 𝑖 ) = 1, 𝜑𝑖 (𝑞 𝑗 ) = 0 if 𝑖 ̸= 𝑗

The basis function 𝜑𝑘1 (𝑥, 𝑦) with the vertex 𝑞 𝑘1 in Fig. 3.30 at point 𝑝 = (𝑥, 𝑦) in triangle 𝑇𝑘 simply coincide with the
barycentric coordinates 𝜆𝑘1 (area coordinates):

area of triangle(𝑝, 𝑞 𝑘2 , 𝑞 𝑘3 )
𝜑𝑘1 (𝑥, 𝑦) = 𝜆𝑘1 (𝑥, 𝑦) =
area of triangle(𝑞 𝑘1 , 𝑞 𝑘2 , 𝑞 𝑘3 )
If we write:

194 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(b) Projection to Vh(Th, P0)

(a) Test mesh Th for projection

Fig. 3.31: Finite element P0

1 Vh(Th, P1);
2 Vh fh = g(x.y);

then:
𝑛𝑣
∑︁
fh = 𝑓ℎ (𝑥, 𝑦) = 𝑓 (𝑞 𝑖 )𝜑𝑖 (𝑥, 𝑦)
𝑖=1

See Fig. 3.32a for the projection of 𝑓 (𝑥, 𝑦) = sin(𝜋𝑥) cos(𝜋𝑦) into Vh(Th, P1).

P2-element

For each vertex or mid-point 𝑞 𝑖 . The basis function 𝜑𝑖 in Vh(Th, P2) is given by:
𝜑𝑖 (𝑥, 𝑦) = 𝑎𝑘𝑖 + 𝑏𝑘𝑖 𝑥 + 𝑐𝑘𝑖 𝑦 + 𝑑𝑘𝑖 𝑥2 + 𝑒𝑘𝑖 𝑥𝑦 + 𝑓𝑗𝑓 𝑦 2 for (𝑥, 𝑦) ∈ 𝑇𝑘 ,
𝜑𝑖 (𝑞 𝑖 ) = 1, 𝜑𝑖 (𝑞 𝑗 ) = 0 if 𝑖 ̸= 𝑗
The basis function 𝜑𝑘1 (𝑥, 𝑦) with the vertex 𝑞 𝑘1 in Fig. 3.30 is defined by the barycentric coordinates:
𝜑𝑘1 (𝑥, 𝑦) = 𝜆𝑘1 (𝑥, 𝑦)(2𝜆𝑘1 (𝑥, 𝑦) − 1)
and for the mid-point 𝑞 𝑘2 :
𝜑𝑘2 (𝑥, 𝑦) = 4𝜆𝑘1 (𝑥, 𝑦)𝜆𝑘4 (𝑥, 𝑦)
If we write:
1 Vh(Th, P2);
2 Vh fh = f(x.y);

then:
𝑀
∑︁
fh = 𝑓ℎ (𝑥, 𝑦) = 𝑓 (𝑞 𝑖 )𝜑𝑖 (𝑥, 𝑦) (summation over all vertex or mid-point)
𝑖=1

See Projection to Vh(Th, P2) for the projection of 𝑓 (𝑥, 𝑦) = sin(𝜋𝑥) cos(𝜋𝑦) into Vh(Th, P2).

3.3. Finite element 195


FreeFEM Documentation, Release 4.8

(a) Projection to Vh(Th, P1)

(b) Projection to Vh(Th, P2)

Fig. 3.32: Finite elements P1, P2

3.3.7 Surface Lagrangian Finite Elements

Definition of the surface P1 Lagragian element

To build the surface Pk-Lagrange, the main idea is to consider the usual 2d Lagrangian Finite Elements ; and its writing
in barycentric coordinates ; apply a space transformation and barycentric properties. The FreeFEM finite elements for
surface problem are: P0 P1 P2 P1b.
0) Notation
• Let 𝐾
ˆ be the shape triangle in the space R2 of vertice (𝑖0 , 𝑖1 , 𝑖2 )

• Let 𝐾 be a triangle of the space R3 of vertice (𝐴0 , 𝐴1 , 𝐴2 )


• 𝑥𝑞 a quadrature point on K
• 𝑋𝑞 a quadrature point on A
• 𝑃 12𝑑 designates 2d P1 Lagrangian Finite Elements
• 𝑃 1𝑆 designates surface 3d P1 Lagrangian Finite Elements
• (𝜆𝑖 )2𝑖=0 shape fonction of 𝐾
ˆ (𝑃 12𝑑 )

• (𝜓𝑖 )2𝑖=0 shape fonction of of 𝐾 (𝑃 1𝑆 )


1) Geometric transformation: from the current FE to the reference FE
⎛ ⎞
(︂ )︂ 𝑥
𝑥ˆ
Let be 𝑥
ˆ= ˆ ⊂ R2 and 𝑋 = ⎝𝑦 ⎠ a point of the triangle 𝐾 ⊂ R3 , where 𝑥
a point of the triangle 𝐾 ˆ and 𝑋
ˆ are
𝑦ˆ
𝑧
expressed in baricentric coordinates.
The motivation here is to parameterize the 3d surface mesh to the reference 2d triangle, thus to be reduced to a finite
element 2d P1. Let’s define a geometric transformation F, such as 𝐹 : R2 → R3
However, thus defines transformation F as not bijective.

196 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

So, consider the following approximation

𝐹˜ : R2 → R3
ˆ→𝑋
𝑥
⎛ ⎞ ⎛ −−−→ ⎞
𝑥 𝐴0 𝐴1
⎝𝑦 ⎠ → ⎜ −−−→
𝐴0 𝐴2 𝑥 − 𝐴0 )
⎠ (ˆ


0 −−−→ −−−→
𝐴0 𝐴1 ∧ 𝐴0 𝐴2

where ∧ denote the usual vector product.

Fig. 3.33: F, a parameterization from the reference 2d triangle to a 3d surface triangle

⎛ ⎞
𝑛
−−−→ −−−→ ⎝ 𝑥 ⎠ −−−→ −−−→
Note: 𝐴0𝐴1 ∧ 𝐴0 𝐴2 = 𝑛𝑦 defines the normal to the tangent plane generated by (𝐴0 , 𝐴0 𝐴1 , 𝐴0 𝐴2 )
𝑛𝑧

The affine transformation 𝐹˜ allows you to pass from the 2d reference triangle, which we project in R3 to the 3d current
triangle, discretizing the surface we note Γ.
Then 𝐹˜ −1 is well defined and allows to return to the reference triangle 𝐾,
ˆ to the usual coordinates of R2 completed by
the coordinate 𝑧 = 0.
2) Interpolation element fini
Remember that the reference geometric element for the finite element 𝑃 1𝑠 that we are building is the reference triangle
ˆ in the vertex plane (𝑖0 , 𝑖1 , 𝑖2 ), which we project into space by posing 𝑧 = 0 by the membrane hypothesis.
𝐾
⎛ ⎞ ⎛ ⎞ ⎛ ⎞
0 1 0
Hence 𝑖0 = ⎝0⎠, 𝑖1 = ⎝0⎠, 𝑖1 = ⎝1⎠.
0 0 0
Let X be a point of the current triangle K, we have X= 𝐹˜ (ˆ
𝑥). The barycentric coordinates of X in K are given by:
∑︀2 ˆ 𝑥) où
𝑋 = 𝑖=0 𝐴𝑖 𝜆(ˆ
• 𝐴𝑖 the points of the current triangle K
ˆ 𝑖 basic functions 𝑃 12𝑑
• 𝜆
ˆ 0 (𝑥, 𝑦) = 1 − 𝑥 − 𝑦
• 𝜆
ˆ 1 (𝑥, 𝑦) = 𝑥
• 𝜆
ˆ 2 (𝑥, 𝑦) = 𝑦
• 𝜆

3.3. Finite element 197


FreeFEM Documentation, Release 4.8

We need to define a quadrature formula for the finite element⎛approximation.


⎞ The usual formulation for a 2d triangle
𝑥
ˆ𝑞
will be used by redefining the quadrature points 𝑋𝑞 = 𝑥𝑞 = ⎝ 𝑦ˆ𝑞 ⎠.
0
3) The Lagragian P1 functions and theirs 1st order derivatives
The finite element interpolation gives us the following relationship: 𝜓𝑖 (𝑋) = 𝐹 −1 (𝜓𝑖 )(𝐹 −1 (𝑋)). To find the ex-
pression of the basic functions 𝜓 on the current triangle K, it is sufficient to use the inverse of the transformation 𝐹˜ to
get back to the reference triangle 𝐾.
ˆ However in FreeFEM, the definition of the reference finite element, the current
geometry is based on barycentric coordinates in order not to use geometric transformation. 𝐹˜ . The method used here
is geometric and based on the properties of the vector product and the area of the current triangle K.
i) The shape functions
Let be the triangle K of vertices 𝑖0 , 𝑖1 , 𝑖2 ⊂ R3 and (𝜆𝑖 )2𝑖=0 the local barycentric coordinates at K. The normal is
−−−→ −−−→ −−−→ −−−→
defined as the tangent plane generated by (𝐴0 , 𝐴0 𝐴1 , 𝐴0 𝐴2 ), \ ⃗𝑛 = 𝐴0 𝐴1 ∧ 𝐴0 𝐴2 avec || ⃗𝑛 ||= 2 mes (𝐾).
ˆ

Le denotes the operator V, defines the usual vector product of R3 such as 𝑉 (𝐴, 𝐵, 𝐶) = (𝐵 − 𝐴) ∧ (𝐶 − 𝐴)
The mixed product of three vectors u, v, w, noté [𝑢, 𝑣, 𝑤], is the determinant of these three vectors in any direct or-
thonormal basis, thus (𝐴 ∧ 𝑉, 𝐶) = det (𝐴, 𝐵, 𝐶)
with (., .) is the usual scalar product of R3 . \ Let Ph :math:` in mathbb{R}^3` and P his projected in the triangle K
such as:

Let’s lay the sub-triangles as follows :


• 𝐾0 = (𝑃, 𝐴1, 𝐴2)
• 𝐾1 = (𝐴0, 𝑃, 𝐴2)
• 𝐾2 = (𝐴0, 𝐴1, 𝑃 )
with 𝐾 = 𝐾0 ∪ 𝐾1 ∪ 𝐾2 .

Note:
Properties in R3
−−−→ −−−→
• Let ⃗𝑛 be the normal to the tangent plane generated by (𝐴0 , 𝐴0 𝐴1 , 𝐴0 𝐴2 )
−−−→ −−−→
• ⃗𝑛 = 𝐴0 𝐴1 ∧ 𝐴0 𝐴2
• By definition, 𝒜 = 12 |< ⃗𝑛, ⃗𝑛 >| and the vectorial area by 𝒜𝒮 = 12 < ⃗𝑛, ⃗𝑛 > hence
𝒜𝒮 (𝑃 𝐵𝐶) = 21 < ⃗𝑛0 , ⃗𝑛 >, with ⃗𝑛0 the normal vector to the plane (PBC)

198 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Let’s define the respective vector areas


• 𝑁
⃗ 0 (𝑃 ) = 𝑉 (𝑃, 𝐴1, 𝐴2) the vectorial area of K0

• 𝑁
⃗ 1 (𝑃 ) = 𝑉 (𝐴0, 𝑃, 𝐴2) the vectorial area of K1

• 𝑁
⃗ 2 (𝑃 ) = 𝑉 (𝐴0, 𝐴1, 𝑃 ) the vectorial area of K2

By definition, in 3d, the barycentric coordinates are given by algebraic area ratio: :math:` lambda_i(P) = frac {(vec
N_i(P),vec N)}{(vec N,vec N)}label{basisfunc}`
Note that (𝑁 ⃗ ) = 2 sign mes (𝐾𝑖 ) || 𝑁
⃗ 𝑖 (𝑃 ), 𝑁 ⃗ || and (𝑁 ⃗ ) = 2 sign mes (𝐾) || 𝑁
⃗,𝑁 ⃗ ||, avec 𝑠𝑖𝑔𝑛 the orientation of
the current triangle compared to the reference triangle.
∑︀2
We find the finite element interpolation, 𝑃 = 𝑖=0 𝜆𝑖 (𝑃 )𝐴𝑖 .
ii) 1st order derivatives of Lagrangian P1 FE
Let 𝑌
⃗ be any vector of ∈ R3 .

⃗ 2 (𝑃 ), 𝑌
(𝑁 ⃗ ) = ((𝐴1 − 𝐴0 ) ∧ (𝑃 − 𝐴0 ), 𝑌 )
= det(𝐴1 − 𝐴0 , 𝑃 − 𝐴0 , 𝑌 )
= det(𝐴1 − 𝐴0 , 𝑃, 𝑌 ) − det(𝐴1 − 𝐴0 , 𝐴0 , 𝑌 )

Let’s calculate the differential of (𝑁


⃗ 2 (𝑃 ), 𝑌 ), ∀𝑌

⃗ ) = det(𝐴1 − 𝐴0 , 𝑃 ′ , 𝑌 )𝑑𝑃
⃗ 2 (𝑃 ), 𝑌
𝐷 𝑃 (𝑁
∇𝑃 ( 𝑁 ⃗ ) = det (𝐴1 − 𝐴0 , 𝑃 ′ , 𝑌
⃗ 2 (𝑃 ), 𝑌 ⃗)
⃗ , 𝑃 ′)
= −𝑑𝑒𝑡(𝐴1 − 𝐴0 , 𝑌
⃗ .𝑃 ′
= −(𝐴1 − 𝐴0 ) ∧ 𝑌
⃗ ∧ (𝐴1 − 𝐴0 )
=𝑌

Consider in particular 𝑌 ⃗ , then


⃗ =𝑁

⃗ 2 (𝑃 ), 𝑁
∇ 𝑃 (𝑁 ⃗) = 𝑁
⃗ ∧ (𝐴1 − 𝐴0 )
=𝑁⃗ ∧ 𝐸2 ⃗ , 𝑃 ′)
= −det(𝐴1 − 𝐴0 , 𝑌

This leads to :math:` nabla_P lambda_2(P) = frac {(vec N wedge E_2)}{(vec N,vec N)} `
By similar calculations for 𝑁
⃗ 0 (𝑃 ) et 𝑁
⃗ 1 (𝑃 )
(𝑁⃗ ∧𝐸𝑖 )
∇𝑃 𝜆𝑖 (𝑃 ) = (𝑁⃗ ,𝑁
⃗)

Note: With the definition of the surface gradient and the 2d Pk-Lagrange FE used barycentric coordinates, surface
Pk-Langrange FE are trivial.

3.3. Finite element 199


FreeFEM Documentation, Release 4.8

3.3.8 P1 Nonconforming Element

Refer to [THOMASSET2012] for details; briefly, we now consider non-continuous approximations so we will lose the
property:

𝑤ℎ ∈ 𝑉ℎ ⊂ 𝐻 1 (Ω)

If we write:

1 Vh(Th, P1nc);
2 Vh fh = f(x.y);

then:
𝑛𝑣
∑︁
fh = 𝑓ℎ (𝑥, 𝑦) = 𝑓 (𝑚𝑖 )𝜑𝑖 (𝑥, 𝑦) (summation over all midpoint)
𝑖=1

Here the basis function 𝜑𝑖 associated with the mid-point 𝑚𝑖 = (𝑞 𝑘𝑖 + 𝑞 𝑘𝑖+1 )/2 where 𝑞 𝑘𝑖 is the 𝑖-th point in 𝑇𝑘 , and
we assume that 𝑗 + 1 = 0 if 𝑗 = 3:

𝜑𝑖 (𝑥, 𝑦) = 𝑎𝑘𝑖 + 𝑏𝑘𝑖 𝑥 + 𝑐𝑘𝑖 𝑦 for (𝑥, 𝑦) ∈ 𝑇𝑘 ,


𝜑𝑖 (𝑚𝑖 ) = 1, 𝜑𝑖 (𝑚𝑗 ) = 0 if 𝑖 ̸= 𝑗

Strictly speaking 𝜕𝜑𝑖 /𝜕𝑥, 𝜕𝜑𝑖 /𝜕𝑦 contain Dirac distribution 𝜌𝛿𝜕𝑇𝑘 .
The numerical calculations will automatically ignore them. In [THOMASSET2012], there is a proof of the estimation
(︃ 𝑛 ∫︁ )︃1/2
∑︁𝑣
2
|∇𝑤 − ∇𝑤ℎ | 𝑡𝑒𝑥𝑡𝑑𝑥𝑡𝑒𝑥𝑡𝑑𝑦 = 𝑂(ℎ)
𝑘=1 𝑇𝑘

The basis functions 𝜑𝑘 have the following properties.


1. For the bilinear form 𝑎 defined in Fig. 3.34a satisfy:

𝑎(𝜑𝑖 , 𝜑𝑖 ) > 0, 𝑎(𝜑𝑖 , 𝜑𝑗 ) ≤ 0 if 𝑖 ̸= 𝑗


∑︀ 𝑛𝑣
𝑘=1 𝑎(𝜑𝑖 , 𝜑𝑘 ) ≥ 0

2. 𝑓 ≥ 0 ⇒ 𝑢ℎ ≥ 0
3. If 𝑖 ̸= 𝑗, the basis function 𝜑𝑖 and 𝜑𝑗 are 𝐿2 -orthogonal:

∫︁
𝜑𝑖 𝜑𝑗 𝑡𝑒𝑥𝑡𝑑𝑥𝑡𝑒𝑥𝑡𝑑𝑦 = 0 if 𝑖 ̸= 𝑗
Ω

which is false for 𝑃1 -element.


See Fig. 3.34a for the projection of 𝑓 (𝑥, 𝑦) = sin(𝜋𝑥) cos(𝜋𝑦) into Vh(Th, P1nc).

200 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(b) Projection to Vh(Th, P1b)

(a) Projection to Vh(Th, P1nc)

Fig. 3.34: Finite elements P1nc, P1b

3.3.9 Other FE-space

For each triangle 𝑇𝑘 ∈ 𝒯ℎ , let 𝜆𝑘1 (𝑥, 𝑦), 𝜆𝑘2 (𝑥, 𝑦), 𝜆𝑘3 (𝑥, 𝑦) be the area cordinate of the triangle (see Fig. 3.30), and
put:

𝛽𝑘 (𝑥, 𝑦) = 27𝜆𝑘1 (𝑥, 𝑦)𝜆𝑘2 (𝑥, 𝑦)𝜆𝑘3 (𝑥, 𝑦)

called bubble function on 𝑇𝑘 . The bubble function has the feature: 1. 𝛽𝑘 (𝑥, 𝑦) = 0 if (𝑥, 𝑦) ∈ 𝜕𝑇𝑘 .
𝑞 𝑘1 +𝑞 𝑘2 +𝑞 𝑘3
2. 𝛽𝑘 (𝑞 𝑘𝑏 ) = 1 where 𝑞 𝑘𝑏 is the barycenter 3 .
If we write:

1 Vh(Th, P1b);
2 Vh fh = f(x.y);

then:
𝑛𝑣
∑︁ 𝑛𝑡
∑︁
fh = 𝑓ℎ (𝑥, 𝑦) = 𝑓 (𝑞 𝑖 )𝜑𝑖 (𝑥, 𝑦) + 𝑓 (𝑞 𝑘𝑏 )𝛽𝑘 (𝑥, 𝑦)
𝑖=1 𝑘=1

See Fig. 3.34b for the projection of 𝑓 (𝑥, 𝑦) = sin(𝜋𝑥) cos(𝜋𝑦) into Vh(Th, P1b).

3.3. Finite element 201


FreeFEM Documentation, Release 4.8

3.3.10 Vector Valued FE-function

Functions from R2 to R𝑁 with 𝑁 = 1 are called scalar functions and called vector valued when 𝑁 > 1. When 𝑁 = 2

1 fespace Vh(Th, [P0, P1]) ;

makes the space

𝑉ℎ = {w = (𝑤1 , 𝑤2 )| 𝑤1 ∈ 𝑉ℎ (𝒯ℎ , 𝑃0 ), 𝑤2 ∈ 𝑉ℎ (𝒯ℎ , 𝑃1 )}

Raviart-Thomas Element

In the Raviart-Thomas finite element 𝑅𝑇∫︀0ℎ , the degrees of freedom are the fluxes across edges 𝑒 of the mesh, where
the flux of the function f : R2 −→ R2 is 𝑒 f .𝑛𝑒 , 𝑛𝑒 is the unit normal of edge 𝑒.
This implies an orientation of all the edges of the mesh, for example we can use the global numbering of the edge
vertices and we just go from small to large numbers.
To compute the flux, we use a quadrature with one Gauss point, the mid-point of the edge.
Consider a triangle 𝑇𝑘 with three vertices (a, b, c).
Lets denote the vertices numbers by 𝑖𝑎 , 𝑖𝑏 , 𝑖𝑐 , and define the three edge vectors e1 , e2 , e3 by 𝑠𝑔𝑛(𝑖𝑏 − 𝑖𝑐 )(b − c),
𝑠𝑔𝑛(𝑖𝑐 − 𝑖𝑎 )(c − a), 𝑠𝑔𝑛(𝑖𝑎 − 𝑖𝑏 )(a − b).
We get three basis functions:

𝑠𝑔𝑛(𝑖𝑏 − 𝑖𝑐 ) 𝑠𝑔𝑛(𝑖𝑐 − 𝑖𝑎 ) 𝑠𝑔𝑛(𝑖𝑎 − 𝑖𝑏 )


𝜑𝑘1 = (x − a), 𝜑𝑘2 = (x − b), 𝜑𝑘3 = (x − c),
2|𝑇𝑘 | 2|𝑇𝑘 | 2|𝑇𝑘 |

where |𝑇𝑘 | is the area of the triangle 𝑇𝑘 . If we write:

1 Vh(Th, RT0);
2 Vh [f1h, f2h] = [f1(x, y), f2(x, y)];

then:
𝑛𝑡 ∑︁
∑︁ 6
fh = fℎ (𝑥, 𝑦) = 𝑛𝑖𝑙 𝑗𝑙 |eil |𝑓𝑗𝑙 (𝑚𝑖𝑙 )𝜑𝑖𝑙 𝑗𝑙
𝑘=1 𝑙=1

where 𝑛𝑖𝑙 𝑗𝑙 is the 𝑗𝑙 -th component of the normal vector n𝑖𝑙 ,


{︂ }︂
b+c a+c b+a
{𝑚1 , 𝑚2 , 𝑚3 } = , ,
2 2 2

and 𝑖𝑙 = {1, 1, 2, 2, 3, 3}, 𝑗𝑙 = {1, 2, 1, 2, 1, 2} with the order of 𝑙.

1 // Mesh
2 mesh Th = square(2, 2);
3

4 // Fespace
5 fespace Xh(Th, P1);
6 Xh uh = x^2 + y^2, vh;
7

8 fespace Vh(Th, RT0);


9 Vh [Uxh, Uyh] = [sin(x), cos(y)]; //vectorial FE function
10

(continues on next page)

202 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

Fig. 3.35: Normal vectors of each edge

(continued from previous page)


11 // Change the mesh
12 Th = square(5,5);
13

14 //Xh is unchanged
15 //Uxh = x; //error: impossible to set only 1 component
16 //of a vector FE function
17 vh = Uxh;//ok
18 //and now vh use the 5x5 mesh
19 //but the fespace of vh is always the 2x2 mesh
20

21 // Plot
22 plot(uh);
23 uh = uh; //do a interpolation of uh (old) of 5x5 mesh
24 //to get the new uh on 10x10 mesh
25 plot(uh);
26

27 vh([x-1/2, y]) = x^2 + y^2; //interpolate vh = ((x-1/2)^2 + y^2)

To get the value at a point 𝑥 = 1, 𝑦 = 2 of the FE function uh, or [Uxh, Uyh], one writes:

1 real value;
2 value = uh(2,4); //get value = uh(2, 4)
3 value = Uxh(2, 4); //get value = Uxh(2, 4)
4 //OR
5 x = 1; y = 2;
6 value = uh; //get value = uh(1, 2)
7 value = Uxh; //get value = Uxh(1, 2)
(continues on next page)

3.3. Finite element 203


FreeFEM Documentation, Release 4.8

(a) vh Iso on mesh 2 × 2 (b) vh Iso on mesh 5 × 5

(continued from previous page)


8 value = Uyh; //get value = Uyh(1, 2)

To get the value of the array associated to the FE function uh, one writes

1 real value = uh[][0]; //get the value of degree of freedom 0


2 real maxdf = uh[].max; //maximum value of degree of freedom
3 int size = uh.n; //the number of degree of freedom
4 real[int] array(uh.n) = uh[]; //copy the array of the function uh

Warning: For a non-scalar finite element function [Uxh, Uyh] the two arrays Uxh[] and Uyh[] are the same
array, because the degree of freedom can touch more than one component.

3.3.11 A Fast Finite Element Interpolator

In practice, one may discretize the variational equations by the Finite Element method. Then there will be one mesh
for Ω1 and another one for Ω2 . The computation of integrals of products of functions defined on different meshes is
difficult.
Quadrature formula and interpolations from one mesh to another at quadrature points are needed. We present below
the interpolation operator which we have used and which is new, to the best of our knowledge.
Let 𝒯ℎ0 = ∪𝑘 𝑇𝑘0 , 𝒯ℎ1 = ∪𝑘 𝑇𝑘1 be two triangulations of a domain Ω. Let:

𝑉 (T𝑖ℎ ) = {𝐶 0 (Ω𝑖ℎ ) : 𝑓 |𝑇𝑘𝑖 ∈ 𝑃0 }, 𝑖 = 0, 1

be the spaces of continuous piecewise affine functions on each triangulation.


Let 𝑓 ∈ 𝑉 (𝒯ℎ0 ). The problem is to find 𝑔 ∈ 𝑉 (𝒯ℎ1 ) such that:

𝑔(𝑞) = 𝑓 (𝑞) ∀𝑞 vertex of 𝒯ℎ1

Although this is a seemingly simple problem, it is difficult to find an efficient algorithm in practice.

204 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8


We propose an algorithm which is of complexity 𝑁 1 log 𝑁 0 , where 𝑁 𝑖 is the number of vertices of 𝒯⟨ , and which is
very fast for most practical 2D applications.
Algorithm
The method has 5 steps.
First a quadtree is built containing all the vertices of the mesh 𝒯ℎ0 such that in each terminal cell there are at least one,
and at most 4, vertices of 𝒯ℎ0 .
For each 𝑞 1 , vertex of 𝒯ℎ1 do:
1. Find the terminal cell of the quadtree containing 𝑞 1 .
2. Find the the nearest vertex 𝑞𝑗0 to 𝑞 1 in that cell.
3. Choose one triangle 𝑇𝑘0 ∈ 𝒯ℎ0 which has 𝑞𝑗0 for vertex.
4. Compute the barycentric coordinates {𝜆𝑗 }𝑗=1,2,3 of 𝑞 1 in 𝑇𝑘0 .
• if all barycentric coordinates are positive, go to Step 5
• otherwise, if one barycentric coordinate 𝜆𝑖 is negative, replace 𝑇𝑘0 by the adjacent triangle opposite 𝑞𝑖0 and
go to Step 4.
• otherwise, if two barycentric coordinates are negative, take one of the two randomly and replace 𝑇𝑘0 by the
adjacent triangle as above.
5. Calculate 𝑔(𝑞 1 ) on 𝑇𝑘0 by linear interpolation of 𝑓 :

∑︁
𝑔(𝑞 1 ) = 𝜆𝑗 𝑓 (𝑞𝑗0 )
𝑗=1,2,3

Fig. 3.37: To interpolate a function at 𝑞 0 , the knowledge of the triangle which contains 𝑞 0 is needed. The algorithm
may start at 𝑞 1 ∈ 𝑇𝑘0 and stall on the boundary (thick line) because the line 𝑞 0 𝑞 1 is not inside Ω. But if the holes are
triangulated too (doted line) then the problem does not arise.

Two problems need to be solved:


• What if :math:`q^1` is not in Ω0ℎ ? Then Step 5 will stop with a boundary triangle.
So we add a step which tests the distance of 𝑞 1 with the two adjacent boundary edges and selects the
nearest, and so on till the distance grows.

3.3. Finite element 205


FreeFEM Documentation, Release 4.8

• What if Ω0ℎ is not convex and the marching process of Step 4 locks on a boundary? By construction Delaunay-
Voronoï’s mesh generators always triangulate the convex hull of the vertices of the domain.
Therefore, we make sure that this information is not lost when 𝒯ℎ0 , 𝒯ℎ1 are constructed and we keep
the triangles which are outside the domain on a special list.
That way, in step 5 we can use that list to step over holes if needed.

Note: Sometimes, in rare cases, the interpolation process misses some points, we can change the search algorithm
through a global variable searchMethod

1 searchMethod = 0; // default value for fast search algorithm


2 searchMethod = 1; // safe search algorithm, uses brute force in case of missing point
3 // (warning: can be very expensive in cases where a lot of points are outside of the␣
˓→domain)

4 searchMethod = 2; // always uses brute force. It is very computationally expensive.

Note: Step 3 requires an array of pointers such that each vertex points to one triangle of the triangulation.

Note: The operator = is the interpolation operator of FreeFEM, the continuous finite functions are extended by
continuity to the outside of the domain.
Try the following example :

1 // Mesh
2 mesh Ths = square(10, 10);
3 mesh Thg = square(30, 30, [x*3-1, y*3-1]);
4 plot(Ths, Thg, wait=true);
5

6 // Fespace
7 fespace Ch(Ths, P2);
8 Ch us = (x-0.5)*(y-0.5);
9

10 fespace Dh(Ths, P2dc);


11 Dh vs = (x-0.5)*(y-0.5);
12

13 fespace Fh(Thg, P2dc);


14 Fh ug=us, vg=vs;
15

16 // Plot
17 plot(us, ug, wait=true);
18 plot(vs, vg, wait=true);

206 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(a) Extension of a continuous FE-function (b) Extension of discontinuous FE-function

Fig. 3.38: Extension of FE-function

3.3.12 Keywords: Problem and Solve

For FreeFEM, a problem must be given in variational form, so we need a bilinear form 𝑎(𝑢, 𝑣), a linear form ℓ(𝑓, 𝑣),
and possibly a boundary condition form must be added.

1 problem P (u, v)
2 = a(u,v) - l(f,v)
3 + (boundary condition)
4 ;

Note: When you want to formulate the problem and solve it in the same time, you can use the keyword solve.

Weak Form and Boundary Condition

To present the principles of Variational Formulations, also called weak form, for the Partial Differential Equations, let’s
take a model problem: a Poisson equation with Dirichlet and Robin Boundary condition.
The problem: Find 𝑢 a real function defined on a domain Ω of R𝑑 (𝑑 = 2, 3) such that:

−∇ · (𝜅∇𝑢) = 𝑓 in Ω
𝜕𝑢
𝑎𝑢 + 𝜅 𝜕𝑛 = 𝑏 on Γ𝑟
𝑢 = 𝑔 on Γ𝑑

where:
• if 𝑑 = 2 then ∇.(𝜅∇𝑢) = 𝜕𝑥 (𝜅𝜕𝑥 𝑢) + 𝜕𝑦 (𝜅𝜕𝑦 𝑢) with 𝜕𝑥 𝑢 = 𝜕𝑢
𝜕𝑥 and 𝜕𝑦 𝑢 = 𝜕𝑢
𝜕𝑦

• if 𝑑 = 3 then ∇.(𝜅∇𝑢) = 𝜕𝑥 (𝜅𝜕𝑥 𝑢) + 𝜕𝑦 (𝜅𝜕𝑦 𝑢) + 𝜕𝑧 (𝜅𝜕𝑧 𝑢) with 𝜕𝑥 𝑢 = 𝜕𝑥 ,


𝜕𝑢
𝜕𝑦 𝑢 = 𝜕𝑢
𝜕𝑦 and , 𝜕𝑧 𝑢 = 𝜕𝑢
𝜕𝑧

• The border Γ = 𝜕Ω is split in Γ𝑑 and Γ𝑛 such that Γ𝑑 ∩ Γ𝑛 = ∅ and Γ𝑑 ∪ Γ𝑛 = 𝜕Ω,


• 𝜅 is a given positive function, such that ∃𝜅0 ∈ R, 0 < 𝜅0 ≤ 𝜅.
• 𝑎 a given non negative function,
• 𝑏 a given function.

Note: This is the well known Neumann boundary condition if 𝑎 = 0, and if Γ𝑑 is empty.

3.3. Finite element 207


FreeFEM Documentation, Release 4.8

In this case the function appears in the problem just by its derivatives, so it is defined only up to a constant (if 𝑢 is a
solution then 𝑢 + 𝑐 is also a solution).

Let 𝑣, a regular test function, null on Γ𝑑 , by integration by parts we get:


∫︁ ∫︁ ∫︁ ∫︁
𝜕𝑢
− ∇ · (𝜅∇𝑢) 𝑣 𝑑𝜔 = 𝜅∇𝑣 · ∇𝑢 𝑑𝜔 − 𝑣𝜅 𝑑𝛾, = 𝑓 𝑣 𝑑𝜔
Ω Ω Γ 𝜕n Ω

where if 𝑑 = 2 the ∇𝑣.∇𝑢 = ( 𝜕𝑢 𝜕𝑣


𝜕𝑥 𝜕𝑥 +
𝜕𝑢 𝜕𝑣
𝜕𝑦 𝜕𝑦 ),

where if 𝑑 = 3 the ∇𝑣.∇𝑢 = ( 𝜕𝑢 𝜕𝑣


𝜕𝑥 𝜕𝑥 +
𝜕𝑢 𝜕𝑣
𝜕𝑦 𝜕𝑦 + 𝜕𝑢 𝜕𝑣
𝜕𝑧 𝜕𝑧 ),

and where n is the unitary outer-pointing normal of the Γ.


Now we note that 𝜅 𝜕𝑛
𝜕𝑢
= −𝑎𝑢 + 𝑏 on Γ𝑟 and 𝑣 = 0 on Γ𝑑 and Γ = Γ𝑑 ∪ Γ𝑛 thus:
∫︁ ∫︁ ∫︁
𝜕𝑢
− 𝑣𝜅 = 𝑎𝑢𝑣 − 𝑏𝑣
Γ 𝜕𝑛 Γ𝑟 Γ𝑟

The problem becomes:


Find 𝑢 ∈ 𝑉𝑔 = {𝑤 ∈ 𝐻 1 (Ω)/𝑤 = 𝑔 on Γ𝑑 } such that:
∫︁ ∫︁ ∫︁ ∫︁
𝜅∇𝑣.∇𝑢 𝑑𝜔 + 𝑎𝑢𝑣 𝑑𝛾 = 𝑓 𝑣 𝑑𝜔 + 𝑏𝑣 𝑑𝛾, ∀𝑣 ∈ 𝑉0 (3.16)
Ω Γ𝑟 Ω Γ𝑟

where 𝑉0 = {𝑣 ∈ 𝐻 1 (Ω)/𝑣 = 0 on Γ𝑑 }
Except in the case of Neumann conditions everywhere, the problem (3.16) is well posed when 𝜅 ≥ 𝜅0 > 0.

Note: If we have only the Neumann boundary condition, linear algebra tells us that the right hand side must be
orthogonal to the kernel of the operator for the solution to exist.
One way of writing the compatibility condition is:
∫︁ ∫︁
𝑓 𝑑𝜔 + 𝑏 𝑑𝛾 = 0
Ω Γ

and a way to fix the constant is to solve for 𝑢 ∈ 𝐻 1 (Ω) such that:
∫︁ ∫︁ ∫︁
(𝜀𝑢𝑣 + 𝜅∇𝑣.∇𝑢) 𝑑𝜔 = 𝑓 𝑣 𝑑𝜔 + 𝑏𝑣 𝑑𝛾, ∀𝑣 ∈ 𝐻 1 (Ω)
Ω Ω Γ𝑟

2
where 𝜀 is a small parameter (∼ 𝜅 10−10 |Ω| ). 𝑑

Remark that
∫︀ if the solution is of order 𝜀 then the compatibility condition is unsatisfied, otherwise we get the solution
1

such that Ω 𝑢 = 0, you can also add a Lagrange multiplier to solve the real mathematical problem like in the Lagrange
multipliers example.

In FreeFEM, the bidimensional problem (3.16) becomes:

1 problem Pw (u, v)
2 = int2d(Th)( //int_{Omega} kappa nabla v . nabla u
3 kappa*(dx(u)*dx(v) + dy(u)*dy(v))
4 )
5 + int1d(Th, gn)( //int_{Gamma_r} a u v
6 a * u*v
(continues on next page)

208 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


7 )
8 - int2d(Th)( //int_{Omega} f v
9 f*v
10 )
11 - int1d(Th, gn)( //int_{Gamma_r} b v
12 b * v
13 )
14 + on(gd, u=g) //u = g on Gamma_d
15 ;

where Th is a mesh of the bi-dimensional domain Ω, and gd and gn are respectively the boundary labels of boundary
Γ𝑑 and Γ𝑛 .
And the three dimensional problem (3.16) becomes

1 macro Grad(u) [dx(u), dy(u), dz(u) ]//


2 problem Pw (u, v)
3 = int3d(Th)( //int_{Omega} kappa nabla v . nabla u
4 kappa*(Grad(u)'*Grad(v))
5 )
6 + int2d(Th, gn)( //int_{Gamma_r} a u v
7 a * u*v
8 )
9 - int3d(Th)( //int_{Omega} f v
10 f*v
11 )
12 - int2d(Th, gn)( //int_{Gamma_r} b v
13 b * v
14 )
15 + on(gd, u=g) //u = g on Gamma_d
16 ;

where Th is a mesh of the three dimensional domain Ω, and gd and gn are respectively the boundary labels of boundary
Γ𝑑 and Γ𝑛 .

3.3.13 Parameters affecting solve and problem

The parameters are FE functions real or complex, the number 𝑛 of parameters is even (𝑛 = 2 * 𝑘), the 𝑘 first function
parameters are unknown, and the 𝑘 last are test functions.

Note: If the functions are a part of vectorial FE then you must give all the functions of the vectorial FE in the same
order (see Poisson problem with mixed finite element for example).

Note: Don’t mix complex and real parameters FE function.

Warning: Bug:
The mixing of multiple fespace with different periodic boundary conditions are not implemented.

3.3. Finite element 209


FreeFEM Documentation, Release 4.8

So all the finite element spaces used for tests or unknown functions in a problem, must have the same type of
periodic boundary conditions or no periodic boundary conditions.
No clean message is given and the result is unpredictable.

The parameters are:


• solver= LU, CG, Crout, Cholesky, GMRES, sparsesolver, UMFPACK . . .
The default solver is sparsesolver (it is equal to UMFPACK if no other sparse solver is defined) or is
set to LU if no direct sparse solver is available.
The storage mode of the matrix of the underlying linear system depends on the type of solver chosen;
for LU the matrix is sky-line non symmetric, for Crout the matrix is sky-line symmetric, for Cholesky
the matrix is sky-line symmetric positive definite, for CG the matrix is sparse symmetric positive, and
for GMRES, sparsesolver or UMFPACK the matrix is just sparse.
• eps= a real expression.
𝜀 sets the stopping test for the iterative methods like CG.
Note that if 𝜀 is negative then the stopping test is:

||𝐴𝑥 − 𝑏|| < |𝜀|

if it is positive, then the stopping test is:

|𝜀|
||𝐴𝑥 − 𝑏|| <
||𝐴𝑥0 − 𝑏||

• init= boolean expression, if it is false or 0 the matrix is reconstructed.


Note that if the mesh changes the matrix is reconstructed too.
• precon= name of a function (for example P) to set the preconditioner.
The prototype for the function P must be:

1 func real[int] P(real[int] & xx);

• tgv= Huge value (1030 ) used to implement Dirichlet boundary conditions.


• tolpivot= sets the tolerance of the pivot in UMFPACK (10−1 ) and, LU, Crout, Cholesky factorisation (10−20 ).
• tolpivotsym= sets the tolerance of the pivot sym in UMFPACK
• strategy= sets the integer UMFPACK strategy (0 by default).

3.3.14 Problem definition

Below v is the unknown function and w is the test function.


After the “=” sign, one may find sums of:
• Identifier(s); this is the name given earlier to the variational form(s) (type varf ) for possible reuse.
Remark, that the name in the varf of the unknown test function is forgotten, we use the order in the
argument list to recall names as in a C++ function,
• The terms of the bilinear form itself: if 𝐾 is a given function,

210 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• Bilinear part for 3D meshes Th


∑︁ ∫︁
– int3d(Th)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝑇

∑︁ ∫︁
– int3d(Th, 1)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω1 𝑇

∑︁ ∫︁
– int3d(Th, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝑇,𝜑<0

∑︁ ∫︁
– int3d(Th, l, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑<0

∑︁ ∫︁
– int2d(Th, 2, 5)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th (𝜕𝑇 ∪Γ)∩(Γ2 ∪Γ5 )

∑︁ ∫︁
– int2d(Th, 1)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω1 𝑇

∑︁ ∫︁
– int2d(Th, 2, 5)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th (𝜕𝑇 ∪Γ)∩(Γ2 ∪Γ5 )

∑︁ ∫︁
– int2d(Th, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝑇,𝜑=0

∑︁ ∫︁
– int2d(Th, l, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑=0

∑︁ ∫︁
– intallfaces(Th)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝜕𝑇

∑︁ ∫︁
– intallfaces(Th, 1)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω1 𝜕𝑇

– They contribute to the sparse matrix of type matrix which, whether declared explicitly or not, is con-
structed by FreeFEM.
• Bilinear part for 2D meshes Th
∑︁ ∫︁
– int2d(Th)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝑇

∑︁ ∫︁
– int2d(Th, 1)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω1 𝑇

∑︁ ∫︁
– int2d(Th, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝑇,𝜑<0

∑︁ ∫︁
– int2d(Th, l, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑<0

∑︁ ∫︁
– int1d(Th, 2, 5)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th (𝜕𝑇 ∪Γ)∩(Γ2 ∪Γ5 )

∑︁ ∫︁
– int1d(Th, 1)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω1 𝑇

3.3. Finite element 211


FreeFEM Documentation, Release 4.8

∑︁ ∫︁
– int1d(Th, 2, 5)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th (𝜕𝑇 ∪Γ)∩(Γ2 ∪Γ5 )

∑︁ ∫︁
– int1d(Th, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝑇,𝜑=0

∑︁ ∫︁
– int1d(Th, l, levelset=phi)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑=0

∑︁ ∫︁
– intalledges(Th)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th 𝜕𝑇

∑︁ ∫︁
– intalledges(Th, 1)(K*v*w) = 𝐾𝑣𝑤
𝑇 ∈Th,𝑇 ⊂Ω1 𝜕𝑇

– They contribute to the sparse matrix of type matrix which, whether declared explicitly or not, is con-
structed by FreeFEM.
– The right hand-side of the Partial Differential Equation in 3D, the terms of the linear form: for given
functions 𝐾, 𝑓 :
∑︁ ∫︁
– int3d(Th)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇

∑︁ ∫︁
– int3d(Th, l)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ∈Ω𝑙 𝑇

∑︁ ∫︁
– int3d(Th, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇,𝜑<0

∑︁ ∫︁
– int3d(Th, l, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑<0

∑︁ ∫︁
– int2d(Th, 2, 5)(K*w) = 𝐾𝑤
𝑇 ∈Th (𝜕𝑇 ∪Γ)∩(Γ2 ∪Γ5 )

∑︁ ∫︁
– int2d(Th, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇,𝜑=0

∑︁ ∫︁
– int2d(Th, l, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑=0

∑︁ ∫︁
– intallfaces(Th)(f*w) = 𝑓𝑤
𝑇 ∈Th 𝜕𝑇

– A vector of type real[int]


• The right hand-side of the Partial Differential Equation in 2D, the terms of the linear form: for given functions
𝐾, 𝑓 :
∑︁ ∫︁
– int2d(Th)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇

∑︁ ∫︁
– int2d(Th, l)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ∈Ω𝑙 𝑇

∑︁ ∫︁
– int2d(Th, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇,𝜑<0

212 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

∑︁ ∫︁
– int2d(Th, l, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑<0

∑︁ ∫︁
– int1d(Th, 2, 5)(K*w) = 𝐾𝑤
𝑇 ∈Th (𝜕𝑇 ∪Γ)∩(Γ2 ∪Γ5 )

∑︁ ∫︁
– int1d(Th, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇,𝜑=0

∑︁ ∫︁
– int1d(Th, l, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ⊂Ω𝑙 𝑇,𝜑=0

∑︁ ∫︁
– intalledges(Th)(f*w) = 𝑓𝑤
𝑇 ∈Th 𝜕𝑇

– a vector of type real[int]


• The boundary condition terms:
– An “on” scalar form (for Dirichlet) : on(1, u=g)
Used for all degrees of freedom 𝑖 of the boundary referred by “1”, the diagonal term of the matrix
𝑎𝑖𝑖 = 𝑡𝑔𝑣 with the terrible giant value tgv (= 1030 by default), and the right hand side 𝑏[𝑖] =
”(Πℎ 𝑔)[𝑖]” × 𝑡𝑔𝑣, where the ”(Πℎ 𝑔)𝑔[𝑖]” is the boundary node value given by the interpolation
of 𝑔.
– A linear form on Γ (for Neumann in 2d) -int1d(Th)(f*w) or -int1d(Th, 3)(f*w)
– A bilinear form on Γ or Γ2 (for Robin in 2d) int1d(Th)(K*v*w) or int1d(Th,2)(K*v*w)
– A linear form on Γ (for Neumann in 3d) -int2d(Th)(f*w) or -int2d(Th, 3)(f*w)
– A bilinear form on Γ or Γ2 (for Robin in 3d) int2d(Th)(K*v*w) or int2d(Th,2)(K*v*w)

Note:
• An “on” vectorial form (for Dirichlet): on(1, u1=g1, u2=g2)
If you have vectorial finite element like RT0, the 2 components are coupled, and so you have : 𝑏[𝑖] = ”(Πℎ (𝑔1, 𝑔2))[𝑖]”×
𝑡𝑔𝑣, where Πℎ is the vectorial finite element interpolant.
• An “on” vectorial form (for Dirichlet): on(u=g, tgv= none positive value ) ,
if the value is equal to -2 (i.e tgv == -2 `) then we put to :math:`0 all term of the line and
colomn 𝑖 in the matrix, except diagonal term 𝑎𝑖𝑖 = 1, and 𝑏[𝑖] = ”(Πℎ 𝑔)[𝑖]” else if the value is equal
to -20 (i.e tgv == -20 `) then we put to :math:`0 all term of the line and colomn 𝑖 in the matrix,
and 𝑏[𝑖] = ”(Πℎ 𝑔)[𝑖]”
else if the value is equal to -10 (i.e tgv == -10 `) then we put to :math:`0 all term of the line the
matrix, and 𝑏[𝑖] = ”(Πℎ 𝑔)[𝑖]”
else (i.e tgv == -1 `) we put to :math:`0 all term of the line 𝑖 in the matrix, except diagonal term
𝑎𝑖𝑖 = 1, and 𝑏[𝑖] = ”(Πℎ 𝑔)[𝑖]”.
• If needed, the different kind of terms in the sum can appear more than once.
• The integral mesh and the mesh associated to test functions or unknown functions can be different in the case of
varf form.
• N.x, N.y and N.z are the normal’s components.
• Ns.x, Ns.y and Ns.z are the normal’s components of the suface in case of meshS integral

3.3. Finite element 213


FreeFEM Documentation, Release 4.8

• Tl.x, Tl.y and Tl.z are the tangent’s components of the line in case of meshL integral

Warning: It is not possible to write in the same integral the linear part and the bilinear part such as in
int1d(Th)(K*v*w - f*w).

3.3.15 Numerical Integration

Let 𝐷 be a 𝑁 -dimensional bounded domain.


For an arbitrary polynomial 𝑓 of degree 𝑟, if we can find particular (quadrature) points 𝜉𝑗 , 𝑗 = 1, · · · , 𝐽 in 𝐷 and
(quadrature) constants 𝜔𝑗 such that
∫︁ 𝐿
∑︁
𝑓 (x) = 𝑐ℓ 𝑓 (𝜉ℓ )
𝐷 ℓ=1

then we have an error estimate (see [CROUZEIX1984]), and then there exists a constant 𝐶 > 0 such that
⃒∫︁ 𝐿

⃒ ∑︁ ⃒
⃒ 𝑓 (x) − 𝜔ℓ 𝑓 (𝜉ℓ )⃒ ≤ 𝐶|𝐷|ℎ𝑟+1
⃒ ⃒
⃒ 𝐷 ⃒
ℓ=1

for any function 𝑟 + 1 times continuously differentiable 𝑓 in 𝐷, where ℎ is the diameter of 𝐷 and |𝐷| its measure (a
point in the segment [𝑞 𝑖 𝑞 𝑗 ] is given as

{(𝑥, 𝑦)| 𝑥 = (1 − 𝑡)𝑞𝑥𝑖 + 𝑡𝑞𝑥𝑗 , 𝑦 = (1 − 𝑡)𝑞𝑦𝑖 + 𝑡𝑞𝑦𝑗 , 0 ≤ 𝑡 ≤ 1}


∑︀𝑛𝑡
For a domain Ωℎ = 𝑘=1 𝑇𝑘 , 𝒯ℎ = {𝑇𝑘 }, we can calculate the integral over Γℎ = 𝜕Ωℎ by:
𝑓 (x)𝑑𝑠 =int1d(Th)(f) =int1d(Th, qfe=*)(f) =int1d(Th, qforder=*)(f)
∫︀
Γℎ

where * stands for the name of the quadrature formula or the precision (order) of the Gauss formula.

Quadrature formula on an edge


𝐿 qfe qforder Point in [𝑞 𝑖 , 𝑞 𝑗 ] 𝜔ℓ Exact on
𝑃𝑘 , 𝑘 =
1 qf1pE 2 1/2 √︀ ||𝑞 𝑖 𝑞 𝑗 || 1
2 qf2pE 3 (1 ± √︀1/3)/2 ||𝑞 𝑖 𝑞 𝑗 ||/2 3
3 qf3pE 6 (1 ± 3/5)/2 (5/18)||𝑞 𝑖 𝑞 𝑗 || 5
𝑖 𝑗
1/2 √
(8/18)||𝑞

𝑞 ||
4 qf4pE 8 (1 ± 525+70 30
35 √ )/2
18− 30
72 ||𝑞 𝑖 𝑞 𝑗 || 7

(1 ± 525−70 30
35 √ )/2
18+ 30
72 √||𝑞 𝑞 ||
𝑖 𝑗

245+14 70 322−13 70
5 qf5pE 10 (1 ± 21 )/2 1800 ||𝑞 𝑖 𝑞 𝑗 || 9
64 𝑖 𝑗
1/2 √ 225 ||𝑞 √ 𝑞 ||
(1 ± 245−14
21
70
)/2 322+13 70
1800 ||𝑞 𝑖 𝑞 𝑗 ||
𝑖 𝑗
2 qf1pElump2 0 ||𝑞 𝑞 ||/2 1
1 ||𝑞 𝑖 𝑞 𝑗 ||/2

where |𝑞 𝑖 𝑞 𝑗 | is the length of segment 𝑞 𝑖 𝑞 𝑗 .


For a part Γ1 of Γℎ with the label “1”, we can calculate the integral over Γ1 by:
𝑓 (𝑥, 𝑦)𝑑𝑠 =int1d(Th, 1)(f) =int1d(Th, 1, qfe=qf2pE)(f)
∫︀
Γ1

214 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

The integrals over Γ1 , Γ3 are given by:


∫︀
Γ1 ∪Γ3
𝑓 (𝑥, 𝑦)𝑑𝑠

For each triangle 𝑇𝑘 = [𝑞 𝑘1 𝑞 𝑘2 𝑞 𝑘3 ], the point 𝑃 (𝑥, 𝑦) in 𝑇𝑘 is expressed by the area coordinate as 𝑃 (𝜉, 𝜂):
⃒ 1 𝑞𝑥𝑘1 𝑞𝑦𝑘1 ⃒
⃒ ⃒ ⃒ ⃒ ⃒ ⃒ ⃒ ⃒
⃒ 1 𝑥 𝑦 ⃒⃒ ⃒ 1 𝑞𝑥𝑘1 𝑞𝑦𝑘1 ⃒ ⃒ 1 𝑞𝑥𝑘1 𝑞𝑦𝑘1 ⃒
1 ⃒⃒ ⃒ ⃒ ⃒ ⃒ ⃒ ⃒
|𝑇𝑘 | = ⃒ 1 𝑞𝑥𝑘2 𝑞𝑦𝑘2 ⃒⃒ 𝐷1 = ⃒⃒ 1 𝑞𝑥𝑘2 𝑞𝑦𝑘2 ⃒⃒ 𝐷2 = ⃒⃒ 1 𝑥 𝑦 ⃒⃒ 𝐷3 = ⃒⃒ 1 𝑞𝑥𝑘2 𝑞𝑦𝑘2 ⃒⃒
2⃒ ⃒ 1 𝑞𝑥𝑘3 𝑞𝑦𝑘3 ⃒ ⃒ 1 𝑞𝑥𝑘3 𝑞𝑦𝑘3 ⃒
1 𝑞𝑥𝑘3 𝑞𝑦𝑘3 ⃒ ⃒ 1 𝑥 𝑦 ⃒
1 1 1
𝜉 = 𝐷1 /|𝑇𝑘 | 𝜂 = 𝐷2 /|𝑇𝑘 | then 1 − 𝜉 − 𝜂 = 𝐷3 /|𝑇𝑘 |
2 2 2
∑︀𝑛𝑡
For a two dimensional domain or a border of three dimensional domain Ωℎ = 𝑘=1 𝑇𝑘 , 𝒯ℎ = {𝑇𝑘 }, we can calculate
the integral over Ωℎ by:
𝑓 (𝑥, 𝑦) =int2d(Th)(f) =int2d(Th, qft=*)(f) =int2d(Th, qforder=*)(f)
∫︀
Ωℎ

where * stands for the name of quadrature formula or the order of the Gauss formula.

Quadrature formula on a triangle


𝐿 qft qforder Point in 𝑇𝑘 𝜔ℓ Ex-
act on
𝑃𝑘 , 𝑘 =
1 qf1pT 2
(︀ 1 1 )︀
(︀ 13 , 31 )︀ |𝑇𝑘 | 1
3 qf2pT 3 (︀ 21 , 2)︀ |𝑇𝑘 |/3 2
|𝑇𝑘 |/3
(︀ 2 , 10)︀
|𝑇𝑘 |/3
(︀0, 2 )︀
7 qf5pT 6 1 1
(︁ 3√
3 , 0.225|𝑇
√𝑘
| 5
√ )︁
6− 15 6− 15 (155− 15)|𝑇𝑘 |
21 , 21 1200

(︁ √ √ )︁ (155− 15)|𝑇𝑘 |
6− 15 9+2 15
, 1200

(︁ 21√ 21
√ )︁ (155− 15)|𝑇𝑘 |
9+2 15 6− 15 1200

21 , 21 (155+ 15)|𝑇𝑘 |
(︁ √ √ )︁ 1200
6+ 15 6+ 15 √
, 21 (155+ 15)|𝑇𝑘 |
(︁ 21√ √ )︁ 1200

6+ 15 9−2 15 (155+ 15)|𝑇𝑘 |
21 , 21 1200
(︁ √ √ )︁
9−2 15 6+ 15
21 , 21
3 qf1pTlump (0, 0) |𝑇𝑘 |/3 1
(1, 0) |𝑇𝑘 |/3
(0,
(︀ 1 1) |𝑇𝑘 |/3
9 3
)︀
qf2pT4P1 (︀ 43 , 41 )︀ |𝑇𝑘 |/12 1
|𝑇𝑘 |/12
(︀ 4 , 14)︀
|𝑇𝑘 |/12
(︀0, 43 )︀
|𝑇𝑘 |/12
(︀0,1
4 )︀
|𝑇𝑘 |/12
(︀ 43 , 0)︀ |𝑇𝑘 |/12
(︀ 41 , 01 )︀ |𝑇𝑘 |/6
(︀ 41 , 41 )︀ |𝑇𝑘 |/6
(︀ 41 , 21 )︀ |𝑇𝑘 |/6
2, 4
15 qf7pT 8 See [TAYLOR2005] for detail 7
21 qf9pT 10 See [TAYLOR2005] for detail 9
∑︀𝑛𝑡
For a three dimensional domain Ωℎ = 𝑘=1 𝑇𝑘 , 𝒯ℎ = {𝑇𝑘 }, we can calculate the integral over Ωℎ by:
𝑓 (𝑥, 𝑦) =int3d(Th)(f) =int3d(Th,qfV=*)(f) =int3D(Th,qforder=*)(f)
∫︀
Ωℎ

3.3. Finite element 215


FreeFEM Documentation, Release 4.8

where * stands for the name of quadrature formula or the order of the Gauss formula.

Quadrature formula on a tetrahedron


𝐿 qfV qforderPoint in 𝑇𝑘 ∈ R3 𝜔ℓ Exact on
𝑃𝑘 , 𝑘 =
1
(︀ 1 1 1
)︀
qfV1 2 4, 4, 4 |𝑇𝑘 | 1
4 qfV2 3 𝐺4(0.58 . . . , 0.13 . . . , 0.13 . . .) |𝑇𝑘 |/4 2
14 qfV5 6 𝐺4(0.72 . . . , 0.092 . . . , 0.092 . . .) 0.073 . . . |𝑇𝑘 | 5
𝐺4(0.067 . . . , 0.31 . . . , 0.31 . . .) 0.11 . . . |𝑇𝑘 |
𝐺6(0.45 . . . , 0.045 . . . , 0.45 . . .) 0.042 . . . |𝑇𝑘 |
4 qfV1lump 𝐺4(1, 0, 0) |𝑇𝑘 |/4 1

Where 𝐺4(𝑎, 𝑏, 𝑏) such that 𝑎 + 3𝑏 = 1 is the set of the four point in barycentric coordinate:

{(𝑎, 𝑏, 𝑏, 𝑏), (𝑏, 𝑎, 𝑏, 𝑏), (𝑏, 𝑏, 𝑎, 𝑏), (𝑏, 𝑏, 𝑏, 𝑎)}

and where 𝐺6(𝑎, 𝑏, 𝑏) such that 2𝑎 + 2𝑏 = 1 is the set of the six points in barycentric coordinate:

{(𝑎, 𝑎, 𝑏, 𝑏), (𝑎, 𝑏, 𝑎, 𝑏), (𝑎, 𝑏, 𝑏, 𝑎), (𝑏, 𝑏, 𝑎, 𝑎), (𝑏, 𝑎, 𝑏, 𝑎), (𝑏, 𝑎, 𝑎, 𝑏)}

Note: These tetrahedral quadrature formulae come from http://nines.cs.kuleuven.be/research/ecf/mtables.html

Note: By default, we use the formula which is exact for polynomials of degree 5 on triangles or edges (in bold in three
tables).

It is possible to add an own quadrature formulae with using plugin qf11to25 on segment, triangle or Tetrahedron.
The quadrature formulae in 𝐷 dimension is a bidimentional array of size 𝑁𝑞 × (𝐷 + 1) such that the 𝐷 + 1 value of on
∑︀𝐷
row 𝑖 = 0, ..., 𝑁𝑝 − 1 are 𝑤𝑖 , 𝑥 ˆ𝑖𝐷 where 𝑤𝑖 is the weight of the quadrature point, and 1 − 𝑘=1 𝑥
ˆ𝑖1 , ..., 𝑥 ˆ𝑖𝑘 , 𝑥
ˆ𝑖1 , ..., 𝑥
ˆ𝑖𝐷
is the barycentric coordinate the quadrature point.

1 load "qf11to25"
2

3 // Quadrature on segment
4 real[int, int] qq1 = [
5 [0.5, 0],
6 [0.5, 1]
7 ];
8

9 QF1 qf1(1, qq1); //def of quadrature formulae qf1 on segment


10 //remark:
11 //1 is the order of the quadrature exact for polynome of degree < 1
12

13 //Quadrature on triangle
14 real[int, int] qq2 = [
15 [1./3., 0, 0],
16 [1./3., 1, 0],
17 [1./3., 0, 1]
18 ];
19

(continues on next page)

216 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


20 QF2 qf2(1, qq2); //def of quadrature formulae qf2 on triangle
21 //remark:
22 //1 is the order of the quadrature exact for polynome of degree < 1
23 //so must have sum w^i = 1
24

25 // Quadrature on tetrahedron
26 real[int, int] qq3 = [
27 [1./4., 0, 0, 0],
28 [1./4., 1, 0, 0],
29 [1./4., 0, 1, 0],
30 [1./4., 0, 0, 1]
31 ];
32

33 QF3 qf3(1, qq3); //def of quadrature formulae qf3 on get


34 //remark:
35 //1 is the order of the quadrature exact for polynome of degree < 1)
36

37 // Verification in 1d and 2d
38 mesh Th = square(10, 10);
39

40 real I1 = int1d(Th, qfe=qf1)(x^2);


41 real I1l = int1d(Th, qfe=qf1pElump)(x^2);
42

43 real I2 = int2d(Th, qft=qf2)(x^2);


44 real I2l = int2d(Th, qft=qf1pTlump)(x^2);
45

46 cout << I1 << " == " << I1l << endl;


47 cout << I2 << " == " << I2l << endl;
48 assert( abs(I1-I1l) < 1e-10 );
49 assert( abs(I2-I2l) < 1e-10 );

The output is

1 1.67 == 1.67
2 0.335 == 0.335

3.3.16 Variational Form, Sparse Matrix, PDE Data Vector

In FreeFEM it is possible to define variational forms, and use them to build matrices and vectors, and store them to
speed-up the script (4 times faster here).
For example let us solve the Thermal Conduction problem.
The variational formulation is in 𝐿2 (0, 𝑇 ; 𝐻 1 (Ω)); we shall seek 𝑢𝑛 satisfying:
∫︁ 𝑛
𝑢 − 𝑢𝑛−1
∫︁
∀𝑤 ∈ 𝑉0 ; 𝑤 + 𝜅∇𝑢 ∇𝑤) + 𝛼(𝑢𝑛 − 𝑢𝑢𝑒 )𝑤 = 0
𝑛
Ω 𝛿𝑡 Γ

where 𝑉0 = {𝑤 ∈ 𝐻 1 (Ω)/𝑤|Γ24 = 0}.


So to code the method with the matrices 𝐴 = (𝐴𝑖𝑗 ), 𝑀 = (𝑀𝑖𝑗 ), and the vectors 𝑢𝑛 , 𝑏𝑛 , 𝑏′ , 𝑏”, 𝑏𝑐𝑙 (notation if 𝑤 is a
vector then 𝑤𝑖 is a component of the vector).
𝑏”𝑖 if 𝑖 ∈ Γ24
{︂
1
𝑢𝑛 = 𝐴−1 𝑏𝑛 , 𝑏′ = 𝑏0 + 𝑀 𝑢𝑛−1 , 𝑏” = 𝑏𝑐𝑙 , 𝑏𝑛𝑖 =
𝜀 𝑏′𝑖 else if ̸∈ Γ24

3.3. Finite element 217


FreeFEM Documentation, Release 4.8

Where with 1
𝜀 = tgv = 1030 :

if 𝑖 ∈ Γ24 , and𝑗 = 𝑖
⎧ 1
⎨ ∫︁ 𝜀 ∫︁
𝐴𝑖𝑗 =
⎩ 𝑤𝑗 𝑤𝑖 /𝑑𝑡 + 𝑘(∇𝑤𝑗 .∇𝑤𝑖 ) + 𝛼𝑤𝑗 𝑤𝑖 else if 𝑖 ̸∈ Γ24 , or𝑗 ̸= 𝑖
⎧ Ω Γ13
⎨ ∫︁ 1
𝜀 if 𝑖 ∈ Γ24 , and𝑗 = 𝑖
𝑀𝑖𝑗 =
⎩ 𝑤𝑗 𝑤𝑖 /𝑑𝑡 else if 𝑖 ̸∈ Γ24 , or𝑗 ̸= 𝑖
∫︀ Ω
𝑏0,𝑖 = Γ13 𝛼𝑢𝑢𝑒 𝑤𝑖
𝑏𝑐𝑙 = 𝑢0 the initial data

1 // Parameters
2 func fu0 = 10 + 90*x/6;
3 func k = 1.8*(y<0.5) + 0.2;
4 real ue = 25.;
5 real alpha = 0.25;
6 real T = 5;
7 real dt = 0.1 ;
8

9 // Mesh
10 mesh Th = square(30, 5, [6*x, y]);
11

12 // Fespace
13 fespace Vh(Th, P1);
14 Vh u0 = fu0, u = u0;

Create three variational formulation, and build the matrices 𝐴,𝑀 .

1 // Problem
2 varf vthermic (u, v)
3 = int2d(Th)(
4 u*v/dt
5 + k*(dx(u)*dx(v) + dy(u)*dy(v))
6 )
7 + int1d(Th, 1, 3)(
8 alpha*u*v
9 )
10 + on(2, 4, u=1)
11 ;
12

13 varf vthermic0 (u, v)


14 = int1d(Th, 1, 3)(
15 alpha*ue*v
16 )
17 ;
18

19 varf vMass (u, v)


20 = int2d(Th)(
21 u*v/dt
22 )
23 + on(2, 4, u=1)
24 ;
25

(continues on next page)

218 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


26 real tgv = 1e30;
27 matrix A = vthermic(Vh, Vh, tgv=tgv, solver=CG);
28 matrix M = vMass(Vh, Vh);

Now, to build the right hand size we need 4 vectors.

1 real[int] b0 = vthermic0(0, Vh); //constant part of the RHS


2 real[int] bcn = vthermic(0, Vh); //tgv on Dirichlet boundary node ( !=0 )
3 //we have for the node i : i in Gamma_24 -> bcn[i] != 0
4 real[int] bcl = tgv*u0[]; //the Dirichlet boundary condition part

Note: The boundary condition is implemented by penalization and vector bcn contains the contribution of the bound-
ary condition 𝑢 = 1, so to change the boundary condition, we have just to multiply the vector bcn[] by the current
value f of the new boundary condition term by term with the operator .*.
Uzawa model gives a real example of using all this features.

And the new version of the algorithm is now:

1 // Time loop
2 ofstream ff("thermic.dat");
3 for(real t = 0; t < T; t += dt){
4 // Update
5 real[int] b = b0; //for the RHS
6 b += M*u[]; //add the the time dependent part
7 //lock boundary part:
8 b = bcn ? bcl : b; //do forall i: b[i] = bcn[i] ? bcl[i] : b[i]
9

10 // Solve
11 u[] = A^-1*b;
12

13 // Save
14 ff << t << " " << u(3, 0.5) << endl;
15

16 // Plot
17 plot(u);
18 }
19

20 // Display
21 for(int i = 0; i < 20; i++)
22 cout << dy(u)(6.0*i/20.0, 0.9) << endl;
23

24 // Plot
25 plot(u, fill=true, wait=true);

Note: The functions appearing in the variational form are formal and local to the varf definition, the only important
thing is the order in the parameter list, like in:

1 varf vb1([u1, u2], q) = int2d(Th)((dy(u1) + dy(u2))*q) + int2d(Th)(1*q);


2 varf vb2([v1, v2], p) = int2d(Th)((dy(v1) + dy(v2))*p) + int2d(Th)(1*p);

3.3. Finite element 219


FreeFEM Documentation, Release 4.8

To build matrix 𝐴 from the bilinear part the variational form 𝑎 of type varf simply write:

1 A = a(Vh, Wh , []...]);
2 // where
3 //Vh is "fespace" for the unknown fields with a correct number of component
4 //Wh is "fespace" for the test fields with a correct number of component

Possible named parameters in , [...] are


• solver= LU, CG, Crout, Cholesky, GMRES, sparsesolver, UMFPACK . . .
The default solver is GMRES.
The storage mode of the matrix of the underlying linear system depends on the type of solver chosen;
for LU the matrix is sky-line non symmetric, for Crout the matrix is sky-line symmetric, for Cholesky
the matrix is sky-line symmetric positive definite, for CG the matrix is sparse symmetric positive, and
for GMRES, sparsesolver or UMFPACK the matrix is just sparse.
• factorize = If true then do the matrix factorization for LU, Cholesky or Crout, the default value is false.
• eps= A real expression.
𝜀 sets the stopping test for the iterative methods like CG.
Note that if 𝜀 is negative then the stopping test is:

||𝐴𝑥 − 𝑏|| < |𝜀|

if it is positive then the stopping test is

|𝜀|
||𝐴𝑥 − 𝑏|| <
||𝐴𝑥0 − 𝑏||

• precon= Name of a function (for example P) to set the preconditioner.


The prototype for the function P must be:

1 func real[int] P(real[int] & xx) ;

• tgv= Huge value (1030 ) used to implement Dirichlet boundary conditions.


• tolpivot= Set the tolerance of the pivot in UMFPACK (10− 1) and, LU, Crout, Cholesky factorization (10−20 ).
• tolpivotsym= Set the tolerance of the pivot sym in UMFPACK
• strategy= Set the integer UMFPACK strategy (0 by default).

Note: The line of the matrix corresponding to the space Wh and the column of the matrix corresponding to the space
Vh.

To build the dual vector b (of type real[int]) from the linear part of the variational form a do simply:

1 real b(Vh.ndof);
2 b = a(0, Vh);

A first example to compute the area of each triangle 𝐾 of mesh 𝑇 ℎ, just do:

220 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

1 fespace Nh(Th, P0); //the space function constant / triangle


2 Nh areaK;
3 varf varea (unused, chiK) = int2d(Th)(chiK);
4 etaK[] = varea(0, Ph);

Effectively, the basic functions of space 𝑁 ℎ, are the characteristic function of the element of Th, and the numbering is
the numeration of the element, so by construction:
∫︁ ∫︁
etaK[𝑖] = 1|𝐾𝑖 = 1;
𝐾𝑖

Now, we can use this to compute error indicators like in example Adaptation using residual error indicator.
First to compute a continuous approximation to the function ℎ “density mesh size” of the mesh 𝑇 ℎ.

1 fespace Vh(Th, P1);


2 Vh h ;
3 real[int] count(Th.nv);
4 varf vmeshsizen (u, v) = intalledges(Th, qfnbpE=1)(v);
5 varf vedgecount (u, v) = intalledges(Th, qfnbpE=1)(v/lenEdge);
6

7 // Computation of the mesh size


8 count = vedgecount(0, Vh); //number of edge / vertex
9 h[] = vmeshsizen(0, Vh); //sum length edge / vertex
10 h[] = h[]./count; //mean length edge / vertex

To compute error indicator for Poisson equation:


∫︁ ∫︁
𝜕𝑢ℎ 2
𝜂𝐾 = ℎ2𝐾 |(𝑓 + ∆𝑢ℎ )|2 + ℎ𝑒 |[ ]|
𝐾 𝜕𝐾 𝜕𝑛
where ℎ𝐾 is size of the longest edge (hTriangle), ℎ𝑒 is the size of the current edge (lenEdge), 𝑛 the normal.

1 fespace Nh(Th, P0); // the space function constant / triangle


2 Nh etak;
3 varf vetaK (unused, chiK)
4 = intalledges(Th)(
5 chiK*lenEdge*square(jump(N.x*dx(u) + N.y*dy(u)))
6 )
7 + int2d(Th)(
8 chiK*square(hTriangle*(f + dxx(u) + dyy(u)))
9 )
10 ;
11

12 etak[] = vetaK(0, Ph);

We add automatic expression optimization by default, if this optimization creates problems, it can be removed with the
keyword optimize as in the following example:

1 varf a (u1, u2)


2 = int2d(Th, optimize=0)(
3 dx(u1)*dx(u2)
4 + dy(u1)*dy(u2)
5 )
6 + on(1, 2, 4, u1=0)
(continues on next page)

3.3. Finite element 221


FreeFEM Documentation, Release 4.8

(continued from previous page)


7 + on(3, u1=1)
8 ;

or you can also do optimization and remove the check by setting optimize=2.
Remark, it is all possible to build interpolation matrix, like in the following example:

1 mesh TH = square(3, 4);


2 mesh th = square(2, 3);
3 mesh Th = square(4, 4);
4

5 fespace VH(TH, P1);


6 fespace Vh(th, P1);
7 fespace Wh(Th, P1);
8

9 matrix B = interpolate(VH, Vh); //build interpolation matrix Vh->VH


10 matrix BB = interpolate(Wh, Vh); //build interpolation matrix Vh->Wh

and after some operations on sparse matrices are available for example:

1 int N = 10;
2 real [int, int] A(N, N); //a full matrix
3 real [int] a(N), b(N);
4 A = 0;
5 for (int i = 0; i < N; i++){
6 A(i, i) = 1+i;
7 if (i+1 < N) A(i, i+1) = -i;
8 a[i] = i;
9 }
10 b = A*b;
11 matrix sparseA = A;
12 cout << sparseA << endl;
13 sparseA = 2*sparseA + sparseA';
14 sparseA = 4*sparseA + sparseA*5;
15 matrix sparseB = sparseA + sparseA + sparseA; ;
16 cout << "sparseB = " << sparseB(0,0) << endl;

3.3.17 Interpolation matrix

It is also possible to store the matrix of a linear interpolation operator from a finite element space 𝑉ℎ to another 𝑊ℎ to
interpolate(𝑊ℎ ,𝑉ℎ ,. . . ) a function.
Note that the continuous finite functions are extended by continuity outside of the domain.
The named parameters of function interpolate are:
• inside= set true to create zero-extension.
• t= set true to get the transposed matrix
• op= set an integer written below
– 0 the default value and interpolate of the function
– 1 interpolate the 𝜕𝑥

222 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

– 2 interpolate the 𝜕𝑦
– 3 interpolate the 𝜕𝑧
• U2Vc= set the which is the component of 𝑊ℎ come in 𝑉ℎ in interpolate process in a int array so the size of the
array is number of component of 𝑊ℎ , if the put −1 then component is set to 0, like in the following example:
(by default the component number is unchanged).

1 fespace V4h(Th4, [P1, P1, P1, P1]);


2 fespace V3h(Th, [P1, P1, P1]);
3 int[int] u2vc = [1, 3, -1]; //-1 -> put zero on the component
4 matrix IV34 = interpolate(V3h, V4h, inside=0, U2Vc=u2vc); //V3h <- V4h
5 V4h [a1, a2, a3, a4] = [1, 2, 3, 4];
6 V3h [b1, b2, b3] = [10, 20, 30];
7 b1[] = IV34*a1[];

So here we have: freefem b1 == 2, b2 == 4, b3 == 0 ...

Tip: Matrix interpolation

1 // Mesh
2 mesh Th = square(4, 4);
3 mesh Th4 = square(2, 2, [x*0.5, y*0.5]);
4 plot(Th, Th4, wait=true);
5

6 // Fespace
7 fespace Vh(Th, P1);
8 Vh v, vv;
9 fespace Vh4(Th4, P1);
10 Vh4 v4=x*y;
11

12 fespace Wh(Th, P0);


13 fespace Wh4(Th4, P0);
14

15 // Interpolation
16 matrix IV = interpolate(Vh, Vh4); //here the function is exended by continuity
17 cout << "IV Vh<-Vh4 " << IV << endl;
18

19 v=v4;
20 vv[]= IV*v4[]; //here v == vv
21

22 real[int] diff= vv[] - v[];


23 cout << "|| v - vv || = " << diff.linfty << endl;
24 assert( diff.linfty<= 1e-6);
25

26 matrix IV0 = interpolate(Vh, Vh4, inside=1); //here the function is exended by zero
27 cout << "IV Vh<-Vh4 (inside=1) " << IV0 << endl;
28

29 matrix IVt0 = interpolate(Vh, Vh4, inside=1, t=1);


30 cout << "IV Vh<-Vh4^t (inside=1) " << IVt0 << endl;
31

32 matrix IV4t0 = interpolate(Vh4, Vh);


33 cout << "IV Vh4<-Vh^t " << IV4t0 << endl;
34

(continues on next page)

3.3. Finite element 223


FreeFEM Documentation, Release 4.8

(continued from previous page)


35 matrix IW4 = interpolate(Wh4, Wh);
36 cout << "IV Wh4<-Wh " << IW4 << endl;
37

38 matrix IW4V = interpolate(Wh4, Vh);


39 cout << "IV Wh4<-Vh " << IW4 << endl;

Build interpolation matrix 𝐴 at a array of points (𝑥𝑥[𝑗], 𝑦𝑦[𝑗]), 𝑖 = 0, 2 here:

𝑎𝑖 𝑗 = 𝑑𝑜𝑝(𝑤𝑐𝑖 (𝑥𝑥[𝑗], 𝑦𝑦[𝑗]))

where 𝑤𝑖 is the basic finite element function, 𝑐 the component number, 𝑑𝑜𝑝 the type of diff operator like in op def.

1 real[int] xx = [.3, .4], yy = [.1, .4];


2 int c = 0, dop = 0;
3 matrix Ixx = interpolate(Vh, xx, yy, op=dop, composante=c);
4 cout << Ixx << endl;
5 Vh ww;
6 real[int] dd = [1, 2];
7 ww[] = Ixx*dd;

Tip: Schwarz
The following shows how to implement with an interpolation matrix a domain decomposition algorithm based on
Schwarz method with Robin conditions.
Given a non-overlapping partition Ω̄ = Ω̄0 ∪ Ω̄1 with Ω0 ∩ Ω1 = ∅, Σ := Ω̄0 ∩ Ω̄1 the algorithm is:

−∆𝑢𝑖 = 𝑓 in Ω𝑖 , 𝑖 = 0, 1,
𝜕(𝑢1 − 𝑢0 )
+ 𝛼(𝑢1 − 𝑢0 ) = 0 on Σ.
𝜕𝑛
The same in variational form is:
∫︀ ∫︀ ∫︀
Ω𝑖
∇𝑢𝑖 · ∇𝑣 + Σ
𝛼𝑢𝑖 𝑣 = Ω𝑖
𝑓𝑣
𝛼𝑢𝑗 𝑣, ∀𝑣 ∈ 𝐻01 (Ω), 𝑖, 𝑗 = [0, 1] ∪ [1, 0]
∫︀ ∫︀
− Ω𝑗
(∇𝑢𝑗 · ∇𝑣 − 𝑓 𝑣) + Σ

To discretized with the 𝑃 1 triangular Lagrangian finite element space 𝑉ℎ simply replace 𝐻01 (Ω) by 𝑉ℎ (Ω0 ) ∪ 𝑉ℎ (Ω1 ).
Then difficulty is to compute Ω𝑗 ∇𝑢𝑗 · ∇𝑣 when 𝑣 is a basis function of 𝑉ℎ (Ω𝑖 ), 𝑖 ̸= 𝑗.
∫︀

It is done as follows (with Γ = 𝜕Ω):

1 // Parameters
2 int n = 30;
3 int Gamma = 1;
4 int Sigma = 2;
5

6 func f = 1.;
7 real alpha = 1.;
8

9 int Niter = 50;


10

11 // Mesh
(continues on next page)

224 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(continued from previous page)


12 mesh[int] Th(2);
13 int[int] reg(2);
14

15 border a0(t=0, 1){x=t; y=0; label=Gamma;}


16 border a1(t=1, 2){x=t; y=0; label=Gamma;}
17 border b1(t=0, 1){x=2; y=t; label=Gamma;}
18 border c1(t=2, 1){x=t; y=1; label=Gamma;}
19 border c0(t=1, 0){x=t; y=1; label=Gamma;}
20 border b0(t=1, 0){x=0; y=t; label=Gamma;}
21 border d(t=0, 1){x=1; y=t; label=Sigma;}
22 plot(a0(n) + a1(n) + b1(n) + c1(n) + c0(n) + b0(n) + d(n));
23 mesh TH = buildmesh(a0(n) + a1(n) + b1(n) + c1(n) + c0(n) + b0(n) + d(n));
24

25 reg(0) = TH(0.5, 0.5).region;


26 reg(1) = TH(1.5, 0.5).region;
27

28 for(int i = 0; i < 2; i++) Th[i] = trunc(TH, region==reg(i));


29

30 // Fespace
31 fespace Vh0(Th[0], P1);
32 Vh0 u0 = 0;
33

34 fespace Vh1(Th[1], P1);


35 Vh1 u1 = 0;
36

37 // Macro
38 macro grad(u) [dx(u), dy(u)] //
39

40 // Problem
41 int i;
42 varf a (u, v)
43 = int2d(Th[i])(
44 grad(u)'*grad(v)
45 )
46 + int1d(Th[i], Sigma)(
47 alpha*u*v
48 )
49 + on(Gamma, u=0)
50 ;
51

52 varf b (u, v)
53 = int2d(Th[i])(
54 f*v
55 )
56 + on(Gamma, u=0)
57 ;
58

59 varf du1dn (u, v)


60 =-int2d(Th[1])(
61 grad(u1)'*grad(v)
62 - f*v
63 )
(continues on next page)

3.3. Finite element 225


FreeFEM Documentation, Release 4.8

(continued from previous page)


64 + int1d(Th[1], Sigma)(
65 alpha*u1*v
66 )
67 +on(Gamma, u=0)
68 ;
69

70 varf du0dn (u, v)


71 =-int2d(Th[0])(
72 grad(u0)'*grad(v)
73 - f*v
74 )
75 + int1d(Th[0], Sigma)(
76 alpha*u0*v
77 )
78 +on(Gamma, u=0)
79 ;
80

81 matrix I01 = interpolate(Vh1, Vh0);


82 matrix I10 = interpolate(Vh0, Vh1);
83

84 matrix[int] A(2);
85 i = 0; A[i] = a(Vh0, Vh0);
86 i = 1; A[i] = a(Vh1, Vh1);
87

88 // Solving loop
89 for(int iter = 0; iter < Niter; iter++){
90 // Solve on Th[0]
91 {
92 i = 0;
93 real[int] b0 = b(0, Vh0);
94 real[int] Du1dn = du1dn(0, Vh1);
95 real[int] Tdu1dn(Vh0.ndof); Tdu1dn = I01'*Du1dn;
96 b0 += Tdu1dn;
97 u0[] = A[0]^-1*b0;
98 }
99 // Solve on Th[1]
100 {
101 i = 1;
102 real[int] b1 = b(0, Vh1);
103 real[int] Du0dn = du0dn(0, Vh0);
104 real[int] Tdu0dn(Vh1.ndof); Tdu0dn = I10'*Du0dn;
105 b1 += Tdu0dn;
106 u1[] = A[1]^-1*b1;
107 }
108 plot(u0, u1, cmm="iter="+iter);
109 }

226 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

3.3.18 Finite elements connectivity

Here, we show how to get informations on a finite element space 𝑊ℎ (𝒯𝑛 , *), where “*” may be P1, P2, P1nc, etc.
• Wh.nt gives the number of element of 𝑊ℎ
• Wh.ndof gives the number of degrees of freedom or unknown
• Wh.ndofK gives the number of degrees of freedom on one element
• Wh(k,i) gives the number of 𝑖th degrees of freedom of element 𝑘.
See the following example:

Tip: Finite element connectivity

1 // Mesh
2 mesh Th = square(5, 5);
3

4 // Fespace
5 fespace Wh(Th, P2);
6

7 cout << "Number of degree of freedom = " << Wh.ndof << endl;
8 cout << "Number of degree of freedom / ELEMENT = " << Wh.ndofK << endl;
9

10 int k = 2, kdf = Wh.ndofK; //element 2


11 cout << "Degree of freedom of element " << k << ":" << endl;
12 for (int i = 0; i < kdf; i++)
13 cout << Wh(k,i) << " ";
14 cout << endl;

The output is:

1 Number of degree of freedom = 121


2 Number of degree of freedom / ELEMENT = 6
3 Degree of freedom of element 2:
4 78 95 83 87 79 92

3.4 Visualization

Results created by the finite element method can be a huge set of data, so it is very important to render them easy to
grasp.
There are two ways of visualization in FreeFEM:
• One, the default view, which supports the drawing of meshes, isovalues of real FE-functions, and of vector fields,
all by the command plot (see Plot section below). For publishing purpose, FreeFEM can store these plots as
postscript files.
• Another method is to use external tools, for example, gnuplot (see Gnuplot section, medit section, Paraview
section, Matlab/Octave section) using the command system to launch them and/or to save the data in text files.

3.4. Visualization 227


FreeFEM Documentation, Release 4.8

3.4.1 Plot

With the command plot, meshes, isovalues of scalar functions, and vector fields can be displayed.
The parameters of the plot command can be meshes, real FE functions, arrays of 2 real FE functions, arrays of two
double arrays, to plot respectively a mesh, a function, a vector field, or a curve defined by the two double arrays.

Note: The length of an arrow is always bound to be in [5‰, 5%] of the screen size in order to see something.

The plot command parameters are listed in the Reference part.


The keyboard shortcuts are:
• enter tries to show plot
• p previous plot (10 plots saved)
• ? shows this help
• +,- zooms in/out around the cursor 3/2 times
• = resets the view
• r refreshes plot
• up, down, left, right special keys to tanslate
• 3 switches 3d/2d plot keys :
– z,Z focal zoom and zoom out
– H,h increases or decreases the Z scale of the plot
• mouse motion:
– left button rotates
– right button zooms (ctrl+button on mac)
– right button +alt tanslates (alt+ctrl+button on mac)
• a,A increases or decreases the arrow size
• B switches between showing the border meshes or not
• i,I updates or not: the min/max bound of the functions to the window
• n,N decreases or increases the number of iso value arrays
• b switches between black and white or color plotting
• g switches between grey or color plotting
• f switches between filling iso or iso line
• l switches between lighting or not
• v switches between show or not showing the numerical value of colors
• m switches between show or not showing the meshes
• w window dump in file ffglutXXXX.ppm
• * keep/drop viewpoint for next plot
• k complex data / change view type
• ESC closes the graphics process before version 3.22, after no way to close

228 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

• otherwise does nothing


For example:

1 real[int] xx(10), yy(10);


2

3 mesh Th = square(5,5);
4

5 fespace Vh(Th, P1);


6

7 //plot scalar and vectorial FE function


8 Vh uh=x*x+y*y, vh=-y^2+x^2;
9 plot(Th, uh, [uh, vh], value=true, ps="three.eps", wait=true);
10

11 //zoom on box defined by the two corner points [0.1,0.2] and [0.5,0.6]
12 plot(uh, [uh, vh], bb=[[0.1, 0.2], [0.5, 0.6]],
13 wait=true, grey=true, fill=true, value=true, ps="threeg.eps");
14

15 //compute a cut
16 for (int i = 0; i < 10; i++){
17 x = i/10.;
18 y = i/10.;
19 xx[i] = i;
20 yy[i] = uh; //value of uh at point (i/10., i/10.)
21 }
22 plot([xx, yy], ps="likegnu.eps", wait=true);

To change the color table and to choose the value of iso line you can do:

1 // from: \url{http://en.wikipedia.org/wiki/HSV_color_space}
2 // The HSV (Hue, Saturation, Value) model defines a color space
3 // in terms of three constituent components:
4 // HSV color space as a color wheel
5 // Hue, the color type (such as red, blue, or yellow):
6 // Ranges from 0-360 (but normalized to 0-100% in some applications, like here)
7 // Saturation, the "vibrancy" of the color: Ranges from 0-100%
8 // The lower the saturation of a color, the more "grayness" is present
9 // and the more faded the color will appear.
10 // Value, the brightness of the color: Ranges from 0-100%
11

12 mesh Th = square(10, 10, [2*x-1, 2*y-1]);


13

14 fespace Vh(Th, P1);


15 Vh uh=2-x*x-y*y;
16

17 real[int] colorhsv=[ // color hsv model


18 4./6., 1 , 0.5, // dark blue
19 4./6., 1 , 1, // blue
20 5./6., 1 , 1, // magenta
21 1, 1. , 1, // red
22 1, 0.5 , 1 // light red
23 ];
24 real[int] viso(31);
25

(continues on next page)

3.4. Visualization 229


FreeFEM Documentation, Release 4.8

(a) Mesh, isovalue and vector (b) Enlargement in grey of isovalue and vector

(c) Plots a cut of uh. Note that a refinement of the same can
be obtained in combination with gnuplot

Fig. 3.39: Plot

230 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

(b) Isovalue with an other color table

(a) HSV color cylinder

Fig. 3.40: HSV

(continued from previous page)


26 for (int i = 0; i < viso.n; i++)
27 viso[i] = i*0.1;
28

29 plot(uh, viso=viso(0:viso.n-1), value=true, fill=true, wait=true, hsv=colorhsv);

Note: See HSV example for the complete script.

3.4.2 Link with gnuplot

Example Membrane shows how to generate a gnuplot from a FreeFEM file. Here is another technique which has the
advantage of being online, i.e. one doesn’t need to quit FreeFEM to generate a gnuplot.
However, this works only if gnuplot is installed, and only on an Unix-like computer.
Add to the previous example:

1 {// file for gnuplot


2 ofstream gnu("plot.gp");
3 for (int i = 0; i < n; i++)
4 gnu << xx[i] << " " << yy[i] << endl;
5 }
6

7 // to call gnuplot command and wait 5 second (due to the Unix command)
8 // and make postscript plot
9 exec("echo 'plot \"plot.gp\" w l \n pause 5 \n set term postscript \n set output \
˓→"gnuplot.eps\" \n replot \n quit' | gnuplot");

Note: See Plot example for the complete script.

3.4. Visualization 231


FreeFEM Documentation, Release 4.8

Fig. 3.41: Plots a cut of uh with gnuplot

232 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

3.4.3 Link with medit

As said above, medit is a freeware display package by Pascal Frey using OpenGL. Then you may run the following
example.
Now medit software is included in FreeFEM under ffmedit name.
The medit command parameters are listed in the Reference part.

Fig. 3.42: :freefem:medit` plot

With version 3.2 or later

1 load "medit"
2

3 mesh Th = square(10, 10, [2*x-1, 2*y-1]);


4

5 fespace Vh(Th, P1);


6 Vh u=2-x*x-y*y;
7

8 medit("u", Th, u);

Before:

1 mesh Th = square(10, 10, [2*x-1, 2*y-1]);


2

3 fespace Vh(Th, P1);


4 Vh u=2-x*x-y*y;
5

6 savemesh(Th, "u", [x, y, u*.5]); //save u.points and u.faces file


7 // build a u.bb file for medit
8 {
9 ofstream file("u.bb");
10 file << "2 1 1 " << u[].n << " 2 \n";
11 for (int j = 0; j < u[].n; j++)
12 file << u[][j] << endl;
13 }
14 //call medit command
(continues on next page)

3.4. Visualization 233


FreeFEM Documentation, Release 4.8

(continued from previous page)


15 exec("ffmedit u");
16 //clean files on unix-like OS
17 exec("rm u.bb u.faces u.points");

Note: See Medit example for the complete script.

3.4.4 Link with Paraview

One can also export mesh or results in the .vtk format in order to post-process data using Paraview.

1 load "iovtk"
2

3 mesh Th = square(10, 10, [2*x-1, 2*y-1]);


4

5 fespace Vh(Th, P1);


6 Vh u=2-x*x-y*y;
7

8 int[int] Order = [1];


9 string DataName = "u";
10 savevtk("u.vtu", Th, u, dataname=DataName, order=Order);

Fig. 3.43: Paraview plot

Warning: Finite element variables saved using paraview must be in P0 or P1

Note: See Paraview example for the complete script.

234 Chapter 3. Documentation


FreeFEM Documentation, Release 4.8

3.4.5 Link with Matlab© and Octave

In order to create a plot from a FreeFEM simulation in Octave and Matlab the mesh, the finite element space connec-
tivity and the simulation data must be written to files:

1 include "ffmatlib.idp"
2

3 mesh Th = square(10, 10, [2*x-1, 2*y-1]);


4 fespace Vh(Th, P1);
5 Vh u=2-x*x-y*y;
6

7 savemesh(Th,"export_mesh.msh");
8 ffSaveVh(Th,Vh,"export_vh.txt");
9 ffSaveData(u,"export_data.txt");

Within Matlab or Octave the files can be plot with the ffmatlib library:

1 addpath('path to ffmatlib');
2 [p,b,t]=ffreadmesh('export_mesh.msh');
3 vh=ffreaddata('export_vh.txt');
4 u=ffreaddata('export_data.txt');
5 ffpdeplot(p,b,t,'VhSeq',vh,'XYData',u,'ZStyle','continuous','Mesh','on');
6 grid;

Fig. 3.44: Matlab / Octave plot

Note: For more Matlab / Octave plot examples have a look at the tutorial section Matlab / Octave Examples or visit
the ffmatlib library on github.

3.4. Visualization 235


FreeFEM Documentation, Release 4.8

3.5 Algorithms & Optimization

3.5.1 Conjugate Gradient/GMRES

Suppose we want to solve the Euler problem (here 𝑥 has nothing to do with the reserved variable for the first coordinate
in FreeFEM):
find 𝑥 ∈ R𝑛 such that
(︂ )︂
𝜕𝐽
∇𝐽(𝑥) = (x) = 0 (3.17)
𝜕𝑥𝑖

where 𝐽 is a function (to minimize for example) from R𝑛 to R.


If the function is convex we can use the conjugate gradient algorithm to solve the problem, and we just need the function
(named dJ for example) which computes ∇𝐽, so the parameters are the name of that function with prototype func
real[int] dJ(real[int] &xx); which computes ∇𝐽, and a vector x of type (of course the number 20 can be
changed) real[int] x(20); to initialize the process and get the result.
Given an initial value x(0) , a maximum number 𝑖max of iterations, and an error tolerance 0 < 𝜖 < 1:
Put x = x(0) and write

1 NLCG(dJ, x, precon=M, nbiter=imax, eps=epsilon, stop=stopfunc);

will give the solution of x of ∇𝐽(x) = 0. We can omit parameters precon, nbiter, eps, stop. Here 𝑀 is the
preconditioner whose default is the identity matrix.
The stopping test is

‖∇𝐽(x)‖𝑃 ≤ 𝜖‖∇𝐽(x(0) )‖𝑃

Writing the minus value in eps=, i.e.,

1 NLCG(dJ, x, precon=M, nbiter=imax, eps=-epsilon);

We can use the stopping test:

‖∇𝐽(x)‖2𝑃 ≤ 𝜖

The parameters of these three functions are:


• nbiter= set the number of iteration (by default 100)
• precon= set the preconditioner function (P for example) by default it is the identity, note the prototype is func
real[int] P(real[int] &x).
• eps= set the value of the stop test 𝜀 (= 10−6 by default) if positive then relative test ||∇𝐽(𝑥)||𝑃 ≤ 𝜀||∇𝐽(𝑥0 )||𝑃 ,
otherwise the absolute test is ||∇𝐽(𝑥)||2𝑃 ≤ |𝜀|.
• veps= set and return the value of the stop test, if positive, then relative test is ||∇𝐽(𝑥)||𝑃 ≤ 𝜀||∇𝐽(𝑥0 )||𝑃 ,
otherwise the absolute test is ||∇𝐽(𝑥)||2𝑃 ≤ |𝜀|. The return value is minus the real stop test (remark: it is useful
in loop).
• stop= stopfunc add your test function to stop before the eps criterion. The prototype for the function stopfunc