FreeFEM Documentation
FreeFEM Documentation
Release 4.8
Frederic Hecht
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
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.
3
FreeFEM Documentation, Release 4.8
4 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8
• Since the version 4.5, the FreeFEM binary packages provides with a compiled PETSc library.
• FreeFEM is now interfaced with ParMmg.
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.
Allows to define and solve a 2d/3d BEM formulation and rebuild the associated potential. The document is in con-
struction.
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.
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
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
Beforehand, install the following dependances libraries using the apt tool:
1 dpkg -i FreeFEM_VERSION_Ubuntu_withPETSc_amd64.deb
An up-to-date package of FreeFEM for Arch is available on the Archlinux user repository.
To install it:
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. :
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
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
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
1. Install Xcode, Xcode Command Line tools and Xcode Additional Tools from the Apple website
2. Install gfortran from Homebrew
Note: If you have installed gcc via brew, gfortran comes with it and you do not need this line
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
5 make -j<nbProcs>
6 make -j<nbProcs> install
5. If you want build your own configure according your system, install autoconf and automake from Homebrew
(optional, see note in step 10)
6. To use FreeFEM with its plugins, install from Homebrew 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)
8. Download the latest Git for Mac installer git and the FreeFEM source from the repository
1 cd FreeFem-sources
2 autoreconf -i
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
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
Compilation on Ubuntu
Warning: In the oldest distribution of Ubuntu, libgsl-dev does not exist, use libgsl2-dev instead
3. Autoconf
1 cd FreeFem-sources
2 autoreconf -i
4. Configure
1 ./configure --enable-download --enable-optim
2 --prefix=/where/you/want/to/have/files/installed
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 make -j<nbProcs>
2 make -j<nbProcs> check
Note: make check is optional, but advised to check the validity of your FreeFEM build
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
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 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
3. Autoconf
1 cd FreeFem-sources
2 autoreconf -i
4. Configure
12 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8
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
1 make
Note: If your computer has many threads, you can run make in parallel using make -j16 for 16 threads, for
example.
Compilation on Fedora
3. Autoconf
1 cd FreeFem-sources
2 autoreconf -i
4. Configure
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
1 make -j<nbProcs>
2 make -j<nbProcs> check
Note: make check is optional, but advised to check the validity of your FreeFEM build
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 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 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
6. Open a MingW64 terminal (or MingW32 for old 32 bit FreeFEM version) and compile the FreeFEM source
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).
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.
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"
16 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8
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.
1 export FF_VERBOSITY=100;
2 ./FreeFem++-nw
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++"
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/.
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-
Emacs editor
1.3 Download
Note: The support ended for all releases under Windows 32 bits.
18 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8
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.
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
ISO690
MLA
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
1.7 Contributing
22 Chapter 1. Introduction
FreeFEM Documentation, Release 4.8
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.
25
FreeFEM Documentation, Release 4.8
13 fespace Vh(Th,P2);
14 Vh f = sin(pi*x)*cos(pi*y);
15 Vh g = sin(pi*x + cos(pi*y));
16
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 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
1 real aaa = 1;
2 real aaa;
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:
1 ...
2 fespace Vh(Th, P1);
3 Vh u;
4 cout << u;
5 matrix A = a(Vh, Vh);
6 cout << A;
We will compute 𝑢 with 𝑓 (𝑥, 𝑦) = 𝑥𝑦 and Ω the unit disk. The boundary 𝐶 = 𝜕Ω is defined as:
Note: In FreeFEM, the domain Ω is assumed to be described by the left side of its boundary.
11 // Define a function f
12 func f= x*y;
13
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.
for all 𝑣 which are in the finite element space 𝑉ℎ and zero on the boundary 𝐶.
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:
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:
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.
(a) mesh Th
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
with:
= ∫︀Ω ∇𝑢 · ∇𝑣 d𝑥d𝑦
∫︀
𝑎(𝑢, 𝑣)
(2.4)
ℓ(𝑓, 𝑣) = Ω 𝑓 𝑣 d𝑥d𝑦
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.
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)
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
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.
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.
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.
∆(∆𝜙) = 𝑓 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, ∀𝑥 ∈ Γ,
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 𝜕Ω.
𝜕𝑡
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 Ω
𝜙|Γ = 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
𝜕2𝜙 𝜕2𝜙
∆𝜙 = +
𝜕𝑥21 𝜕𝑥22
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):
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
(a) Mesh of the ellipse (b) Level lines of the membrane deformation
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.
except that on Γ2 𝜕𝑛 𝜙 = 2 instead of zero. So we will consider a non-homogeneous Neumann condition and solve:
∫︁ ∫︁ ∫︁
∇𝜙 · ∇𝑤 = 𝑓𝑤 + 2𝑤 ∀𝑤 ∈ 𝑉
Ω Ω Γ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
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;
1 L2error 0 = 0.00462991
2 L2error 1 = 0.00117128
3 convergence rate = 1.9829
(continues on next page)
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.
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
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:
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.
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).
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 .
𝑢 = 𝑢𝑖 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
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)
46 // Plot
47 plot(u, wait=true, value=true, fill=true, ps="HeatExchanger.eps");
Tip: Exercise :
Use the symmetry of the problem with respect to the x axes.
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 Ω
𝜕𝑣
𝜕𝑛 |Γ =𝑔
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
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
(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 Γ}
23 // Eigen values
24 int nev=2; // Number of requested eigenvalues near sigma
25
32 cout << ev(0) << " 2 eigen values " << ev(1) << endl;
33 v = eV[0];
34 plot(v, wait=true, ps="eigen.eps");
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.
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)
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:
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 .
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,
𝜕𝑛
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
36 plot(v);
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
𝜕𝑦
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:
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);
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 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.
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:
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)
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
50
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.
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 Ω.
𝑐(𝑥𝑡 , 𝑡) = 𝑐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.
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
Note: 3D plots can be done by adding the qualifyer dim=3 to the plot instruction.
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)
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);
(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
(a) The rotating hill after one revolution with (b) The rotating hill after one revolution with Discontinuous
Characteristics-Galerkin 𝑃1 Galerkin
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
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 − (𝜇 + 𝜆)∇(∇.u) = f in Ω,
−𝑑𝑖𝑣(𝜎) = f in Ω
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
1 // Parameters
2 real E = 21e5;
3 real nu = 0.28;
4
5 real f = -1;
6
7 // Mesh
(continues on next page)
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
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;
Solution of Lamé’s equations for elasticity for a 2D beam deflected by its own weight and clamped by its left vertical
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.
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
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
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)
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.
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Γ
−∆𝑝𝑚+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
Fig. 2.12: Solution of Stokes’ equations for the driven cavity problem, showing the velocity field and the pressure level
lines.
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
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
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
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
(b) Pressure
(c) Velocity
101 p = pold-q;
102 u[] += dtM1x*q[];
103 v[] += dtM1y*q[];
104
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.
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
Ω
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
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)
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)
74 u1[] -= du1[];
75 u2[] -= du2[];
76 p[] -= dp[];
77
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
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
Note: We use a trick to make continuation on the viscosity 𝜈, because the Newton method blowup owe start with the
final viscosity 𝜈.
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 :
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
𝑇
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
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
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)
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
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
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
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)
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
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
(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
31 // Fespace
32 fespace Vh(Th, P1);
33 Vh R=(region-air)/(meat-air);
34 Vh<complex> v, w;
35 Vh vr, vi;
36
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
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.
(c) Temperature
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 // 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
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)
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
64 real[int] Z(3);
65 for(int j = 0; j < z.n; j++)
66 Z[j]=1;
67
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 𝑧.
Note: It is necessary to recopy 𝑍 into 𝑧 because one is a local variable while the other one is global.
−∇ · (𝜅∇𝑝) = 2𝐼𝐸 (𝑢 − 𝑢𝑑 )
𝑝|Γ = 0
Consequently:
∫︀
𝛿𝐽 = −
∫︀ Ω (∇ · (𝜅∇𝑝))𝛿𝑢
= Ω∫︀(𝜅∇𝑝 · ∇𝛿𝑢)
= − Ω (𝛿𝜅∇𝑝 · ∇𝑢)
𝐽𝑏′ = − ∫︀𝐵 ∇𝑝 · ∇𝑢
∫︀
𝐽𝑐′ = − ∫︀𝐶 ∇𝑝 · ∇𝑢
𝐽𝑑′ = − 𝐷 ∇𝑝 · ∇𝑢
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).
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) 𝑝¯𝑚 (𝑝 − 𝑝𝑚 ∘ 𝑋 𝑚 )
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
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)
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 }
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, 𝑇 )
𝑏𝑖 if 𝑖 ∈ Γ24
{︂ ′′
𝑛 −1 𝑛 ′ 𝑛−1 1 𝑛
𝑢 = 𝐴 𝑏 , 𝑏 = 𝑏0 + 𝑀 𝑢 , 𝑏” = 𝑏𝑐𝑙 , 𝑏𝑖 =
𝜀 𝑏′𝑖 else
Fig. 2.18: Pressure for a Euler flow around a disk at Mach 2 computed by (2.6)
Where with 1
𝜀 = tgv = 1030 :
if 𝑖 ∈ Γ24 , and
⎧ 1
⎨ ∫︁ 𝜀 ∫︁ 𝑗=𝑖
𝐴𝑖𝑗 =
⎩ 𝑤𝑗 𝑤𝑖 /𝑑𝑡 + 𝑘(∇𝑤𝑗 .∇𝑤𝑖 ) + 𝛼𝑤𝑗 𝑤𝑖 else
⎧ Ω Γ13
⎨ ∫︁ 1𝜀 if 𝑖 ∈ Γ24 , and 𝑗 = 𝑖
𝑀𝑖𝑗 =
⎩ 𝑛 𝑤𝑗 𝑤𝑖 /𝑑𝑡 else
∫︀ Ω
𝑏0,𝑖 = 𝑛 Γ13 𝛼𝑢𝑢𝑒 𝑤𝑖
𝑏𝑐𝑙 = 𝑢0 the initial data
1 ...
2 Vh u0=fu0, u=u0;
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
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)
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:
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:
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:
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.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:
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
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 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);
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
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 !
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";
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 }
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 :
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
E = −∇𝑢
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)
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);
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");
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:
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');
• 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)
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]);
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.
2.20.7 References
• Octave
• Matlab
• ffmatlib
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
3.1.1 Generalities
3.1.3 Numbers
3.1.5 Meshes
(︂∫︁ )︂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,Ω = 𝐷𝛼 𝑣𝐷𝛼 𝑤
|𝛼|≤𝑚 Ω
[𝐿2 (Ω)2 ] denotes 𝐿2 (Ω) × 𝐿2 (Ω), and also 𝐻 1 (Ω)2 = 𝐻 1 (Ω) × 𝐻 1 (Ω)
• [𝑉ℎ ] 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;
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]);
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
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 int upper = 1;
2 int others = 2;
3 int inner = 3;
4
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
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
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.
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.
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:
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`
1 // Mesh
2 mesh Th = square(2, 2);
3
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
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
75 cout << "// New method to get boundary information and mesh adjacent" << endl;
76 {
77 int k = 0;
(continues on next page)
98 cout << "Adjacent triangle of the triangle " << k << " by edge " << e << " = " <<␣
˓→Adjacent << endl;
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
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
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.
(a) The empty mesh with boundary (b) An empty mesh defined from a pseudo region numbering
of triangle
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 }
Remeshing
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:
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 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");
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)
This section presents the way to obtain a regular triangulation with FreeFEM.
For a set 𝑆, we define the diameter of 𝑆 by
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 function:
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
Fig. 3.8: 3D graphs for the initial mesh and 1st and 2nd mesh adaptations
Tip: The solution has the singularity 𝑟3/2 , 𝑟 = |𝑥 − 𝛾| at the point 𝛾 of the intersection of two lines 𝑏𝑐 and 𝑏𝑑 (see
Fig. 3.9a).
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
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:
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.
• 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:
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:
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
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
(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
• 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 :
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
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) ;
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");
(a) Initial mesh (b) All left mesh triangle is split conformaly in
int(1+5*(square(x-0.5)+y*y)^2 triangles
Meshing Examples
Tip: Cardioid
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);
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);
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 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 .
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
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);
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)
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 , 𝜃𝑖,𝑗 ],
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 ).
• 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
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 }
1 include "Cube.idp"
2
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)
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);
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)
37 func XX = x*cos(y);
38 func YY = x*sin(y);
39 func ZZ = z;
40
Remeshing
Note: if an operation on a mesh3 is performed then the same operation is applyed on its surface part (its meshS
associated)
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:
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
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) ;
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);
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𝑒 − 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.
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
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
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
1 load"msh3"
2 int nn = 30;
3 int[int] labs = [1, 2, 2, 1, 1, 2]; // Label numbering
(continues on next page)
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.
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𝑒 − 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)
10 func ZZ1min = 0;
11 func ZZ1max = 1.5;
12 func XX1 = x;
13 func YY1 = y;
14
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
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
50 // Gluing surfaces
51 meshS Th33 = Th31h + Th31b + Th32h + Th32b + Th33h + Th33b;
52 plot(Th33, cmm="Th33");
53
59 // Build a mesh of a half cylindrical shell of interior radius 1, and exterior radius 2␣
˓→and a height of 1.5
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
24 medit("Th", Th);
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
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
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)
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
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.
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
(a) The surface mesh of the hex with internal sphere (b) The tetrahedral mesh of the cube with internal ball
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.
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)
⃒
𝜋 𝜋 ⃒ 𝑥=Rx 𝑐𝑜𝑠(𝑢)𝑐𝑜𝑠(𝑣)+Ox
∀𝑢 ∈ [− , [ and 𝑣 ∈ [0, 2𝜋], ⃒⃒ 𝑦=Ry 𝑐𝑜𝑠(𝑢)𝑠𝑖𝑛(𝑣)+Oy
2 2 𝑧=Rz 𝑠𝑖𝑛(𝑣)+Oz
6 int nx=N[0],ny=N[1],nz=N[2];
7
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)
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 }
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)
Remeshing
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);
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 𝑉 𝑜𝑙(𝐵),
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
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:
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:
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 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
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;.
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)
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.
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.
• 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
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
Remeshing
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);
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
• 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 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:
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;
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.
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);
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.
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);
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.
• 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.
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.
𝑛𝑣
𝑥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 :
• 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.
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
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)
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
38 meshS ThS=movemesh23(Th,transfo=[f1min,f2min,f3min]);
39
46
50
51 medit("sphere",Th3sph,wait=1);
52 medit("sphererefinedomain",wait=1,Th3sphrefine);
53 medit("sphererefinelocal",wait=1,Th3sphrefine2);
54
2d case
Users who want to read a triangulation made elsewhere should see the structure of the file generated below:
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
18 // Fespace
19 fespace femp1(th, P1);
20 femp1 f = sin(x)*cos(y);
21 femp1 g;
22
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);
1 mesh Th=readmeshS("Th.mesh");
2 mesh Thff = readmesh("Thff.msh"); // FreeFEM format
1 savemesh(Th,"Th.mesh")
2 savemesh(Thff,"Thff.msh") // FreeFEM format
3
8 savemesh(Th,"mm",[x,y,u]); // save surface mesh for medit, see for example minimal-
˓→surf.edp
1 load "iovtk"
2 mesh Th=vtkloadS("mymesh.vtk");
1 load "iovtk"
2 savevtk("Th.vtk",Th);
1 load "gmsh"
2 mesh Th=gmshload("mymesh.msh");
1 load "gmsh"
2 savegmsh(Th, "Th");
3d case
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.
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
1 mesh3 Th3=readmesh3("Th3.mesh");
2 mesh3 Th3ff = readmesh3("Th3ff.msh"); // FreeFEM format
1 savemesh(Th3,"Th3.mesh")
2 savemesh(Th3ff,"Th3ff.msh") // FreeFEM format
1 load "iovtk"
2 mesh3 Th3=vtkloadS("mymesh.vtk");
1 load "iovtk"
2 savevtk("Th3.vtk",Th3);
1 load "gmsh"
2 mesh3 Th3=gmshload3("mymesh.msh");
1 load "gmsh"
2 savegmsh(Th3, "Th3");
Surface 3d case
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 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
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
1 meshS ThS=readmeshS("ThS.mesh");
2 meshS Th3ff = readmeshS("ThSff.msh"); // FreeFEM format
1 savemesh(ThS,"ThS.mesh")
2 savemesh(ThSff,"ThSff.msh") // FreeFEM format
1 load "iovtk"
2 meshS ThS=vtkloadS("mymesh.vtk");
1 load "iovtk"
2 savevtk("ThS.vtk",ThS);
1 load "gmsh"
2 meshS ThS=gmshloadS("mymesh.msh");
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:
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:
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
22 // Fespace
23 fespace Vh(Th, P2);
24 func f = sqrt(x*x + y*y);
25 Vh us, v;
26
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
55 // Solve
56 Lap2dOmega;
57 Lap2dOmega1;
58
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:
– 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:
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
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
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
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:
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:
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).
– 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)
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);
17 medit("SurfaceMesh", Th3);
3.2.9 mmg3d
Todo: mmg3d-v4.0
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.
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
21 // Plot
22 medit("Initial", Th);
23 medit("Isometric", Th3);
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
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
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)
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 }
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
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)
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
46 cout << "h min, max = " << h[].min << " "<< h[].max << " " << h[].n << " " << Th3.nv
˓→<< endl;
47 plot(u, wait=true);
48
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
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.
1 cout << "Number of the line component = " << nbc << endl;
2 cout << "Number of points = " << xy.m << endl;
3 cout << "be = " << be << endl;
4
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 𝑏𝑒, 𝑥𝑦.
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.
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
35 mesh Th = buildmesh(G(-NC));
36 plot(Th, wait=true);
37
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>);
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]]);
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).
• [P1] piecewise linear continuous finite element (2d, 3d, surface 3d), the degrees of freedom are the vertices
values.
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:
The 3D Case:
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)
The 3D Case:
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.
𝑣 continuous at vertices,
{︂ ⃒ {︂ }︂
P2ℎ 2
⃒
= 𝑣 ∈ 𝐿 (Ω) ⃒⃒ ∀𝐾 ∈ 𝒯ℎ , 𝑣|𝐾 ∈ 𝑃3 ,
𝜕𝑛 𝑣 continuous at middle of edge,
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
(𝑢, 𝑢𝑥 , 𝑢𝑦 ).
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)
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
⃒
ℎ
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
𝐾
The 3D Case:
{︃ ⃒ ⃒ 1 }︃
⃒ ⃒ 𝛼𝐾 ⃒𝑥
𝑅𝑇 0ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , v|𝐾 (𝑥, 𝑦, 𝑧) = ⃒ 𝛼𝐾 + 𝛽𝐾 ⃒ (3.8)
⃒ ⃒ 2 ⃒𝑦
⃒ ⃒ 𝛼3𝐾 𝑧
∑︀𝑑
where by writing div w = 𝑖=1𝜕𝑤𝑖 /𝜕𝑥𝑖 with w = (𝑤𝑖 )𝑑𝑖=1 :
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 𝑥
𝐾
{︃ ⃒ ⃒ 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
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
𝐾
{︁ ⃒ ⃒ 1 }︁
1 2 ⃒𝛼
𝑅𝑇 1ℎ = v ∈ 𝐻(curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 , 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃12 , 𝑃0 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽 | −𝑦
⃒
2 𝐾 𝑥
𝐾
(3.11)
{︁ ⃒ ⃒ 1 }︁
⃒𝛼
1
𝑅𝑇 2ℎ = v ∈ 𝐻(div) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 2
, 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃22 , 𝑃1 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | 𝑥𝑦 (3.12)
⃒
2
𝐾
{︁ ⃒ ⃒ 1 }︁
1 2 ⃒𝛼
𝑅𝑇 2ℎ = v ∈ 𝐻(curl) ⃒ ∀𝐾 ∈ 𝒯ℎ , 𝛼𝐾 , 𝛼𝐾 , 𝛽𝐾 ∈ 𝑃22 , 𝑃1 , v|𝐾 (𝑥, 𝑦) = ⃒ 𝛼𝐾 + 𝛽𝐾 | −𝑦
⃒
2 𝑥
𝐾
(3.13)
• [BDM1Ortho] (needs load "Element_Mixte") the Brezzi-Douglas-Marini Orthogonal also call Nedelec of
type II , finite element
• [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.
𝑋ℎ = 𝑣 ∈ 𝐻 1 (]0, 1[2 )| ∀𝐾 ∈ 𝒯ℎ
{︀ }︀
𝑣|𝐾 ∈ 𝑃1
. .
𝑋𝑝ℎ = {𝑣 ∈ 𝑋ℎ | 𝑣 (| 0. ) = 𝑣 (| 1. ) , 𝑣 (| 0 ) = 𝑣 (| 1 )}
⃒ 𝛼𝐾
𝑅ℎ = 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:
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
and
⃒ 𝑥ℎ ⃒
𝑈ℎ = ⃒ 𝑈
𝑈 𝑦ℎ 𝑉ℎ = ⃒ 𝑉𝑉 𝑥ℎ
𝑦ℎ
𝑀ℎ = {𝑣 ∈ 𝐻 (]0, 1[3 )| ∀𝐾 ∈ 𝒯ℎ
1
𝑣|𝐾 ∈ 𝑃2 }
{︁ ⃒ 𝛼𝐾 ⃒ 𝑥 }︁
𝑅ℎ = v ∈ 𝐻 1 (]0, 1[3 )2 | ∀𝐾 ∈ 𝒯ℎ v|𝐾 (𝑥, 𝑦, 𝑧) = ⃒ 𝛽𝐾 + 𝛿𝐾 ⃒ 𝑦
⃒ ⃒
𝛾
𝐾 𝑧
where Xh, Mh, Rh expresses finite element spaces (called FE spaces) 𝑋ℎ , 𝑀ℎ , 𝑅ℎ , respectively.
The functions 𝑈ℎ , 𝑉ℎ have two components so we have
1 Xh uh, vh;
2 Xh[int] Uh(10); //array of 10 functions in Xh
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
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 }
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
For each vertex 𝑞 𝑖 , the basis function 𝜑𝑖 in Vh(Th, P1) is given by:
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:
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).
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 )
𝐹˜ : R2 → R3
ˆ→𝑋
𝑥
⎛ ⎞ ⎛ −−−→ ⎞
𝑥 𝐴0 𝐴1
⎝𝑦 ⎠ → ⎜ −−−→
𝐴0 𝐴2 𝑥 − 𝐴0 )
⎠ (ˆ
⎟
⎝
0 −−−→ −−−→
𝐴0 𝐴1 ∧ 𝐴0 𝐴2
⎛ ⎞
𝑛
−−−→ −−−→ ⎝ 𝑥 ⎠ −−−→ −−−→
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 (𝑥, 𝑦) = 𝑦
• 𝜆
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:
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)
• 𝑁
⃗ 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 , 𝑌 )
⃗ ) = det(𝐴1 − 𝐴0 , 𝑃 ′ , 𝑌 )𝑑𝑃
⃗ 2 (𝑃 ), 𝑌
𝐷 𝑃 (𝑁
∇𝑃 ( 𝑁 ⃗ ) = det (𝐴1 − 𝐴0 , 𝑃 ′ , 𝑌
⃗ 2 (𝑃 ), 𝑌 ⃗)
⃗ , 𝑃 ′)
= −𝑑𝑒𝑡(𝐴1 − 𝐴0 , 𝑌
⃗ .𝑃 ′
= −(𝐴1 − 𝐴0 ) ∧ 𝑌
⃗ ∧ (𝐴1 − 𝐴0 )
=𝑌
⃗ 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.
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:
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 𝑇𝑘
2. 𝑓 ≥ 0 ⇒ 𝑢ℎ ≥ 0
3. If 𝑖 ̸= 𝑗, the basis function 𝜑𝑖 and 𝜑𝑗 are 𝐿2 -orthogonal:
∫︁
𝜑𝑖 𝜑𝑗 𝑡𝑒𝑥𝑡𝑑𝑥𝑡𝑒𝑥𝑡𝑑𝑦 = 0 if 𝑖 ̸= 𝑗
Ω
For each triangle 𝑇𝑘 ∈ 𝒯ℎ , let 𝜆𝑘1 (𝑥, 𝑦), 𝜆𝑘2 (𝑥, 𝑦), 𝜆𝑘3 (𝑥, 𝑦) be the area cordinate of the triangle (see Fig. 3.30), and
put:
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).
Functions from R2 to R𝑁 with 𝑁 = 1 are called scalar functions and called vector valued when 𝑁 > 1. When 𝑁 = 2
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 Vh(Th, RT0);
2 Vh [f1h, f2h] = [f1(x, y), f2(x, y)];
then:
𝑛𝑡 ∑︁
∑︁ 6
fh = fℎ (𝑥, 𝑦) = 𝑛𝑖𝑙 𝑗𝑙 |eil |𝑓𝑗𝑙 (𝑚𝑖𝑙 )𝜑𝑖𝑙 𝑗𝑙
𝑘=1 𝑙=1
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
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
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)
To get the value of the array associated to the FE function uh, one writes
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.
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:
Although this is a seemingly simple problem, it is difficult to find an efficient algorithm in practice.
⟩
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.
• 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
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
16 // Plot
17 plot(us, ug, wait=true);
18 plot(vs, vg, wait=true);
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.
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 𝜕𝑦 𝑢 = 𝜕𝑢
𝜕𝑦
Note: This is the well known Neumann boundary condition if 𝑎 = 0, and if Γ𝑑 is empty.
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).
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.
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)
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
where Th is a mesh of the three dimensional domain Ω, and gd and gn are respectively the boundary labels of boundary
Γ𝑑 and Γ𝑛 .
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).
Warning: Bug:
The mixing of multiple fespace with different periodic boundary conditions are not implemented.
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.
|𝜀|
||𝐴𝑥 − 𝑏|| <
||𝐴𝑥0 − 𝑏||
∑︁ ∫︁
– 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 𝑇
∑︁ ∫︁
– 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 𝜕𝑇
∑︁ ∫︁
– int2d(Th, l)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ∈Ω𝑙 𝑇
∑︁ ∫︁
– int2d(Th, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇,𝜑<0
∑︁ ∫︁
– 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 𝜕𝑇
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
• 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).
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
where * stands for the name of the quadrature formula or the precision (order) of the Gauss formula.
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
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.
where * stands for the name of quadrature formula or the order of the Gauss formula.
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: 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
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
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
37 // Verification in 1d and 2d
38 mesh Th = square(10, 10);
39
The output is
1 1.67 == 1.67
2 0.335 == 0.335
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 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;
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
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.
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:
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
|𝜀|
||𝐴𝑥 − 𝑏|| <
||𝐴𝑥0 − 𝑏||
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:
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 𝑇 ℎ.
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:
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:
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;
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 𝜕𝑥
– 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 // 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
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
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
where 𝑤𝑖 is the basic finite element function, 𝑐 the component number, 𝑑𝑜𝑝 the type of diff operator like in op def.
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 𝑉ℎ (Ω𝑖 ), 𝑖 ̸= 𝑗.
∫︀
1 // Parameters
2 int n = 30;
3 int Gamma = 1;
4 int Sigma = 2;
5
6 func f = 1.;
7 real alpha = 1.;
8
11 // Mesh
(continues on next page)
30 // Fespace
31 fespace Vh0(Th[0], P1);
32 Vh0 u0 = 0;
33
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
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 }
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:
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
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.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.
3 mesh Th = square(5,5);
4
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
(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
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:
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");
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.
1 load "medit"
2
Before:
One can also export mesh or results in the .vtk format in order to post-process data using Paraview.
1 load "iovtk"
2
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
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;
Note: For more Matlab / Octave plot examples have a look at the tutorial section Matlab / Octave Examples or visit
the ffmatlib library on github.
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)
𝜕𝑥𝑖
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)‖2𝑃 ≤ 𝜖