FreeFEM Documentation
FreeFEM Documentation
Release 4.13
Frederic Hecht
i
ii
CONTENTS
1 Introduction 3
1.1 New features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Installation guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3 Download . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.4 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.5 Citation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.6 Authors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.7 Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.8 Git & Github usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2 Learning by Examples 35
2.1 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.2 Classification of partial differential equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.3 Membrane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.4 Heat Exchanger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.5 Acoustics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.6 Thermal Conduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.7 Irrotational Fan Blade Flow and Thermal effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.8 Pure Convection : The Rotating Hill . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
2.9 The System of elasticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
2.10 The System of Stokes for Fluids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
2.11 A projection algorithm for the Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . . 70
2.12 Newton Method for the Steady Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . 75
2.13 A Large Fluid Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
2.14 An Example with Complex Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
2.15 Optimal Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.16 A Flow with Shocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
2.17 Time dependent schema optimization for heat equations . . . . . . . . . . . . . . . . . . . . . . . . 93
2.18 Tutorial to write a transient Stokes solver in matrix form . . . . . . . . . . . . . . . . . . . . . . . . 95
2.19 Wifi Propagation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
2.20 Plotting in Matlab and Octave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3 Documentation 109
3.1 Notations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.2 Mesh Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
3.3 Finite element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
3.4 Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
3.5 Algorithms & Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
3.6 Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
3.7 PETSc and SLEPc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
iii
3.8 The Boundary Element Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
3.9 Composite finite element spaces NEW! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
3.10 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
3.11 Developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
3.12 ffddm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
6 Examples 623
6.1 Misc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
6.2 Mesh Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
6.3 Finite Element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
6.4 Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
6.5 Algorithms & Optimizations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
6.6 Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
6.7 Developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
Bibliography 707
iv
FreeFEM Documentation, Release 4.13
CONTENTS 1
FreeFEM Documentation, Release 4.13
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.13
4 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
1 ```freefem
2 [freefem code]
3 ```
Documented Markdown examples in the distribution can be viewed on the documentation website (toggle
Search in examples). Markdown can be previewed with e.g Visual Studio Code, with the FreeFEM VS
Code extension providing syntax highlighting for FreeFEM code blocks.
– add command md2edp to extract freefem code form Markdown file.
– add read binary file with short number in bfstream plugin
– add 3d case in ClosePoints plugin : function Voisinage3, ClosePoints3
– add real[int,int] at=A';
– Powell Sabin splitmesh6PowellSabin(Th) splitting for Scott–Vogelius lowest Stokes Element in 2D in
plugin/seq/splitmesh6.cpp
– Worsey Farin splitmesh12WorseyFarin(Th3) splitting for Scott–Vogelius lowest Stokes Element in 3D
in plugin/seq/splitmesh12.cpp
– functional interface with fgmres (Linear and affine) in real and complex case see tutorial/algo.edp
• Changed
– PETSc 3.22.2
– move the plugin msh3 in kernel , so remove all load "msh3" in all examples and .idp files
• Fixed
– try to fix orientation of internal edges in 2d
– fix missing break and continue in for [i,ai:a] loop
– line number in macro error (thanks to P-H Tournier)
– fix problem in integration of moving test or unknown function in 2d mesh:
1 matrix A = va(Ph,Vh);
2 int[int] a = A, at= A';
– Exemple to code convolution of 2 functions with one with a small support to be not too expensive see
tutorial/Convolution-Sample.edp example
– Support for dense blocks in PETSc matrices
– GenEO for saddle-point examples with PCHPDDM in PETSc
– Distributed ParaView output on meshS
– Interface to mmg2d for two-dimensional mesh
– Support for Mmg parameters localParameter, -nosizreq, -hgradreq
• Changed
– PETSc 3.20.2
• Fixed
– bug in P3pnc3d in vectorial case (thanks to [email protected])
– in segment(10,region=1,label=ll); region is now used..
1 fespace Uh(Th1,[P2,P2]);
2 fespace Ph(Th2,P1);
3 fespace Vh=Uh*Ph;
∗ can define coupled problems using composite FE spaces, or directly with < > syntax:
6 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
1 fespace Uh(Th1,[P2,P2]);
2 fespace Ph(Th2,P1);
3 Uh [u1,u2],[v1,v2];
4 Ph p,q;
5
1 matrix A = va(Vh,Vh);
2 matrix B(A.n*5,A.n*5);
3 int i=2;
4 B.add(1.+10*i,A,i*ndof,i*ndof);
• Changed
– change isoline to do the job for meshS, see example plugin/isoline.edp
– change Curve function to be with 3 components to use the isoline data.
– change Curvature plugin to compatible with new isoline data for 3 d case.
– change some sprintf in snprint to remove warning
• Fixed
– bug in all P0face, P0edge, P0VF on mesh3,meshS, MeshL and also discontinous version (missing initiali-
sation)
– bug in plot function and ffglut with parameter pdf=”file.pdf” , because shift in plot named parameter not
change in ffglut.
– genere a bug if zero size element in read MeshL from file.
– remove mistake when the border is badly defined , remove empty element in buildmeshL function.
– bug in array quadrature FE.
8 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
– plugin Element_P3pnc for new 2d finite element P3pnc (P3 + 2 bulles) noncoforming (continuite of
P2 mod)
and add 2 examples with this new finite element examples/plugin/cavityNewtowP3pnc.edp
examples/plugin/testFE-P3pnc.edp
– function to set dirichlet Boundary conditon on matrix A (real ou compex) trought an real[int]
(if none zero => set BC )
setBC(A,au1[],-2); and the example
examples/3d/Elasticity-simple-support-BC.edp
• Changed
– the beaviour of linear solver UMFPACK, CHOLMOD in case of error , now FreeFEm exit on ExecError
like in MUMPS
– PETSc 3.17.0
• Removed
-map function in plugin dfft
• Fixed
– pow(int,int) now call int version not complex version..
– correct the normal the N implicite variable on meshL case
– correct version dump in banner FreeFem++ - version 4.10 (V . . .
– correct in CPU time on big mesh due to do bad HCode in HashTable.hpp
– bug in array of finite element on meshhS, meshL (ie. fespace Vh(ThS,[P1,P1]); )
10 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
12 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
– fixed problem of change of mesh when rebuild 2d mesh with buildmesh, . . . . (Thank to P. Jovilet to points
this problem)
– missing METIS library when using SuiteSparse compiled by PETSc
– missing -fno-stack-protector when building PETSc on Windows, see https://community.freefem.org/t/
error-loading-complex-petsc-slepc-library/370
– fixed ffglut for the plotting of FE array solution
– fixed ffglut bug on MacOS Catalina , draw inn only half windows screen (Apple Bug ???)
– correct P0VF finite element
– abs function of array
14 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
Note
Since the version 4.5, FreeFEM relese provides with the last version of PETSc.
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.
16 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
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.
• FreeFem++ (VERSION) Examples, a link to the freefem++ examples folder.
where (VERSION) is the version of the files (for example 4.5).
By default, the installed files are in C:\Programs Files\FreeFem++. In this directory, you have all the .dll files
and other applications: FreeFem++-nw.exe, ffglut.exe, . . . The syntax for the command-line tools are the same
as those of FreeFem.exe.
To use FreeFEM binaries under Windows, two methods are possible:
• Use the FreeFEM launcher (launchff++.exe)
Warning: if you launch FreeFEM without filename script by double-clicking, your get a error due (it is bug of usage
GetOpenFileName in win64).
• In shell terminal (cmd, powershell, bash, . . . ):
– To launch sequential version:
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. And read the README.md file.
If you want terminal access to FreeFEM just copy the file FreeFem++ in a directory of your $PATH shell environment
variable.
We make a small move to explain how to by pass the security problem see FreeFEM install movie
<https://www.ljll.fr/hecht/pkg/Install-FreeFEM-MacOS.mov>`
Ubuntu installation
Note
Beforehand, install the following dependances libraries using the apt tool:
1 dpkg -i FreeFEM_VERSION_Ubuntu_withPETSc_amd64.deb
Note
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
18 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
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 https://www.mpich.org/static/downloads/4.0.2/mpich-4.0.2.tar.gz --output␣
˓→mpich-4.0.2.tar.gz
7 tar xf mpich-4.0.2.tar.gz
8 cd mpich-4.0.2
˓→have/files/installed
˓→have/files/installed
5 make -j<nbProcs>
6 make 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 https://mirror.ibcp.fr/pub/gnu/gsl/gsl-2.7.tar.gz
2 tar zxvf gsl-2.7.tar.gz
3 cd gsl-2.7
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
Note
1 ./3rdparty/getall -a
Note
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
20 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
Note
it isn’t necessary to execute this last command, FreeFEM executable is avalaible here
your_installation/src/nw/FreeFem++ and mpi executable here your_installation/src/mpi/ff-mpirun.
Compilation on Ubuntu
1. Install the following packages on your system
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
Note
4. Configure
Note
1 ./3rdparty/getall -a
Note
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
Warning
Warning
FreeFEM fails to compile using the newest version of gcc 8.1.0, use an older one instead.
22 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
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
Note
1 ./3rdparty/getall -a
Note
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.
Note
Compilation on Fedora
1. Install the following packages on your system
3. Autoconf
1 cd FreeFem-sources
2 autoreconf -i
Note
4. Configure
Note
24 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
1 ./3rdparty/getall -a
Note
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
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.
4. In the MSYS2 shell, execute the following. Hint: if you right click the title bar, go to Options -> Keys and tick
“Ctrl+Shift+letter shortcuts” you can use Ctrl+Shift+V to paste in the MSYS shell.
1 pacman -Syuu
Close the MSYS2 shell once you’re asked to. There are now 3 MSYS subsystems installed: MSYS2, MinGW32
and MinGW64. They can respectively be launched from C:devmsys64msys2.exe, C:devmsys64mingw32.exe and
C:devmsys64mingw64.exe Reopen MSYS2 (doesn’t matter which version, since we’re merely installing packages).
Repeatedly run the following command until it says there are no further updates. You might have to restart your shell
again.
1 pacman -Syuu
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
26 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
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).
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"
1 /etc/freefem++.pref
2 $(HOME)/.freefem++.pref
3 freefem++.pref
• under windows
1 freefem++.pref
We can also use shell environment variables to change verbosity and the search rule before the init files.
1 export FF_VERBOSITY=50
2 export FF_INCLUDEPATH="dir;;dir2"
3 export FF_LOADPATH="dir;;dir3"
Note
The separator between directories must be “;” and not “:” because “:” is used under Windows.
Note
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/.
28 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
Emacs editor
For emacs editor you can download ff++-mode.el .
1.3 Download
1.3.1 Latest binary packages
FreeFEM v4.15 runs under macOS, Ubuntu, and 64-bit Windows.
Note
1.3. Download 29
FreeFEM Documentation, Release 4.13
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.
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
30 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
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 }
APA
ISO690
MLA
1.5. Citation 31
FreeFEM Documentation, Release 4.13
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/
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://joliv.et/
32 Chapter 1. Introduction
FreeFEM Documentation, Release 4.13
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
1.7.1 Bug report
Concerning the FreeFEM documentation
Open an Issue on FreeFem-doc repository.
1.7. Contributing 33
FreeFEM Documentation, Release 4.13
Warning
All code modifications, even in a pull request, must be done in the develop branch
Note
Please make sure your code modification is well writen and formatted (you can use clang-format if necessary)
34 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.
35
FreeFEM Documentation, Release 4.13
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.
Tip
for all 𝑣 which are in the finite element space 𝑉ℎ and zero on the boundary 𝐶.
Tip
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
Note
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𝑦
In FreeFEM the Poisson problem can be declared only as in:
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)
4 + dy(u)*dy(v)
5 )
6 + on(C, u=0)
7 ;
8 matrix A = a(Vh, Vh); //stiffness matrix
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.
𝜕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
𝜕 2 𝑢 𝜕𝑢 𝜕 2 𝜙 𝜕𝜙 𝜕 2 𝑢
− − =0
𝜕𝑡2 𝜕𝑥 𝜕𝑥2 𝜕𝑥 𝜕𝑥2
Tip
Tip
𝜕𝜙
− 𝜇∆𝜙 = 𝑓 ∀𝑥 ∈ Ω ⊂ R𝑑 , ∀𝑡 ∈]0, 𝑇 [
𝜕𝑡
Tip
𝜕2𝜙
− 𝜇∆𝜙 = 𝑓 in 𝑄.
𝜕𝑡2
Tip
𝜕𝜙
+ 𝑎∇𝜙 − 𝜇∆𝜙 = 𝑓
𝜕𝑡
Tip
∆(∆𝜙) = 𝑓 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.3. Membrane 45
FreeFEM Documentation, Release 4.13
𝜕2𝜙 𝜕2𝜙
∆𝜙 = +
𝜕𝑥21 𝜕𝑥22
Todo
Check references
With such “mixed boundary conditions” the problem has a unique solution (see Dautray-Lions (1988), Strang (1986)
and Raviart-Thomas (1983)). The easiest proof is to notice that 𝜙 is the state of least energy, i.e.
∫︁
1
𝐸(𝜑) = min 𝐸(𝑣), with 𝐸(𝑣) = ( |∇𝑣|2 − 𝑓 𝑣)
𝜙−𝑧∈𝑉 Ω 2
and where 𝑉 is the subspace of the Sobolev space 𝐻 1 (Ω) of functions which have zero trace on Γ1 . Recall that
(𝑥 ∈ R𝑑 , 𝑑 = 2 here):
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.
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
(continues on next page)
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
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.
2.3. Membrane 47
FreeFEM Documentation, Release 4.13
(a) Mesh of the ellipse (b) Level lines of the membrane deformation
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
5 real b=1.; //the length of the semiminor axis
6 func f = -4*(cos(x^2+y^2-1) - (x^2+y^2)*sin(x^2+y^2-1));
7 func phiexact = sin(x^2 + y^2 - 1);
8
9 // Mesh
10 border Gamma1(t=0., theta){x=a*cos(t); y=b*sin(t);}
11 border Gamma2(t=theta, 2.*pi){x=a*cos(t); y=b*sin(t);}
12
13 // Error loop
14 real[int] L2error(2); //an array of two values
(continues on next page)
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
4 times: compile 0.02s, execution 6.94s
We find a rate of 1.98 , which is not close enough to the 3 predicted by the theory.
The Geometry is always a polygon so we lose one order due to the geometry approximation in 𝑂(ℎ2 ).
Now if you are not satisfied with the .eps plot generated by FreeFEM and you want to use other graphic facilities,
then you must store the solution in a file very much like in C++. It will be useless if you don’t save the triangulation as
well, consequently you must do
1 {
(continues on next page)
2.3. Membrane 49
FreeFEM Documentation, Release 4.13
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"
10 }
11 }
We use the finite element numbering, where Wh(i,j) is the global index of 𝑗 𝑇 ℎ degrees of freedom of triangle number
𝑖.
Then open gnuplot and do:
This works with P2 and P1, but not with P1nc because the three first degrees of freedom of P1 or P2 are on vertices
and not with P1nc.
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 .
Here 𝑔 is any function of 𝑥 equal to 𝑢𝑖 on 𝐶𝑖 .
The second equation is a reduced form for:
𝑢 = 𝑢𝑖 on 𝐶𝑖 , 𝑖 = 0, 1.
The variational formulation for this problem is in the subspace 𝐻01 (Ω) ⊂ 𝐻 1 (Ω) of functions which have zero traces
on Γ.
∫︁
𝑢 − 𝑔 ∈ 𝐻01 (Ω) : ∇𝑢∇𝑣 = 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 )
41 )
42 +on(C0, u=20)
43 +on(C1, u=60)
44 ;
45
46 // Plot
47 plot(u, wait=true, value=true, fill=true, ps="HeatExchanger.eps");
boundary conditions in a readable way we assigned a label on the boundaries. As said earlier, borders have an
internal number corresponding to their order in the program (check it by adding a cout << C22; above). This
is essential to understand how a mesh can be output to a file and re-read (see below).
• As usual the mesh density is controlled by the number of vertices assigned to each boundary. It is not possible
to change the (uniform) distribution of vertices but a piece of boundary can always be cut in two or more parts,
for instance C12 could be replaced by C121+C122:
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");
(continues on next page)
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 Ω
𝜕𝑣
𝜕𝑛 |Γ =𝑔
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;}
(continues on next page)
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
2.5. Acoustics 55
FreeFEM Documentation, Release 4.13
32 cout << ev(0) << " 2 eigen values " << ev(1) << endl;
33 v = eV[0];
34 plot(v, wait=true, ps="eigen.eps");
(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 Γ}
𝜕𝑡 𝑢 − ∇ · (𝜅∇𝑢) =0 in Ω × (0, 𝑇 )
𝑢(𝑥, 𝑦, 0) = 𝑢0 + 𝑥𝑢1
𝜕𝑢
𝜅 𝜕𝑛 + 𝛼(𝑢 − 𝑢𝑒 ) =0 on Γ × (0, 𝑇 )
Here the diffusion 𝜅 will take two values, one below the middle horizontal line and ten times less above, so as to
simulate a thermostat.
The term 𝛼(𝑢 − 𝑢𝑒 ) accounts for the loss of temperature by convection in air. Mathematically this boundary condition
is of Fourier (or Robin, or mixed) type.
The variational formulation is in 𝐿2 (0, 𝑇 ; 𝐻 1 (Ω)); in loose terms and after applying an implicit Euler finite difference
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
38 ofstream ff("thermic.dat");
39 for(real t = 0; t < T; t += dt){
40 uold = u; //equivalent to u^{n-1} = u^n
41 thermic; //here the thermic problem is solved
42 ff << u(3., 0.5) << endl;
43 plot(u);
44 }
Note
We must separate by hand the bilinear part from the linear one.
Note
The way we store the temperature at point (3, 0.5) for all times in file thermic.dat. Should a one dimensional
plot be required (you can use gnuplot tools), the same procedure can be used. For instance to print 𝑥 ↦→ 𝜕𝑢
𝜕𝑦 (𝑥, 0.9)
one would do:
1 for(int i = 0; i < 20; i++)
2 cout << dy(u)(6.0*i/20.0,0.9) << endl;
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 .
𝜕𝑢
𝜅 + 𝛼(𝑢 − 𝑢𝑒 ) + 𝑐[(𝑢 + 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,
𝜕𝑛
because we have the identity 𝑎4 − 𝑏4 = (𝑎 − 𝑏)(𝑎 + 𝑏)(𝑎2 + 𝑏2 ).
The iterative process will work with 𝑣 = 𝑢 − 𝑢𝑒 .
1 ...
2 // Mesh
3 fespace Vh(Th, P1);
4 Vh vold, w, v=u0-ue, b,vp;
5
6 // Problem
7 problem thermradia(v, w)
8 = int2d(Th)(
9 v*w/dt
10 + k*(dx(v) * dx(w) + dy(v) * dy(w))
11 )
12 + int1d(Th, 1, 3)(
13 b*v*w
14 )
15 - int2d(Th)(
(continues on next page)
36 plot(v);
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:
An equation for the upper surface of a NACA0012 (this is a classical wing profile in aerodynamics) is:
√
𝑦 = 0.17735 𝑥 − 0.075597𝑥 − 0.212836𝑥2 + 0.17363𝑥3 − 0.06254𝑥4 .
1 // Parameters
2 int S = 99;// wing label
3 // u infty
4 real theta = 8*pi/180;// // 1 degree on incidence => lift
5 real lift = theta*0.151952/0.0872665; // lift approximation formula
6 real uinfty1= cos(theta), uinfty2= sin(theta);
7 // Mesh
8 func naca12 = 0.17735*sqrt(x) - 0.075597*x - 0.212836*(x^2) + 0.17363*(x^3) - 0.06254*(x^
˓→4);
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.
𝜕𝑣
𝜕𝑡 𝑣 − ∇ · (𝜅∇𝑣) + 𝑢 · ∇𝑣 = 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
24 real dt = 0.005, nbT = 50;
25
26 // Mesh
27 border D(t=0, 2){x=1+t; y=0;}
28 mesh Sh = buildmesh(C(25) + Splus(-90) + Sminus(-90) + D(200));
29 int steel = Sh(0.5, 0).region, air = Sh(-1, 0).region;
30 // Change label to put BC on In flow
31 // Fespace
32 fespace Wh(Sh, P1);
33 Wh vv;
34
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.
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.
𝑥˙ 𝜏 = 𝑢(𝑥𝜏 ), 𝑥𝜏 =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
13 // Time loop
14 real t = 0;
15 for (int m = 0; m < 2.*pi/dt; m++){
16 t += dt;
17 cold = c;
18 c = convect([u1, u2], -dt, cold);
19 plot(c, cmm=" t="+t +", min="+c[].min+", max="+c[].max);
20 }
Note
3D plots can be done by adding the qualifyer dim=3 to the plot instruction.
𝑐𝑛+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)
23 )
24 - int2d(Th)(
25 ccold*w/dt
26 )
27 ;
28
29 // Time iterations
30 for (real t = 0.; t < 2.*pi; t += dt){
31 ccold = cc;
32 Adual;
33 plot(cc, fill=1, cmm="t="+t+", min="+cc[].min+", max="+ cc[].max);
34 }
35
36 // Plot
37 real [int] viso = [-0.2, -0.1, 0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1];
38 plot(cc, wait=1, fill=1, ps="ConvectCG.eps", viso=viso);
39 plot(cc, wait=1, fill=1, ps="ConvectDG.eps", viso=viso);
Note
(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.
(a) The rotating hill after one revolution with (b) The rotating hill after one revolution with Discontinuous
Characteristics-Galerkin 𝑃1 Galerkin
1 // Mesh
2 border C(t=0., 2.*pi) {x=cos(t); y=sin(t);};
3 mesh Th = buildmesh(C(100));
4
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
−𝜇∆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
8 mesh Th = square(10, 10, [20*x,2*y-1]);
9
10 // Fespace
11 fespace Vh(Th, P2);
12 Vh u, v;
13 Vh uu, vv;
14
15 // Macro
16 real sqrt2=sqrt(2.);
17 macro epsilon(u1,u2) [dx(u1),dy(u2),(dy(u1)+dx(u2))/sqrt2] //
18 // The sqrt2 is because we want: epsilon(u1,u2)'* epsilon(v1,v2) = epsilon(u): epsilon(v)
19 macro div(u,v) ( dx(u)+dy(v) ) //
20
21 // Problem
22 real mu= E/(2*(1+nu));
23 real lambda = E*nu/((1+nu)*(1-2*nu));
24
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.
−∆u + ∇𝑝 = 0
∇·u = 0
The driven cavity is a standard test. It is a box full of liquid with its lid moving horizontally at speed one. The pressure
and the velocity must be discretized in compatible fintie element spaces for the LBB conditions to be satisfied:
(u, ∇𝑝)
sup ≥ 𝛽|u| ∀u ∈ 𝑈ℎ
𝑝∈𝑃ℎ |𝑝|
1 // Parameters
2 int nn = 30;
3
4 // Mesh
5 mesh Th = square(nn, nn);
6
7 // Fespace
8 fespace Uh(Th, P1b);
9 Uh u, v;
10 Uh uu, vv;
11
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))
25 - 1e-10*p*pp
26 )
27 + on(1, 2, 4, u=0, v=0)
28 + on(3, u=1, v=0)
29 ;
30
31 // Plot
32 plot([u, v], p, wait=1);
Note
We add a stabilization term −10e − 10 * p * pp to fix the constant part of the pressure.
Fig. 2.12: Solution of Stokes’ equations for the driven cavity problem, showing the velocity field and the pressure level
lines.
𝜕𝑡 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
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;}
(continues on next page)
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))
(continues on next page)
72 plot(u);
73
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.
(b) Pressure
(c) Velocity
(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
Ω
2. 𝑢𝑖+1 = 𝑢𝑖 − 𝑤𝑖 ;
break ||𝑤𝑖 || < 𝜀.
Where 𝐷𝐹 (𝑢) is the differential of 𝐹 at point 𝑢, this is a linear application such that:
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;}
17 border beu(tt=1, 0){real t=tt^1.2; x=t*L; y=R; label=1;}
18 border beo(t=-R, R){x=L; y=t; label=0;}
19 border bei(t=-R/4, R/4){x=L/2; y=t; label=0;}
20 mesh Th = buildmesh(cc(-50) + ce(30) + beb(20) + beu(20) + beo(10) + bei(10));
21 plot(Th);
22
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
(continues on next page)
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
69 - div(v1,v2) * p
70 )
71 + on(1, du1=0, du2=0)
72 ;
73
74 u1[] -= du1[];
75 u2[] -= du2[];
76 p[] -= dp[];
77
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;
(continues on next page)
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 𝜈.
𝜈 is gradually increased to the desired value.
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
53 // Macro
54 macro grad(u) [dx(u), dy(u)] //
55 macro Grad(U) [grad(U#x), grad(U#y)] //
56 macro Div(U) (dx(U#x) + dy(U#y)) //
57
58 // Functions
59 func g = (x) * (1-x) * 4;
60
61 // Problem
62 real alpha = 0.;
63
64 problem Temperature(T, q)
65 = int2d(Th)(
66 alpha * T * q
67 + kappa* grad(T)' * grad(q)
68 )
69 + int2d(Th)(
70 - alpha*convect([Upx, Upy], -dt, Tp)*q
71 )
(continues on next page)
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;
(continues on next page)
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;
208 tol = tol * coeftol;
209 cut = cut * coefcut;
210 }
211 cout << "Total Time = " << clock()-T0 << endl;
(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
𝛽𝑣 + ∆𝑣 = 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
31 // Fespace
32 fespace Vh(Th, P1);
33 Vh R=(region-air)/(meat-air);
34 Vh<complex> v, w;
35 Vh vr, vi;
(continues on next page)
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
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;
29 ud = u;
30
31 ofstream f("J.txt");
32 func real J(real[int] & Z){
33 for (int i = 0; i < z.n; i++)
34 z[i] =Z[i];
35 A;
36 real s = int2d(th)(Ie*(u-ud)^2);
37 f << s << " ";
38 return s;
39 }
40
41 // Problem BFGS
42 real[int] dz(3), dJdz(3);
(continues on next page)
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.
Note that an adjoint state could have been used. Define 𝑝 by:
−∇ · (𝜅∇𝑝) = 2𝐼𝐸 (𝑢 − 𝑢𝑑 )
𝑝|Γ = 0
Consequently:
∫︀
𝛿𝐽 = −
∫︀ Ω (∇ · (𝜅∇𝑝))𝛿𝑢
= Ω∫︀(𝜅∇𝑝 · ∇𝛿𝑢)
= − Ω (𝛿𝜅∇𝑝 · ∇𝑢)
Then the derivatives are found by setting 𝛿𝑏 = 1, 𝛿𝑐 = 𝛿𝑑 = 0 and so on:
𝐽𝑏′ = − ∫︀𝐵 ∇𝑝 · ∇𝑢
∫︀
𝐽𝑐′ = − ∫︀𝐶 ∇𝑝 · ∇𝑢
𝐽𝑑′ = − 𝐷 ∇𝑝 · ∇𝑢
Note
As BFGS stores an 𝑀 × 𝑀 matrix where 𝑀 is the number of unknowns, it is dangerously expensive to use this
method when the unknown 𝑥 is a Finite Element Function. One should use another optimizer such as the NonLinear
Conjugate Gradient NLCG (also a key word of FreeFEM).
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
36 // Macro
37 macro dn(u) (N.x*dx(u)+N.y*dy(u)) //
38
39 // Initialization
40 if(anew){
41 u1 = u0;
42 v1 = 0;
43 r1 = 1;
44 }
45 else{
46 ifstream g("u.txt"); g >> u1[];
47 ifstream gg("v.txt"); gg >> v1[];
(continues on next page)
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;
88 v1=v;
89 r1=abs(r);
90 cout << "k = " << k << " E = " << int2d(Th)(u^2+v^2+r) << endl;
91 plot(r, value=1);
92 }
93
94 // Mesh adaptation
95 Th = adaptmesh (Th, r, nbvx=40000, err=err0, abserror=1, nbjacoby=2, omega=1.8,␣
˓→ratio=1.8, nbsmooth=3, splitpbedge=1, maxsubdiv=5, rescaling=1);
96 plot(Th);
97 u = u;
98 v = v;
(continues on next page)
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 }
Fig. 2.18: Pressure for a Euler flow around a disk at Mach 2 computed by (2.6)
(0,T).
𝜕𝑡 𝑢 − ∇ · (𝜅∇𝑢) = 0 in Ω × (0, 𝑇 )
𝑢(𝑥, 𝑦, 0) = 𝑢0 + 𝑥𝑢1
𝑢 = 30 on Γ24 × (0, 𝑇 )
𝜕𝑢
𝜅 𝜕𝑛 + 𝛼(𝑢 − 𝑢𝑒 ) = 0 on Γ × (0, 𝑇 )
𝑏𝑖 if 𝑖 ∈ Γ24
{︂ ′′
1
𝑢𝑛 = 𝐴−1 𝑏𝑛 , 𝑏′ = 𝑏0 + 𝑀 𝑢𝑛−1 , 𝑏” = 𝑏𝑐𝑙 , 𝑏𝑛𝑖 =
𝜀 𝑏′𝑖 else
Where with 1
𝜀 = tgv = 1030 :
1
if 𝑖 ∈ Γ24 , and 𝑗 = 𝑖
⎧
⎨ ∫︁ 𝜀 ∫︁
𝐴𝑖𝑗 =
⎩ 𝑤𝑗 𝑤𝑖 /𝑑𝑡 + 𝑘(∇𝑤𝑗 .∇𝑤𝑖 ) + 𝛼𝑤𝑗 𝑤𝑖 else
⎧ Ω Γ13
⎨ ∫︁ 1𝜀 if 𝑖 ∈ Γ24 , and 𝑗 = 𝑖
𝑀𝑖𝑗 =
⎩ 𝑛 𝑤𝑗 𝑤𝑖 /𝑑𝑡 else
∫︀ Ω
𝑏0,𝑖 = 𝑛 Γ13 𝛼𝑢𝑢𝑒 𝑤𝑖
𝑏𝑐𝑙 = 𝑢0 the initial data
1 ...
2 Vh u0=fu0, u=u0;
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))
(continues on next page)
35 // Time loop
36 int m, M = T/dt;
37 for(m = 0; m < M; m++){
38 stokes;
39 uold = u;
40 vold = v;
41 }
42
43 // Plot
44 plot(p, [u, v], value=true, wait=true, cmm="t="+m*dt);
Every iteration is in fact of the form 𝐴[𝑢, 𝑣, 𝑝] = 𝐵[𝑢𝑜𝑙𝑑, 𝑣𝑜𝑙𝑑, 𝑝𝑜𝑙𝑑] + 𝑏 where 𝐴, 𝐵 are matrices and 𝑏 is a vector
containing the boundary conditions. 𝐴, 𝐵, 𝑏 are constructed by:
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.
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);
There is currently no wifi hotspot, and as we want to resolve the equation for a multiple number of position next to the
left wall, let’s do a for loop:
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 !
1 fespace Vh(Th, P1);
2 func real wall() {
3 if (Th(x,y).region == Th(0.5,0.5).region || Th(x,y).region == Th(7,20.5).region ||␣
˓→Th(x,y).region == Th(30.5,2).region) { return 1; }
4 else { return 0; }
5 }
(continues on next page)
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";
16
17 problem muwave(v,w) =
18 int2d(Th)(
19 (v*w*k^2)/(1+(wallreflexion+wallabsorption)*wall())^2
20 - (dx(v)*dx(w)+dy(v)*dy(w))
21 )
22 + on(2, v=1)
23 ;
24
25 muwave;
26 Vh vm = log(real(v)^2 + imag(v)^2);
27 plot(vm, wait=1, fill=true, value=0, nbiso=65);
28 }
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;
25
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);
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");
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');
3 lighting gouraud;
4 view([-47,24]);
5 camlight('headlight');
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]);
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) to solve numerically systems of partial differential equations (PDE) in dimension 1,2 3 and surfaces
embedded in a 3D domain and lines embedded in a 2D or 3D. It is the ideal tool for teaching the finite element method
but it is also perfect for research to quickly prototype and test new algorithmic ideas or solve multi-physics complex
applications.
FreeFEM has an advanced automatic mesh generator, capable of a posteriori mesh adaptivity; it has a general purpose
elliptic solver interfaced with fast algorithms, such as the multi-frontal method UMFPACK, SuperLU, MUMPS etc.
Hyperbolic and parabolic problems are solved by iterative algorithms prescribed by the user with the high level language
of FreeFEM. It has several triangular or tetraedral 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, from within or using an external application like paraview.
This manual is meant for students at a Masters level or more, 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, based on the Finite Element Method. As its name implies,
it is a free software (see the copyrights for full detail) it is not a package, it is an integrated product with its own high
level programming language, referred below as a :index freefem script. This software runs on all UNIX OS (with g++
3.3 or later, and OpenGL), on Window XP, Vista and 7, 8, 10 and 11 and on MacOS 10 intel and arm.
Moreover FreeFEM is highly adaptive. Many phenomena involve several coupled systems, such as: Fluid-structure
interactions, Lorentz forces for aluminum casting and ocean-atmosphere problems, etc. 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 and three
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 and three-dimensional static or time dependent, linear or nonlinear 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.
109
FreeFEM Documentation, Release 4.13
• Automatic mesh generator, based on the Delaunay-Voronoi algorithm; the inner point density is proportional to
the density of points on the boundaries [GEORGE1996].
• Metric-based anisotropic mesh adaptation. The metric can be computed automatically from the Hessian of any
FreeFEM function [HECHT1998].
• High level user friendly typed input language with an algebra of analytic and finite element functions.
• Multiple finite element meshes within one application with automatic interpolation of data on different meshes
and possible storage of the interpolation matrices.
• A large variety of triangular finite elements: linear, quadratic Lagrangian elements and more, discontinuous P1
and Raviart-Thomas elements, elements of a non-scalar type, the mini-element,. . . (but no quadrangles).
• Tools to define discontinuous Galerkin finite element formulations P0, P1dc, P2dc and keywords: jump, mean,
intalledges.
• A large variety of linear direct and iterative solvers (LU, Cholesky, Crout, CG, GMRES, UMFPACK, MUMPS,
SuperLU, . . . ) and eigenvalue and eigenvector solvers (ARPARK) .
• Near optimal execution speed (compared with compiled C++ implementations programmed directly).
• Online graphics, generation of ,.txt,.eps,.gnu, mesh files for further manipulations of input and output data.
• Many examples and tutorials: elliptic, parabolic and hyperbolic problems, Navier-Stokes flows, elasticity, fluid
structure interactions, Schwarz’s domain decomposition method, eigenvalue problem, residual error indicator,
...
• A parallel version using MPI
3.1 Notations
Here mathematical expressions and corresponding FreeFEM commands are explained.
3.1.1 Generalities
• [𝛿𝑖𝑗 ] Kronecker delta (0 if 𝑖 ̸= 𝑗, 1 if 𝑖 = 𝑗 for integers 𝑖, 𝑗)
• [∀] for all
• [∃] there exists
• [i.e.] that is
• [PDE] partial differential equation (with boundary conditions)
• [∅] the empty set
• [N] the set of integers (𝑎 ∈ N ⇔ int a), int means long int inside FreeFEM
• [R] the set of real numbers (𝑎 ∈ R ⇔ real a), double inside FreeFEM
• [C] the set of complex numbers (𝑎 ∈ C ⇔ complex a), complex<double>
• [R𝑑 ] 𝑑-dimensional Euclidean space
• [𝐸 ∖ 𝐴] the set {𝑥 ∈ 𝐸| 𝑥 ̸∈ 𝐴}
• [𝐸 + 𝐹 ] 𝐸 ∪ 𝐹 with 𝐸 ∩ 𝐹 = ∅
• [𝐸 × 𝐹 ] the Cartesian product of 𝐸 and 𝐹
• [𝐸 𝑛 ] the 𝑛-th power of 𝐸 (𝐸 2 = 𝐸 × 𝐸, 𝐸 𝑛 = 𝐸 × 𝐸 𝑛−1 )
• [𝑓 : 𝐸 → 𝐹 ] the mapping form 𝐸 into 𝐹 , i.e., 𝐸 ∋ 𝑥 ↦→ 𝑓 (𝑥) ∈ 𝐹
• [𝐼𝐸 or 𝐼] the identity mapping in 𝐸,i.e., 𝐼(𝑥) = 𝑥 ∀𝑥 ∈ 𝐸
• [𝑓 ∘ 𝑔] for 𝑓 : 𝐹 → 𝐺 and 𝑔 : 𝐸 → 𝐹 , 𝐸 ∋ 𝑥 ↦→ (𝑓 ∘ 𝑔)(𝑥) = 𝑓 (𝑔(𝑥)) ∈ 𝐺 (see Elementary function)
• [𝑓 |𝐴 ] the restriction of 𝑓 : 𝐸 → 𝐹 to the subset 𝐴 of 𝐸
• [{𝑎𝑘 }] column vector with components 𝑎𝑘
• [(𝑎𝑘 )] row vector with components 𝑎𝑘
• [(𝑎𝑘 )𝑇 ] denotes the transpose of a matrix (𝑎𝑘 ), and is {𝑎𝑘 }
• [{𝑎𝑖𝑗 }] matrix with components 𝑎𝑖𝑗 , and (𝑎𝑖𝑗 )𝑇 = (𝑎𝑗𝑖 )
3.1.3 Numbers
For two real numbers 𝑎, 𝑏
• [𝑎, 𝑏] is the interval {𝑥 ∈ R| 𝑎 ≤ 𝑥 ≤ 𝑏}
• ]𝑎, 𝑏] is the interval {𝑥 ∈ R| 𝑎 < 𝑥 ≤ 𝑏}
• [𝑎, 𝑏[ is the interval {𝑥 ∈ R| 𝑎 ≤ 𝑥 < 𝑏}
• ]𝑎, 𝑏[ is the interval {𝑥 ∈ R| 𝑎 < 𝑥 < 𝑏}
3.1.5 Meshes
• [Ω] usually denotes a domain on which PDE is defined
• [Γ] denotes the boundary of Ω,i.e., Γ = 𝜕Ω (keyword border, see Border)
• [𝒯ℎ ] the triangulation of Ω, i.e., the set of triangles 𝑇𝑘 , where ℎ stands for mesh size (keyword mesh, buildmesh,
see Mesh Generation)
• [𝑛𝑡 ] the number of triangles in 𝒯ℎ (get by Th.nt)
• [Ωℎ ] denotes the approximated domain Ωℎ = ∪𝑛𝑘=1
𝑡
𝑇𝑘 of Ω. If Ω is polygonal domain, then it will be Ω = Ωℎ
• [Γℎ ] the boundary of Ωℎ
• [𝑛𝑣 ] the number of vertices in 𝒯ℎ (get by Th.nv)
• [𝑛𝑏𝑒 ] the number of boundary element in 𝒯ℎ (get by Th.nbe)
• [|Ωℎ |] the measure (area or volume) in 𝒯ℎ (get by Th.measure)
• [|𝜕Ωℎ |] the measure of the border (length or area) in 𝒯ℎ (get by Th.bordermeasure)
(︂∫︁ )︂1/2
norm: ‖𝑤‖0,Ω = |𝑤(𝑥, 𝑦)| d𝑥d𝑦
2
Ω
∫︁
scalar product: (𝑣, 𝑤) = 𝑣𝑤
Ω
{︂ ⃒ ∫︁ }︂
• [𝐻 1 (Ω)] the set 𝑤 ∈ 𝐿2 (Ω) ⃒⃒ |𝜕𝑤/𝜕𝑥|2 + |𝜕𝑤/𝜕𝑦|2 d𝑥d𝑦 < ∞
⃒ (︀ )︀
Ω
)︀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 (Ω)
• 3D border meshes
– 3D surface meshS
– 3D curve meshL
Through this presentation, the principal commands for the mesh generation and links between mesh - mesh3 - meshS
- meshL are described.
1 real x0 = 1.2;
2 real x1 = 1.8;
3 real y0 = 0;
4 real y1 = 1;
5 int n = 5;
6 real m = 20;
7 mesh Th = square(n, m, [x0+(x1-x0)*x, y0+(y1-y0)*y]);
Note
0. will produce a mesh where all quads are split with diagonal 𝑥 − 𝑦 = 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡
1. will produce a Union Jack flag type of mesh
2. will produce a mesh where all quads are split with diagonal 𝑥 + 𝑦 = 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡
3. same as in case 0, except two corners where the triangles are the same as case 2, to avoid having 3 vertices
on the boundary
4. same as in case 2, except two corners where the triangles are the same as case 0, to avoid having 3 vertices
on the boundary
1 mesh Th = square(n, m, [x0+(x1-x0)*x, y0+(y1-y0)*y], flags=icase);
Note
Adding the named parameter label=labs will change the 4 default label numbers to labs[i-1], for example
int[int] labs=[11, 12, 13, 14], and adding the named parameter region=10 will change the region num-
ber to 10, for instance (v 3.8).
To see all of these flags at work, check Square mesh example:
1 for (int i = 0; i < 5; ++i){
2 int[int] labs = [11, 12, 13, 14];
3 mesh Th = square(3, 3, flags=i, label=labs, region=10);
4 plot(Th, wait=1, cmm="square flags = "+i );
5 }
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`
9 border bb(t=0, 1; i)
10 {
11 // i is the index variable of the multi border loop
12 int ii = (i+1)%4;
13 real t1 = 1-t;
14 x = xx[i]*t1 + xx[ii]*t;
15 y = yy[i]*t1 + yy[ii]*t;
16 label = 0;
17 }
18
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;
(continues on next page)
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;
78 int l=1;
79 int e=1;
80
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
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.
7 // Fespace
8 fespace Vhxy(Thxy, P1);
9 Vhxy fxy;
10
20 // Plot
21 plot(fxy);
One new way to build a mesh is to have two arrays: one for the 𝑥 values and the other for the 𝑦 values.
1 {
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;
(continues on next page)
(a) The empty mesh with boundary (b) An empty mesh defined from a pseudo region numbering
of triangle
Remeshing
The command movemesh
Meshes can be translated, rotated, and deformed by movemesh; this is useful for elasticity to watch the deformation
due to the displacement Φ(𝑥, 𝑦) = (Φ1 (𝑥, 𝑦), Φ2 (𝑥, 𝑦)) of shape.
It is also useful to handle free boundary problems or optimal shape problems.
If Ω is triangulated as 𝑇ℎ (Ω), and Φ is a displacement vector then Φ(𝑇ℎ ) is obtained by:
Sometimes the transformed mesh is invalid because some triangles have flipped over (meaning it now has a negative
area). To spot such problems, one may check the minimum triangle area in the transformed mesh with checkmovemesh
before any real transformation.
For example:
Φ1 (𝑥, 𝑦) = 𝑥 + 𝑘 * sin(𝑦 * 𝜋)/10)
Φ2 (𝑥, 𝑦) = 𝑦 + 𝑘 * cos(𝑦𝜋)/10)
for a big number 𝑘 > 1.
1 verbosity = 4;
2
3 // Parameters
4 real coef = 1;
5
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
25 u = 0;//to change the FEspace and mesh associated with u
26 u[] = tmp;//set the value of u without any mesh update
27 plot(Th, u, wait=true);
28 }
29 // In this program, since u is only defined on the last mesh, all the
30 // previous meshes are deleted from memory.
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
15 // Adaptmesh
16 for (int i = 0; i < 2; i++){
17 Th = adaptmesh(Th, fh);
18 fh = f; //old mesh is deleted
19 plot(Th, fh, wait=true);
20 }
Fig. 3.8: 3D graphs for the initial mesh and 1st and 2nd mesh adaptations
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
31 // Adaptmesh loop
32 for (int i = 0; i < 4; i++){
33 Poisson;
34 Th = adaptmesh(Th, u, err=error);
35 error = error/2;
36 }
37
38 // Plot
39 plot(u);
To speed up the adaptation, the default parameter err of adaptmesh is changed by hand; it specifies the required
precision, so as to make the new mesh finer or coarser.
The problem is coercive and symmetric, so the linear system can be solved with the conjugate gradient method (pa-
rameter solver=CG) with the stopping criteria on the residual, here eps=1.e-6).
By adaptmesh, the slope of the final solution is correctly computed near the point of intersection of 𝑏𝑐 and 𝑏𝑑 as in
Fig. 3.9b.
This method is described in detail in [HECHT1998]. It has a number of default parameters which can be modified.
If f1,f2 are functions and thold, Thnew are meshes:
1 Thnew = adaptmesh(Thold, f1 ... );
2 Thnew = adaptmesh(Thold, f1,f2 ... ]);
(continues on next page)
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 fi-
nite 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
(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
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
• 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
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
Tip
NACA0012 Airfoil
1 border upper(t=0, 1){x=t; y=0.17735*sqrt(t) - 0.075597*t - 0.212836*(t^2) + 0.
˓→17363*(t^3) - 0.06254*(t^4);}
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);
Tip
Cassini Egg
1 border C(t=0, 2*pi) {x=(2*cos(2*t)+3)*cos(t); y=(2*cos(2*t)+3)*sin(t);}
2 mesh Th = buildmesh(C(50));
3 plot(Th, ps="Cassini.eps", bw=true);
Tip
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);
Tip
Section of Engine
1 real a = 6., b = 1., c = 0.5;
2
Tip
Tip
Tip
Smiling face
1 real d=0.1; int m = 5; real a = 1.5, b = 2, c = 0.7, e = 0.01;
2
Tip
3 points bending
1 // Square for Three-Point Bend Specimens fixed on Fix1, Fix2
2 // It will be loaded on Load.
3 real a = 1, b = 5, c = 0.1;
4 int n = 5, m = b*n;
5 border Left(t=0, 2*a){x=-b; y=a-t;}
6 border Bot1(t=0, b/2-c){x=-b+t; y=-a;}
7 border Fix1(t=0, 2*c){x=-b/2-c+t; y=-a;}
8 border Bot2(t=0, b-2*c){x=-b/2+c+t; y=-a;}
9 border Fix2(t=0, 2*c){x=b/2-c+t; y=-a;}
10 border Bot3(t=0, b/2-c){x=b/2+c+t; y=-a;}
11 border Right(t=0, 2*a){x=b; y=-a+t;}
12 border Top1(t=0, b-c){x=b-t; y=a;}
13 border Load(t=0, 2*c){x=c-t; y=a;}
14 border Top2(t=0, b-c){x=-c-t; y=a;}
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 .
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
8 Label = 40, s = 4 011 : 40
9 Label = 42, s = 4 211 : 42
10 Label = 45, s = 4 121 : 45
11 Label = 57, s = 4 112 : 57
12 Volume region = 11: 8
13 Nb err = 0
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 ).
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 }
Tip
Unit cube
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);
16
17 // 3D mesh
18 real h = 1./nn;
19 int MaxLayersT = (int(2*pi*RR/h)/4)*4;//number of layers
20 real zminT = 0;
21 real zmaxT = 2*pi; //height 2*pi
22 func fx = y*cos(z);
23 func fy = y*sin(z);
24 func fz = x;
25 int[int] r1T = [0,0], r2T = [0,0,2,2], r4T = [0,2];
26 //trick function:
27 //The function defined the proportion
28 //of number layer close to axis with reference MaxLayersT
29 func deg = max(.01, y/max(x/HH, 0.4)/RR);
30 mesh3 Th3T = buildlayers(Th2, coef=deg, MaxLayersT,
31 zbound=[zminT, zmaxT], transfo=[fx, fy, fz],
32 facemerge=0, region=r1T, labelmid=r2T);
33 medit("cone", Th3T);
Tip
Buildlayer mesh
1 load "msh3"
2 load "TetGen"
3 load "medit"
4
5 // Parameters
6 int C1 = 99;
7 int C2 = 98;
8
9 // 2D mesh
10 border C01(t=0, pi){x=t; y=0; label=1;}
11 border C02(t=0, 2*pi){ x=pi; y=t; label=1;}
12 border C03(t=0, pi){ x=pi-t; y=2*pi; label=1;}
13 border C04(t=0, 2*pi){ x=0; y=2*pi-t; label=1;}
14
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;
(continues on next page)
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;
(continues on next page)
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
4 mesh3 Th = cube(nn, nn, nn, label=labs);
5 // extract the surface (boundary) of the cube
6 int[int] llabs = [1, 2];
7 meshS ThS = extract(Th,label=llabs);
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
5 real x10 = 1.;
6 real x11 = 2.;
7 real y10 = 0.;
8 real y11 = 2.*pi;
9
10 func ZZ1min = 0;
(continues on next page)
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);
Tip
Hole region
1 load "msh3"
2 load "TetGen"
3 load "medit"
4
5 // 2D mesh
6 mesh Th = square(10, 20, [x*pi-pi/2, 2*y*pi]); // ]-pi/2, pi/2[X]0,2pi[
7
8 // 3D mesh
9 //parametrization of a sphere
10 func f1 = cos(x)*cos(y);
11 func f2 = cos(x)*sin(y);
12 func f3 = sin(x);
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",
60 nbofholes=1, holelist=hole, nbofregions=1, regionlist=domain);
61 medit("Sphere with a hole", Th3finhole);
Tip
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
(a) The surface mesh of the hex with internal sphere (b) The tetrahedral mesh of the cube with internal ball
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
R⊯ . 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 R⊭ to R⊯ . 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 R⊭ to R⊯ . 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
1 extern (-1 intern)
1 real R = 3, r=1;
2 real h = 0.2; //
3 int nx = R*2*pi/h;
4 int ny = r*2*pi/h;
5 func torex= (R+r*cos(y*pi*2))*cos(x*pi*2);
6 func torey= (R+r*cos(y*pi*2))*sin(x*pi*2);
7 func torez= r*sin(y*pi*2);
8
10 meshS ThS=square3(nx,ny,[torex,torey,torez],orientation=-1) ;
The following code generates a 3 × 4 × 5 grid in the unit cube [0, 1]3 with a clock wise triangulation.
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)
– h is the mesh size
– L is the label
⃒
𝜋 𝜋 ⃒ 𝑥=Rx 𝑐𝑜𝑠(𝑢)𝑐𝑜𝑠(𝑣)+Ox
∀𝑢 ∈ [− , [ and 𝑣 ∈ [0, 2𝜋], ⃒⃒ 𝑦=Ry 𝑐𝑜𝑠(𝑢)𝑠𝑖𝑛(𝑣)+Oy
2 2 𝑧=Rz 𝑠𝑖𝑛(𝑣)+Oz
1 func meshS SurfaceHex(int[int] & N,real[int,int] &B ,int[int,int] & L,int orientation){
2 real x0=B(0,0),x1=B(0,1);
3 real y0=B(1,0),y1=B(1,1);
4 real z0=B(2,0),z1=B(2,1);
5
6 int nx=N[0],ny=N[1],nz=N[2];
7
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[ $
29 // a parametrization of a sphere
30 func f1 =RX*cos(x)*cos(y);
(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 R⊯ 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;};
9 mesh th= buildmesh(a(n)+b(n)+c(n)+d(n)+i(-n));
10 meshS Th= movemesh23(th,transfo=[x,y,cos(x)^2+sin(y)^2]);
Remeshing
The command trunc
This operator allows to define a meshS by truncating another one, i.e. by removing triangles, and/or by splitting each
triangle by a given positive integer s. In a FreeFEM script, this function must be called as follows:
meshS TS2= trunc (TS1, boolean function to keep or remove elements, split = s, label = . . . )
The command has the following arguments:
• boolean function to keep or remove elements
• split= sets the level n of triangle splitting. each triangle is splitted in n × n ( one by default)
• label= sets the label number of new boundary item (1 by default)
An example of how to call the function
1 real R = 3, r=1;
2 real h = 0.2; //
3 int nx = R*2*pi/h;
4 int ny = r*2*pi/h;
5 func torex= (R+r*cos(y*pi*2))*cos(x*pi*2);
6 func torey= (R+r*cos(y*pi*2))*sin(x*pi*2);
7 func torez= r*sin(y*pi*2);
8 // build a tore
9 meshS ThS=square3(nx,ny,[torex,torey,torez]) ;
10 ThS=trunc(ThS, (x < 0.5) | (y < 0.5) | (z > 1.), split=4);
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.
Equivalent for a 2d or 3d mesh, the command change changes the label of elements and border elements of a meshS.
The parameters for this command line are:
• reftri= is a vector of integer that contains successive pairs of the old label number to the new label number for
elements.
• refedge= is a vector of integer that contains successive pairs of the old region number to new region number
for boundary elements.
• flabel= is an integer function given the new value of the label.
• fregion= is an integer function given the new value of the region.
• rmledges= is a vector of integer, where edge’s label given are remove of the mesh
These vectors are composed of 𝑛𝑙 successive pairs of numbers 𝑂, 𝑁 where 𝑛𝑙 is the number (label or region) that we
want to change. For example, we have:
label = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]
region = [𝑂1 , 𝑁1 , ..., 𝑂𝑛𝑙 , 𝑁𝑛𝑙 ]
• 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:
• Th3 ⊂ ThS where it exactly describes the bounder of Th3.
• a mehS is an explicite surface mesh given by a list of vertices, triangle finite elements and boundary edge elements
(can be optional follows the geometry domain)
Note
Hence, if an input mesh (.msh freefem or .mesh format) contains a list of vertices, tetrahedra, triangles and edges,
FreeFEM builds a mesh3 whitch contains explicitly a surface mesh type meshS.
The command Gamma 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");
4 savesurfacemesh(Th3, "surfreal.mesh");
5 mesh3 ThS3 = trunc(Th3, 1, split=3);
6 meshS ThSS = ThS3.Gamma;
7 savesurfacemesh(ThS3, "surfacesplit.mesh");
8 savemesh(ThSS,"GammaSplit.mesh" );
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.
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
The command trunc
This operator allows to define a meshL by truncating another one, i.e. by removing segments, and/or by splitting each
element by a given positive integer s. Here, an example to use this function:
meshL ThL2= trunc (ThL1, boolean function to keep or remove elements, split = s, label = . . . )
The command has the following arguments:
• boolean function to keep or remove elements
• split= sets the level n of edge splitting, each edge is splitted in n subpart( one by default)
• label= sets the label number of new boundary item (1 by default)
• new2old
• old2new
• renum
• orientation=
equal 1, gives the orientation of the triangulation, elements are in the reference orientation (counter clock
wise) equal -1 reverse the orientation of the triangles it’s the global orientation of the surface 1 extern (-1
intern)
• cleanmesh= is a boolean, allowing remove the duplicated nodes
• removeduplicate= is a boolean, allowing remove the duplicated elements and border elements
• precismesh this parameter is the criteria to define two merging points.
By default, it value is 1e-7 and define 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 define 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
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 89 * 𝑎𝑟𝑐𝑡𝑎𝑛(1) ≈ 40, the diedral angle for a decahedron.
1 mesh3 Th = checkmesh(Th);
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 Triangles.
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 vector 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.
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;
21
22 func perio=[[4,y],[2,y],[1,x],[3,x]];
23 real hh=0.1;
(continues on next page)
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
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
format of mesh data
In three dimensions, the file mesh format supported for input and output files by FreeFEM are the extension .msh and
.mesh. These formats are described in the Mesh Format section.
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
format of mesh data
Like 2d and 3d, the input and output format files supported by FreeFEM are the extension .msh and .mesh. These
formats are described in the Mesh Format section.
Extension file .msh The structure of the files with extension .msh in surface 3D is given by:
𝑛𝑣 𝑛𝑡𝑟𝑖 𝑛𝑒𝑑𝑔𝑒𝑠
𝑞𝑥1 𝑞𝑦1 𝑞𝑧1 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
𝑞𝑥2 𝑞𝑦2 𝑞𝑧2 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
.. .. .. ..
. . . .
𝑞𝑥𝑛𝑣 𝑞𝑦𝑛𝑣 𝑞𝑧𝑛𝑣 𝑉 𝑒𝑟𝑡𝑒𝑥𝑙𝑎𝑏𝑒𝑙
11 12 13 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
21 22 23 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
.. .. .. ..
. . . .
(𝑛𝑡𝑟𝑖 )1 (𝑛𝑡𝑟𝑖 )2 (𝑛𝑡𝑟𝑖 )3 𝑟𝑒𝑔𝑖𝑜𝑛𝑙𝑎𝑏𝑒𝑙
11 12 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
21 22 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
.. .. ..
. . .
(𝑛𝑒 𝑑𝑔𝑒)1 (𝑛𝑒𝑑𝑔𝑒 )2 𝑏𝑜𝑢𝑛𝑑𝑎𝑟𝑦𝑙𝑎𝑏𝑒𝑙
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
54
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:
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
21 // Fespace
22 fespace Vh3(Th3, P2);
23 Vh3 u3, v3;
24
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 connected 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 op-
tion 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"
4
5 // Parameters
6 int nn = 20;
7 real zmin = 0;
8 real zmax = 1;
9
10 // Mesh
11 mesh Th2 = square(nn, nn);
12 int[int] rup = [0, 2], rdown = [0, 1];
13 int[int] rmid = [1, 1, 2, 1, 3, 1, 4, 1];
14 mesh3 Th = buildlayers(Th2, nn, zbound=[zmin, zmax], reffacemid=rmid, reffaceup=rup,␣
˓→reffacelow=rdown);
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.
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);
Tip
Falling spheres
1 load "msh3"
2 load "TetGen"
3 load "medit"
4 load "mmg3d"
5 include "MeshSurface.idp"
6
7 // Parameters
8 real hs = 0.8;
9 int[int] N = [4/hs, 8/hs, 11.5/hs];
10 real [int, int] B = [[-2, 2], [-2, 6], [-10, 1.5]];
11 int [int, int] L = [[311, 311], [311, 311], [311, 311]];
12
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
48 // Falling loop
49 for(int it = 0; it < 29; it++){
50 cout << " ITERATION " << it << endl;
51
52 // Solve
53 Lap;
54
55 // Plot
56 plot(Th, uh);
57
58 // Sphere falling
59 Th = mmg3d(Th, options=opt, displacement=[zero, zero, uh], memory=1000);
60 }
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
34 )
35 + on(1, u=0)
36 ;
37
38 // Loop
39 for (int ii = 0; ii < 5; ii++){
40 // Solve
41 Poisson;
42 cout << "u min, max = " << u[].min << " "<< u[].max << endl;
43
46 cout << "h min, max = " << h[].min << " "<< h[].max << " " << h[].n << " " << Th3.
˓→nv << endl;
47 plot(u, wait=true);
48
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.
• ratio= the ratio (1 by default).
• eps= relative 𝜀 (default 1e-10)
• beginend= array to get begin, end couple of each of sub line (resize automatically)
• file= to save the data curve in data file for gnuplot
In the array xy you get the list of vertices of the isoline, each connex line go from 𝑖 = 𝑖𝑐0 , . . . , 𝑖𝑐1 − 1 with 𝑖𝑐0 = 𝑏𝑒(2 * 𝑐)
𝑖𝑐1 = 𝑏𝑒(2 * 𝑐 + 1), and where 𝑥𝑖 = 𝑥𝑦(0, 𝑖), 𝑦𝑖 = 𝑦𝑥(1, 𝑖), 𝑙𝑖 = 𝑥𝑦(2, 𝑖).
Here 𝑙𝑖 is the length of the line (the origin of the line is point 𝑖𝑐0 ).
The sense of the isoline is such that the upper part is at the left size of the isoline. So here : the minimum is a point
0.5, 05 so the curve 1 turn in the clockwise sense, the order of each component are sort such that the number of point
by component is decreasing.
1 cout << "Number of the line component = " << nbc << endl;
2 cout << "Number of points = " << xy.m << endl;
3 cout << "be = " << be << endl;
4
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.
Tip
Leman lake
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 function 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);
11 Th = adaptmesh(Th, h, IsMetric=1);
12
13 // Fespace
14 fespace Vh(Th, P2Morley); //The Morley finite element space
15 Vh [u, ux, uy], [v, vx, vy];
16
17 // Macro
18 macro bilaplacien(u, v) (dxx(u)*dxx(v) + dyy(u)*dyy(v) + 2.*dxy(u)*dxy(v)) /
˓→/
19
20 // Problem
21 solve bilap ([u, ux, uy], [v, vx, vy])
22 = int2d(Th)(
23 bilaplacien(u, v)
24 )
25 - int2d(Th)(
26 f*v
27 )
28 + on(1, 2, 3, 4, u=0, ux=0, uy=0)
29 ;
30
31 // Plot
32 plot(u, cmm="u");
• [HCT] 𝑃3 𝐶 1 conforms finite element (2d) (needs load "Element_HCT") one 3 sub triangles.
Lets call 𝒯ℎ△ the sub mesh of 𝒯ℎ where all triangles are split in 3 at the barycenter.
{︁ ⃒ }︁
1 △
P𝐻𝐶𝑇 = 𝑣 ∈ 𝐶 (Ω) ∀𝐾 ∈ 𝒯 , 𝑣 ∈ 𝑃
⃒
ℎ ⃒ ℎ |𝐾 3
Warning
To build the interplant of a function 𝑢 (scalar) for this finite element, we need the function 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 }
The full example is in examples.
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:
The basis function 𝜑𝑘1 (𝑥, 𝑦) with the vertex 𝑞 𝑘1 in Fig. 3.30 is defined by the barycentric coordinates:
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).
𝐹˜ : 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 ˆ
𝑋 = 𝑖=0 𝐴𝑖 𝜆(ˆ 𝑥) où
• 𝐴𝑖 the points of the current triangle K
ˆ 𝑖 basic functions 𝑃 12𝑑
• 𝜆
ˆ 0 (𝑥, 𝑦) = 1 − 𝑥 − 𝑦
• 𝜆
ˆ 1 (𝑥, 𝑦) = 𝑥
• 𝜆
ˆ 2 (𝑥, 𝑦) = 𝑦
• 𝜆
We need to define a quadrature formula for the finite element⎛approximation.
⎞ The usual formulation for a 2d triangle
𝑥
ˆ𝑞
will be used by redefining the quadrature points 𝑋𝑞 = 𝑥𝑞 = ⎝ 𝑦ˆ𝑞 ⎠.
0
3) The Lagragian P1 functions and theirs 1st order derivatives
The finite element interpolation gives us the following relationship: 𝜓𝑖 (𝑋) = 𝐹 −1 (𝜓𝑖 )(𝐹 −1 (𝑋)). To find the ex-
pression of the basic functions 𝜓 on the current triangle K, it is sufficient to use the inverse of the transformation 𝐹˜ to
get back to the reference triangle 𝐾.
ˆ However in FreeFEM, the definition of the reference finite element, the current
geometry is based on barycentric coordinates in order not to use geometric transformation. 𝐹˜ . The method used here
is geometric and based on the properties of the vector product and the area of the current triangle K.
i) The shape functions
Let be the triangle K of vertices 𝑖0 , 𝑖1 , 𝑖2 ⊂ R3 and (𝜆𝑖 )2𝑖=0 the local barycentric coordinates at K. The normal is
−−−→ −−−→ −−−→ −−−→
defined as the tangent plane generated by (𝐴0 , 𝐴0 𝐴1 , 𝐴0 𝐴2 ), \ ⃗𝑛 = 𝐴0 𝐴1 ∧ 𝐴0 𝐴2 avec || ⃗𝑛 ||= 2 mes (𝐾).
ˆ
Le denotes the operator V, defines the usual vector product of R3 such as 𝑉 (𝐴, 𝐵, 𝐶) = (𝐵 − 𝐴) ∧ (𝐶 − 𝐴)
The mixed product of three vectors u, v, w, noté [𝑢, 𝑣, 𝑤], is the determinant of these three vectors in any direct or-
thonormal basis, thus (𝐴 ∧ 𝑉, 𝐶) = det (𝐴, 𝐵, 𝐶)
with (., .) is the usual scalar product of R3 . \ Let Ph :math:` in mathbb{R}^3` and P his projected in the triangle K
such as:
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 𝒜𝒮 = 21 < ⃗𝑛, ⃗𝑛 > hence
𝒜𝒮 (𝑃 𝐵𝐶) = 12 < ⃗𝑛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)} `
Note
With the definition of the surface gradient and the 2d Pk-Lagrange FE used barycentric coordinates, surface Pk-
Langrange FE are trivial.
𝑤ℎ ∈ 𝑉ℎ ⊂ 𝐻 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. 𝑓 ≥ 0 ⇒ 𝑢ℎ ≥ 0
3. If 𝑖 ̸= 𝑗, the basis function 𝜑𝑖 and 𝜑𝑗 are 𝐿2 -orthogonal:
∫︁
𝜑𝑖 𝜑𝑗 d𝑥d𝑦 = 0 if 𝑖 ̸= 𝑗
Ω
See Fig. 3.34a for the projection of 𝑓 (𝑥, 𝑦) = sin(𝜋𝑥) cos(𝜋𝑦) into Vh(Th, P1nc).
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).
Raviart-Thomas Element
In the Raviart-Thomas finite element 𝑅𝑇∫︀0ℎ , the degrees of freedom are the fluxes across edges 𝑒 of the mesh, where
the flux of the function f : R2 −→ R2 is 𝑒 f .𝑛𝑒 , 𝑛𝑒 is the unit normal of edge 𝑒.
This implies an orientation of all the edges of the mesh, for example we can use the global numbering of the edge
vertices and we just go from small to large numbers.
To compute the flux, we use a quadrature with one Gauss point, the mid-point of the edge.
Consider a triangle 𝑇𝑘 with three vertices (a, b, c).
Lets denote the vertices numbers by 𝑖𝑎 , 𝑖𝑏 , 𝑖𝑐 , and define the three edge vectors e1 , e2 , e3 by 𝑠𝑔𝑛(𝑖𝑏 − 𝑖𝑐 )(b − c),
𝑠𝑔𝑛(𝑖𝑐 − 𝑖𝑎 )(c − a), 𝑠𝑔𝑛(𝑖𝑎 − 𝑖𝑏 )(a − b).
We get three basis functions:
𝑠𝑔𝑛(𝑖𝑏 − 𝑖𝑐 ) 𝑠𝑔𝑛(𝑖𝑐 − 𝑖𝑎 ) 𝑠𝑔𝑛(𝑖𝑎 − 𝑖𝑏 )
𝜑𝑘1 = (x − a), 𝜑𝑘2 = (x − b), 𝜑𝑘3 = (x − c),
2|𝑇𝑘 | 2|𝑇𝑘 | 2|𝑇𝑘 |
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
(continues on next page)
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)
8 value = Uyh; //get value = Uyh(1, 2)
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.
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.
Note
Sometimes, in rare cases, the interpolation process misses some points, we can change the search algorithm through
a global variable searchMethod
1 searchMethod = 0; // default value for fast search algorithm
2 searchMethod = 1; // safe search algorithm, uses brute force in case of missing point
3 // (warning: can be very expensive in cases where a lot of points are outside of the␣
˓→domain)
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
12
16 // Plot
17 plot(us, ug, wait=true);
18 plot(vs, vg, wait=true);
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.
Note
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
7 )
8 - int2d(Th)( //int_{Omega} f v
9 f*v
10 )
11 - int1d(Th, gn)( //int_{Gamma_r} b v
12 b * v
13 )
14 + on(gd, u=g) //u = g on Gamma_d
15 ;
where Th is a mesh of the bi-dimensional domain Ω, and gd and gn are respectively the boundary labels of boundary
Γ𝑑 and Γ𝑛 .
And the three dimensional problem (3.16) becomes
where Th is a mesh of the three dimensional domain Ω, and gd and gn are respectively the boundary labels of boundary
Γ𝑑 and Γ𝑛 .
Note
If the functions are a part of vectorial FE then you must give all the functions of the vectorial FE in the same order
(see Poisson problem with mixed finite element for example).
Note
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 𝜕𝑇
• The right hand-side of the Partial Differential Equation in 2D, the terms of the linear form: for given functions
𝐾, 𝑓 :
∑︁ ∫︁
– int2d(Th)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇
∑︁ ∫︁
– int2d(Th, l)(K*w) = 𝐾𝑤
𝑇 ∈Th,𝑇 ∈Ω𝑙 𝑇
∑︁ ∫︁
– int2d(Th, levelset=phi)(K*w) = 𝐾𝑤
𝑇 ∈Th 𝑇,𝜑<0
∑︁ ∫︁
– 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
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.
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.
∑︀𝑛𝑡
For a three dimensional domain Ωℎ = 𝑘=1 𝑇𝑘 , 𝒯ℎ = {𝑇𝑘 }, we can calculate the integral over Ωℎ by:
𝑓 (𝑥, 𝑦) =int3d(Th)(f) =int3d(Th,qfV=*)(f) =int3D(Th,qforder=*)(f)
∫︀
Ωℎ
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
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],
(continues on next page)
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
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 boundary
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:
1 varf vb1([u1, u2], q) = int2d(Th)((dy(u1) + dy(u2))*q) + int2d(Th)(1*q);
2 varf vb2([v1, v2], p) = int2d(Th)((dy(v1) + dy(v2))*p) + int2d(Th)(1*p);
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;
Tip
Matrix interpolation
1 // Mesh
2 mesh Th = square(4, 4);
3 mesh Th4 = square(2, 2, [x*0.5, y*0.5]);
4 plot(Th, Th4, wait=true);
5
6 // Fespace
7 fespace Vh(Th, P1);
8 Vh v, vv;
9 fespace Vh4(Th4, P1);
10 Vh4 v4=x*y;
11
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
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 𝑉ℎ (Ω𝑖 ), 𝑖 ̸= 𝑗.
∫︀
6 func f = 1.;
7 real alpha = 1.;
8
11 // Mesh
12 mesh[int] Th(2);
13 int[int] reg(2);
14
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
75 + int1d(Th[0], Sigma)(
76 alpha*u0*v
77 )
78 +on(Gamma, u=0)
79 ;
80
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 }
Tip
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]],
(continues on next page)
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);
(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
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
Note
7 // to call gnuplot command and wait 5 second (due to the Unix command)
8 // and make postscript plot
9 exec("echo 'plot \"plot.gp\" w l \n pause 5 \n set term postscript \n set output \
˓→"gnuplot.eps\" \n replot \n quit' | gnuplot");
Note
1 load "medit"
2
Before:
Note
1 load "iovtk"
2
Warning
Note
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.
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𝑃 ≤ 𝜖
where u is the current solution, and g, the current gradient, is not preconditioned.
Tip
Algorithms.edp
1
𝑓 (|∇𝑢|2 ) − Ω 𝑢𝑏
∫︀ ∫︀
𝐽(𝑢) = 2 Ω
1
𝑓 (𝑥) = 𝑎𝑥 + 𝑥 − ln(1 + 𝑥), 𝑓 ′ (𝑥) = 𝑎 + 𝑥
1+𝑥 , 𝑓 ′′ (𝑥) = (1+𝑥)2
4 // The function J
5 //J(u) = 1/2 int_Omega f(|nabla u|^2) - int_Omega u b
6 func real J (real[int] & u){
7 Vh w;
8 w[] = u;
9 real r = int2d(Th)(0.5*f(dx(w)*dx(w) + dy(w)*dy(w)) - b*w);
10 cout << "J(u) = " << r << " " << u.min << " " << u.max << endl;
11 return r;
12 }
13
14 // The gradient of J
15 func real[int] dJ (real[int] & u){
16 Vh w;
17 w[] = u;
18 alpha = df(dx(w)*dx(w) + dy(w)*dy(w));
19 varf au (uh, vh)
20 = int2d(Th)(
21 alpha*(dx(w)*dx(vh) + dy(w)*dy(vh))
22 - b*vh
23 )
24 + on(1, 2, 3, 4, uh=0)
25 ;
26
27 u = au(0, Vh);
28 return u; //warning: no return of local array
29 }
∫︁ ∫︁
∀𝑣ℎ ∈ 𝑉0ℎ , 𝛼∇𝑢ℎ .∇𝑣ℎ = 𝑏𝑣ℎ
Ω Ω
where 𝛼 = 𝑓 ′ (|∇𝑢|2 ).
19 // Preconditionner
20 func real[int] C(real[int] & u){
21 real[int] w = u;
22 u = Alap^-1*w;
23 return u; //warning: no return of local array variable
24 }
To solve the problem, we make 10 iterations of the conjugate gradient, recompute the preconditioner and restart the
conjugate gradient:
1 int conv=0;
2 for(int i = 0; i < 20; i++){
3 conv = NLCG(dJ, u[], nbiter=10, precon=C, veps=eps, verbosity=5);
4 if (conv) break;
5
11 // Plot
12 plot (u, wait=true, cmm="solution with NLCG");
Also, we can use the non-linear version of GMRES algorithm (the function 𝐽 is just convex)
For the details of these algorithms, refer to [PIRONNEAU1998], Chapter IV, 1.3.
Tip
BFGS
1 real[int] b(10), u(10);
2
3 //J
4 func real J (real[int] & u){
5 real s = 0;
6 for (int i = 0; i < u.n; i++)
7 s += (i+1)*u[i]*u[i]*0.5 - b[i]*u[i];
8 if (debugJ)
9 cout << "J = " << s << ", u = " << u[0] << " " << u[1] << endl;
10 return s;
11 }
12
25 b=1;
26 u=2;
27 BFGS(J, DJ, u, eps=1.e-6, nbiter=20, nbiterline=20);
28 cout << "BFGS: J(u) = " << J(u) << ", err = " << error(u, b) << endl;
It is almost the same a using the CMA evolution strategy except, that since it is a derivative free optimizer, the dJ
argument is omitted and there are some other named parameters to control the behavior of the algorithm. With the
same objective function as above, an example of utilization would be (see CMAES Variational inequality for a complete
example):
1 load "ff-cmaes"
2 //define J, u, ...
3 real min = cmaes(J, u, stopTolFun=1e-6, stopMaxIter=3000);
4 cout << "minimum value is " << min << " for u = " << u << endl;
This algorithm works with a normal multivariate distribution in the parameters space and tries to adapt its covariance
matrix using the information provided by the successive function evaluations (see NLopt documentation for more
details). Therefore, some specific parameters can be passed to control the starting distribution, size of the sample
generations, etc. . . Named parameters for this are the following:
• seed= Seed for random number generator (val is an integer). No specified value will lead to a clock based seed
initialization.
• initialStdDev= Value for the standard deviations of the initial covariance matrix ( val is a real). If the value
𝜎 is passed, the initial covariance matrix will be set to 𝜎𝐼. The expected initial distance between initial 𝑋 and
the 𝑎𝑟𝑔𝑚𝑖𝑛 should be roughly initialStdDev. Default is 0.3.
• initialStdDevs= Same as above except that the argument is an array allowing to set a value of the initial
standard deviation for each parameter. Entries differing by several orders of magnitude should be avoided (if it
can’t be, try rescaling the problem).
• stopTolFun= Stops the algorithm if function value differences are smaller than the passed one, default is 10−12 .
• stopTolFunHist= Stops the algorithm if function value differences from the best values are smaller than the
passed one, default is 0 (unused).
• stopTolX= Stopping criteria is triggered if step sizes in the parameters space are smaller than this real value,
default is 0.
• stopTolXFactor= Stopping criteria is triggered when the standard deviation increases more than this value.
The default value is 103 .
• stopMaxFunEval= Stops the algorithm when stopMaxFunEval function evaluations have been done. Set to
900(𝑛 + 3)2 by default, where 𝑛 is the parameters space dimension.
• stopMaxIter= Integer stopping the search when stopMaxIter generations have been sampled. Unused by
default.
• popsize= Integer value used to change the sample size. The default value is 4 + ⌊3 ln(𝑛)⌋. Increasing the
population size usually improves the global search capabilities at the cost of, at most, a linear reduction of the
convergence speed with respect to popsize.
• paramFile= This string type parameter allows the user to pass all the parameters using an extern file, as in
Hansen’s original code. More parameters related to the CMA-ES algorithm can be changed with this file. Note
that the parameters passed to the CMAES function in the FreeFEM script will be ignored if an input parameters
file is given.
3.5.3 IPOPT
The ff-Ipopt package is an interface for the IPOPT [WÄCHTER2006] optimizer. IPOPT is a software library for
large scale, non-linear, constrained optimization. It implements a primal-dual interior point method along with filter
method based line searches.
IPOPT needs a direct sparse symmetric linear solver. If your version of FreeFEM has been compiled with the
--enable-downlad tag, it will automatically be linked with a sequential version of MUMPS. An alternative to
MUMPS would be to download the HSL subroutines (see Compiling and Installing the Java Interface JIPOPT) and
place them in the /ipopt/Ipopt-3.10.2/ThirdParty/HSL directory of the FreeFEM downloads folder before
compiling.
𝑥0 = argmin𝑓 (𝑥)
𝑥∈𝑉 (3.18)
with 𝑉 = {𝑥 ∈ R𝑛 | 𝑐(𝑥) = 0 and 𝑥𝑙 ≤ 𝑥 ≤ 𝑥𝑢 }
Where 𝑐 : R𝑛 → R𝑚 and 𝑥𝑙 , 𝑥𝑢 ∈ R𝑛 and inequalities hold componentwise. The 𝑓 function as well as the constraints
𝑐 should be twice-continuously differentiable.
As a barrier method, interior points algorithms try to find a Karush-Kuhn-Tucker point for (3.18) by solving a sequence
of problems, unconstrained with respect to the inequality constraints, of the form:
The remaining equality constraints are handled with the usual Lagrange multipliers method. If the sequence of barrier
parameters 𝜇 converge to 0, intuition suggests that the sequence of minimizers of (3.19) converge to a local constrained
minimizer of (3.18). For a given 𝜇, (3.19) is solved by finding (𝑥𝜇 , 𝜆𝜇 ) ∈ R𝑛 × R𝑚 such that:
𝑚
∑︁
∇𝐵(𝑥𝜇 , 𝜇) + 𝜆𝜇,𝑖 ∇𝑐𝑖 (𝑥𝜇 ) = ∇𝐵(𝑥𝜇 , 𝜇) + 𝐽𝑐 (𝑥𝜇 )𝑇 𝜆𝜇 = 0
𝑖=1 (3.20)
𝑐(𝑥𝜇 ) = 0
If we respectively call 𝑧𝑢 (𝑥, 𝜇) = (𝜇/(𝑥𝑢,1 − 𝑥1 ), . . . , 𝜇/(𝑥𝑢,𝑛 − 𝑥𝑛 )) and 𝑧𝑙 (𝑥, 𝜇) the other vector appearing in the
above equation, then the optimum (𝑥𝜇 , 𝜆𝜇 ) satisfies:
In this equation, the 𝑧𝑙 and 𝑧𝑢 vectors seem to play the role of Lagrange multipliers for the simple bound inequalities,
and indeed, when 𝜇 → 0, they converge toward some suitable Lagrange multipliers for the KKT conditions, provided
some technical assumptions are fulfilled (see [FORSGREN2002]).
Equation (3.21) is solved by performing a Newton method in order to find a solution of (3.20) for each of the decreas-
ing values of 𝜇. Some order 2 conditions are also taken into account to avoid convergence to local maximizers, see
[FORSGREN2002] for details about them. In the most classic IP algorithms, the Newton method is directly applied to
(3.20). This is in most case inefficient due to frequent computation of infeasible points. These difficulties are avoided
in Primal-Dual interior point methods where (3.20) is transformed into an extended system where 𝑧𝑢 and 𝑧𝑙 are treated
as unknowns and the barrier problems are finding (𝑥, 𝜆, 𝑧𝑢 , 𝑧𝑙 ) ∈ R𝑛 × R𝑚 × R𝑛 × R𝑛 such that:
⎧ 𝑇
⎪ ∇𝑓 (𝑥) + 𝐽𝑐 (𝑥) 𝜆 + 𝑧𝑢 − 𝑧𝑙 = 0
⎪
𝑐(𝑥) = 0
⎨
(3.22)
⎪
⎪ (𝑋 𝑢 − 𝑋)𝑧 𝑢 − 𝜇𝑒 = 0
(𝑋 − 𝑋𝑙 )𝑧𝑙 − 𝜇𝑒 = 0
⎩
Where if 𝑎 is a vector of R𝑛 , 𝐴 denotes the diagonal matrix 𝐴 = (𝑎𝑖 𝛿𝑖𝑗 )1≤𝑖,𝑗≤𝑛 and 𝑒 ∈ R𝑛 = (1, 1, . . . , 1). Solving
this nonlinear system by the Newton method is known as being the primal-dual interior point method. Here again,
more details are available in [FORSGREN2002]. Most actual implementations introduce features in order to globalize
the convergence capability of the method, essentially by adding some line-search steps to the Newton algorithm, or by
using trust regions. For the purpose of IPOPT, this is achieved by a filter line search methods, the details of which can
be found in [WÄCHTER2006].
More IPOPT specific features or implementation details can be found in [WÄCHTER2006]. We will just retain that
IPOPT is a smart Newton method for solving constrained optimization problems, with global convergence capabilities
due to a robust line search method (in the sense that the algorithm will converge no matter the initializer). Due to the
underlying Newton method, the optimization process requires expressions of all derivatives up to the order 2 of the
fitness function as well as those of the constraints. For problems whose Hessian matrices are difficult to compute or
lead to high dimensional dense matrices, it is possible to use a BFGS approximation of these objects at the cost of a
much slower convergence rate.
IPOPT in FreeFEM
Calling the IPOPT optimizer in a FreeFEM script is done with the IPOPT function included in the ff-Ipopt dynamic
library. IPOPT is designed to solve constrained minimization problems in the form:
find𝑥0 = argmin𝑓 (𝑥)
𝑥∈R𝑛
∀𝑖 ≤ 𝑛, 𝑥lb ub
{︂
s.t. 𝑖 ≤ 𝑥𝑖 ≤ 𝑥𝑖 (simple bounds)
∀𝑖 ≤ 𝑚, 𝑐𝑖 ≤ 𝑐𝑖 (𝑥) ≤ 𝑐ub
lb
𝑖 (constraints functions)
Where ub and lb stand for “upper bound” and “lower bound”. If for some 𝑖, 1 ≤ 𝑖 ≤ 𝑚 we have 𝑐lb
𝑖 = 𝑐𝑖 , it means
ub
There are different ways to pass the fitness function and constraints. The more general one is to define the functions
using the keyword func. Any returned matrix must be a sparse one (type matrix, not a real[int,int]):
Warning
In the current version of FreeFEM, returning a matrix object that is local to a function block leads to undefined
results. For each sparse matrix returning function you define, an extern matrix object has to be declared, whose
associated function will overwrite and return on each call. Here is an example for jacC:
1 matrix jacCBuffer; //just declare, no need to define yet
2 func matrix jacC (real[int] &X){
3 ...//fill jacCBuffer
4 return jacCBuffer;
5 }
Warning
IPOPT requires the structure of each matrix at the initialization of the algorithm. Some errors may occur if the
matrices are not constant and are built with the matrix A = [I,J,C] syntax, or with an intermediary full matrix
(real[int,int]), because any null coefficient is discarded during the construction of the sparse matrix. It is also
the case when making matrices linear combinations, for which any zero coefficient will result in the suppression of
the matrix from the combination. Some controls are available to avoid such problems. Check the named parameter
descriptions (checkindex, structhess and structjac can help). We strongly advice to use varf as much as
possible for the matrix forging.
The Hessian returning function is somewhat different because it has to be the Hessian of the Lagrangian function:
𝑚
∑︁
(𝑥, 𝜎𝑓 , 𝜆) ↦→ 𝜎𝑓 ∇2 𝑓 (𝑥) + 𝜆𝑖 ∇2 𝑐𝑖 (𝑥) where 𝜆 ∈ R𝑚 and 𝜎 ∈ R
𝑖=1
If the constraints functions are all affine, or if there are only simple bound constraints, or no constraint at all, the
Lagrangian Hessian is equal to the fitness function Hessian, one can then omit the sigma and lambda parameters:
1 matrix hessianJBuffer;
2 func matrix hessianJ (real[int] &X){...} //Hessian prototype when constraints are affine
If the Hessian is omitted, the interface will tell IPOPT to use the (L)BFGS approximation (it can also be enabled with
a named parameter, see further). Simple bound or unconstrained problems do not require the constraints part, so the
following expressions are valid:
Simple bounds are passed using the lb and ub named parameters, while constraint bounds are passed with the clb
and cub ones. Unboundedness in some directions can be achieved by using the 1𝑒19 and −1𝑒19 values that IPOPT
recognizes as +∞ and −∞:
P2 fitness function and affine constraints function : In the case where the fitness function or constraints function
can be expressed respectively in the following forms:
where 𝐴 and 𝑏 are constant, it is possible to directly pass the (𝐴, 𝑏) pair instead of defining 3 (or 2) functions. It also
indicates to IPOPT that some objects are constant and that they have to be evaluated only once, thus avoiding multiple
copies of the same matrix. The syntax is:
Note that if you define the constraints in this way, they don’t contribute to the Hessian, so the Hessian should only take
one real[int] as an argument.
If both objective and constraint functions are given this way, it automatically activates the IPOPT
mehrotra_algorithm option (better for linear and quadratic programming according to the documentation).
Otherwise, this option can only be set through the option file (see the named parameters section).
A false case is the one of defining 𝑓 in this manner while using standard functions for the constraints:
Indeed, when passing [A, b] in order to define 𝑓 , the Lagrangian Hessian is automatically built and has the constant
𝑥 ↦→ 𝐴 function, with no way to add possible constraint contributions, leading to incorrect second order derivatives.
So, a problem should be defined like that in only two cases:
1. constraints are nonlinear but you want to use the BFGS mode (then add bfgs=1 to the named parameter),
2. constraints are affine, but in this case, compatible to pass in the same way
Here are some other valid definitions of the problem (cases when 𝑓 is a pure quadratic or linear form, or 𝐶 a pure linear
function, etc. . . ):
Returned Value : The IPOPT function returns an error code of type int. A zero value is obtained when the algorithm
succeeds and positive values reflect the fact that IPOPT encounters minor troubles. Negative values reveal more prob-
lematic cases. The associated IPOPT return tags are listed in the table below. The IPOPT pdf documentation provides
a more accurate description of these return statuses:
Success Failures
0 Solve_Succeeded
1 Solved_To_Acceptable_Level -1 Maximum_Iterations_Exceeded
2 Infeasible_Problem_Detected -2 Restoration_Failed
3 Search_Direction_Becomes_Too_Small -3 Error_In_Step_Computation
4 Diverging_Iterates -4 Maximum_CpuTime_Exceeded
5 User_Requested_Stop
6 Feasible_Point_Found
Named Parameters : The available named parameters in this interface are those we thought to be the most subject
to variations from one optimization to another, plus a few that are interface specific. Though, as one could see at
IPOPT Linear solver, there are many parameters that can be changed within IPOPT, affecting the algorithm behavior.
These parameters can still be controlled by placing an option file in the execution directory. Note that IPOPT’s pdf
documentation may provides more information than the previously mentioned online version for certain parameters.
The in-script available parameters are:
• lb, ub : real[int] for lower and upper simple bounds upon the search variables must be of size 𝑛 (search
space dimension). If two components of the same index in these arrays are equal then the corresponding search
variable is fixed. By default IPOPT will remove any fixed variable from the optimization process and always use
the fixed value when calling functions. It can be changed using the fixedvar parameter.
• clb, cub : real[int] of size 𝑚 (number of constraints) for lower and upper constraints bounds. Equality
between two components of the same index 𝑖 in clb and cub reflect an equality constraint.
• structjacc : To pass the greatest possible structure (indexes of non null coefficients) of the constraint Jacobians
under the form [I,J] where I and J are two integer arrays. If not defined, the structure of the constraint
Jacobians, evaluated in Xi, is used (no issue if the Jacobian is constant or always defined with the same varf,
hazardous if it is with a triplet array or if a full matrix is involved).
• structhess : Same as above but for the Hessian function (unused if 𝑓 is P2 or less and constraints are affine).
Here again, keep in mind that it is the Hessian of the Lagrangian function (which is equal to the Hessian of 𝑓
only if constraints are affine). If no structure is given with this parameter, the Lagrangian Hessian is evaluated on
the starting point, with 𝜎 = 1 and 𝜆 = (1, 1, . . . , 1) (it is safe if all the constraints and fitness function Hessians
are constant or build with varf, and here again it is less reliable if built with a triplet array or a full matrix).
• checkindex : A bool that triggers a dichotomic index search when matrices are copied from FreeFEM func-
tions to IPOPT arrays. It is used to avoid wrong index matching when some null coefficients are removed from
the matrices by FreeFEM. It will not solve the problems arising when a too small structure has been given at the
initialization of the algorithm. Enabled by default (except in cases where all matrices are obviously constant).
• warmstart : If set to true, the constraints dual variables 𝜆, and simple bound dual variables are initialized with
the values of the arrays passed to lm, lz and uz named parameters (see below).
• lm : real[int] of size 𝑚, which is used to get the final values of the constraints dual variables 𝜆 and/or initialize
them in case of a warm start (the passed array is also updated to the last dual variables values at the end of the
algorithm).
• lz, uz : real[int] of size 𝑛 to get the final values and/or initialize (in case of a warm start) the dual variables
associated to simple bounds.
• tol : real, convergence tolerance for the algorithm, the default value is 10−8 .
• maxiter : int, maximum number of iterations with 3000 as default value.
• maxcputime : real value, maximum runtime duration. Default is 106 (almost 11 and a halfdays).
• bfgs : bool enabling or not the (low-storage) BFGS approximation of the Lagrangian Hessian. It is set to false
by default, unless there is no way to compute the Hessian with the functions that have been passed to IPOPT.
• derivativetest : Used to perform a comparison of the derivatives given to IPOPT with finite differences
computation. The possible string values are : "none" (default), "first-order", "second-order" and
"only-second-order". The associated derivative error tolerance can be changed via the option file. One
should not care about any error given by it before having tried, and failed, to perform a first optimization.
• dth : Perturbation parameter for the derivative test computations with finite differences. Set by default to 10−8 .
• dttol : Tolerance value for the derivative test error detection (default value unknown yet, maybe 10−5 ).
• optfile : string parameter to specify the IPOPT option file name. IPOPT will look for a ipopt.opt file by
default. Options set in the file will overwrite those defined in the FreeFEM script.
• printlevel : An int to control IPOPT output print level, set to 5 by default, the possible values are from 0 to
12. A description of the output information is available in the PDF documentation of IPOPT.
• fixedvar : string for the definition of simple bound equality constraints treatment : use "make_parameter"
(default value) to simply remove them from the optimization process (the functions will always be evalu-
ated with the fixed value for those variables), "make_constraint" to treat them as any other constraint or
"relax_bounds" to relax fixing bound constraints.
• mustrategy : a string to choose the update strategy for the barrier parameter 𝜇. The two possible tags are
"monotone", to use the monotone (Fiacco-McCormick) strategy, or "adaptive" (default setting).
• muinit : real positive value for the barrier parameter initialization. It is only relevant when mustrategy has
been set to monotone.
• pivtol : real value to set the pivot tolerance for the linear solver. A smaller number pivots for sparsity, a larger
number pivots for stability. The value has to be in the [0, 1] interval and is set to 10−6 by default.
• brf : Bound relax factor: before starting the optimization, the bounds given by the user are relaxed. This option
sets the factor for this relaxation. If it is set to zero, then the bound relaxation is disabled. This real has to be
positive and its default value is 10−8 .
• objvalue : An identifier to a real type variable to get the last value of the objective function (best value in case
of success).
• mumin : minimum value for the barrier parameter 𝜇, a real with 10−11 as default value.
• linesearch : A boolean which disables the line search when set to false. The line search is activated by
default. When disabled, the method becomes a standard Newton algorithm instead of a primal-dual system. The
global convergence is then no longer assured, meaning that many initializers could lead to diverging iterates. But
on the other hand, it can be useful when trying to catch a precise local minimum without having some out of
control process making the iterate caught by some other near optimum.
Tip
7 // Parameters
8 int nn = 20;
9 func f = 1.; //rhs function
10 real r = 0.03, s = 0.1;
11 func g = r - r/2*exp(-0.5*(square(x-0.5) + square(y-0.5))/square(s));
12
13 // Mesh
14 mesh Th = square(nn, nn);
15
16 // Fespace
17 fespace Vh(Th, P2);
18 Vh u = 0;
19 Vh lb = -1.e19;
20 Vh ub = g;
21
22 // Macro
23 macro Grad(u) [dx(u), dy(u)] //
24
25 // Problem
26 varf vP (u, v)
27 = int2d(Th)(
28 Grad(u)'*Grad(v)
29 )
30 - int2d(Th)(
31 f*v
32 )
33 ;
Here we build the matrix and second member associated to the function to fully and finally minimize it. The [A,b]
syntax for the fitness function is then used to pass it to IPOPT.
1 matrix A = vP(Vh, Vh, solver=CG);
2 real[int] b = vP(0, Vh);
We use simple bounds to impose the boundary condition 𝑢 = 0 on 𝜕Ω, as well as the 𝑢 ≤ 𝑔 condition.
1 varf vGamma (u, v) = on(1, 2, 3, 4, u=1);
2 real[int] onGamma = vGamma(0, Vh);
3
8 // Solve
9 IPOPT([A, b], u[], lb=lb[], ub=ub[]);
10
11 // Plot
12 plot(u);
Tip
The problem entails finding (numerically) two functions (𝑢1 , 𝑢2 ) = argmin 𝐽(𝑣1 , 𝑣2 ).
(𝑣1 ,𝑣2 )∈𝑉
1 load "ff-Ipopt";
2
3 // Parameters
4 int nn = 10;
5 func f1 = 10;//right hand side
6 func f2 = -15;
7 func g1 = -0.1;//Boundary condition functions
8 func g2 = 0.1;
9
10 // Mesh
11 mesh Th = square(nn, nn);
12
13 // Fespace
14 fespace Vh(Th, [P1, P1]);
15 Vh [uz, uz2] = [1, 1];
16 Vh [lz, lz2] = [1, 1];
17 Vh [u1, u2] = [0, 0]; //starting point
18
22 // Macro
23 macro Grad(u) [dx(u), dy(u)] //
24
25 // Loop
26 int iter=0;
27 while (++iter){
28 // Problem
29 varf vP ([u1, u2], [v1, v2])
30 = int2d(Th)(
31 Grad(u1)'*Grad(v1)
32 + Grad(u2)'*Grad(v2)
33 )
34 - int2d(Th)(
35 f1*v1
36 + f2*v2
37 )
38 ;
39
49 //Boundary conditions
50 varf vGamma ([u1, u2], [v1, v2]) = on(1, 2, 3, 4, u1=1, u2=1);
51 real[int] onGamma = vGamma(0, Vh);
52 Vh [ub1, ub2] = [g1, g2];
53 Vh [lb1, lb2] = [g1, g2];
54 ub1[] = onGamma ? ub1[] : 1e19; //Unbounded in interior
55 lb1[] = onGamma ? lb1[] : -1e19;
56
61 // Solve
62 IPOPT([b, A], CC, ui1[], lb=lb1[], clb=cl[], ub=ub1[], warmstart=iter>1, uz=uzi[],␣
˓→lz=lzi[], lm=lmi[]);
63
64 // Plot
65 plot(ui1, ui2, wait=true, nbiso=60, dim=3);
66
69 // Mesh adpatation
70 Th = adaptmesh(Th, [ui1, ui2], err=0.004, nbvx=100000);
71 [uz, uz2] = [uzi, uzi2];
72 [lz, lz2] = [lzi, lzi2];
73 [u1, u2] = [ui1, ui2];
74 lm = lmi;
75 }
Where the components are expressed in the spherical coordinate system. Let’s call Ω the [0, 2𝜋] × [0, 𝜋] angular
parameters set. In order to exclude self crossing and opened shapes, the following assumptions upon 𝜌 are made:
For a given function 𝜌 the first fundamental form (the metric) of the defined surface has the following matrix represen-
tation:
(︂ 2 2
𝜌 sin (𝜑) + (𝜕𝜃 𝜌)2
)︂
𝜕𝜃 𝜌𝜕𝜑 𝜌
𝐺= (3.23)
𝜕𝜃 𝜌𝜕𝜑 𝜌 𝜌2 + (𝜕𝜑 𝜌)2
This metric is used to express the area of the surface. Let 𝑔 = det(𝐺), then we have:
∫︀ ∫︀ √
𝒜(𝜌) = Ω‖𝜕 𝜃 𝑋 ∧ 𝜕𝜑 𝑋‖ = Ω 𝑔
√︁ (3.24)
= Ω 𝜌2 (𝜕𝜃 𝜌)2 + 𝜌4 sin2 (𝜑) + 𝜌2 (𝜕𝜑 𝜌)2 sin2 (𝜑)𝑑𝜃𝑑𝜑
∫︀
The volume of the space enclosed within the shape is easier to express:
∫︁ ∫︁ 𝜌(𝜃,𝜑) ∫︁
1
𝒱(𝜌) = Ω 2
𝑟 sin(𝜑)𝑑𝑟𝑑𝜃𝑑𝜑 = Ω𝜌3 sin(𝜑)𝑑𝜃𝑑𝜑 (3.25)
0 3
Derivatives
In order to use a Newton based interior point optimization algorithm, one must be able to evaluate the derivatives of 𝒜
and 𝒱 with respect to 𝑟ℎ𝑜. Concerning the area, we have the following result:
∫︁
1 𝑑¯
𝑔 (𝜌)(𝑣)
∀𝑣 ∈ 𝐶 1 (Ω) , ⟨𝑑𝒜(𝜌), 𝑣⟩ = Ω √ 𝑑𝜃𝑑𝜑
2 𝑔
Where 𝑔¯ is the application mapping the (𝜃, 𝜑) ↦→ 𝑔(𝜃, 𝜑) scalar field to 𝜌. This leads to the following expression, easy
to transpose in a freefem script using:
∀𝑣 ∈ 𝐶 1 (Ω) ∫︀ (︀ 3 2 2
2 2
(3.26)
)︀
⟨𝑑𝒜(𝜌), 𝑣⟩ = Ω
∫︀ 2𝜌 2sin (𝜑) + 𝜌(𝜕2𝜃 𝜌) + 2𝜌(𝜕𝜑 𝜌) sin (𝜑) 𝑣
+ Ω 𝜌 𝜕𝜃 𝜌𝜕𝜃 𝑣 + 𝜌 𝜕𝜑 𝜌 sin (𝜑)𝜕𝜑 𝑣
With a similar approach, one can derive an expression for second order derivatives. However, comporting no specific
difficulties, the detailed calculus are tedious, the result is that these derivatives can be written using a 3 × 3 matrix B
whose coefficients are expressed in term of 𝜌 and its derivatives with respect to 𝜃 and 𝜑, such that:
⎛ ⎞
∫︁ 𝑣
∀(𝑤, 𝑣) ∈ 𝐶 1 (Ω) , 𝑑2 𝒜(𝜌)(𝑤, 𝑣) = Ω 𝑤 𝜕𝜃 𝑤 𝜕𝜑 𝑤 B ⎝ 𝜕𝜃 𝑣 ⎠ 𝑑𝜃𝑑𝜑 (3.27)
(︀ )︀
𝜕𝜑 𝑣
Deriving the volume function derivatives is again an easier task. We immediately get the following expressions:
∀𝑣 , ⟨𝑑𝒱(𝜌), 𝑣⟩ = ∫︀ Ω𝜌2 sin(𝜑)𝑣 𝑑𝜃𝑑𝜑
∫︀
(3.28)
∀𝑤, 𝑣 , 𝑑2 𝒱(𝜌)(𝑤, 𝑣) = Ω2𝜌 sin(𝜑)𝑤𝑣 𝑑𝜃𝑑𝜑
Tip
Given a positive function 𝜌object piecewise continuous, and a scalar 𝒱max > 𝒱(𝜌object ), find 𝜌0 such that:
𝜌0 = argmin 𝒜(𝜌) , s.t. 𝜌0 ≥ 𝜌object and 𝒱(𝜌0 ) ≤ 𝒱max
𝜌∈𝐶 1 (Ω)
If 𝜌object is the spherical parametrization of the surface of a 3-dimensional object (domain) 𝒪, it can be interpreted
as finding the surface with minimum area enclosing the object with a given maximum volume. If 𝒱max is close
to 𝒱(𝜌object ), so should be 𝜌0 and 𝜌object . With higher values of 𝒱max , 𝜌 should be closer to the unconstrained
minimum surface surrounding 𝒪 which is obtained as soon as 𝒱max ≥ 34 𝜋‖𝜌object ‖3∞ (sufficient but not necessary).
It also could be interesting to solve the same problem with the constraint 𝒱(𝜌0 ) ≥ 𝒱min which leads to a sphere
when 𝒱min ≥ 16 𝜋diam(𝒪)3 and moves toward the solution of the unconstrained problem as 𝒱min decreases.
We start by meshing the domain [0, 2𝜋] × [0, 𝜋], then a periodic P1 finite elements space is defined.
1 load "msh3";
2 load "medit";
3 load "ff-Ipopt";
4
5 // Parameters
6 int nadapt = 3;
7 real alpha = 0.9;
8 int np = 30;
9 real regtest;
10 int shapeswitch = 1;
11 real sigma = 2*pi/40.;
12 real treshold = 0.1;
13 real e = 0.1;
14 real r0 = 0.25;
15 real rr = 2-r0;
16 real E = 1./(e*e);
17 real RR = 1./(rr*rr);
18
19 // Mesh
20 mesh Th = square(2*np, np, [2*pi*x, pi*y]);
21
3.5. Algorithms & Optimization 261
22 // Fespace
23 fespace Vh(Th, P1, periodic=[[2, y], [4, y]]);
24 //Initial shape definition
25 //outside of the mesh adaptation loop to initialize with the previous optimial shape␣
FreeFEM Documentation, Release 4.13
We create some finite element functions whose underlying arrays will be used to store the values of dual variables
associated to all the constraints in order to reinitialize the algorithm with it in the case where we use mesh adaptation.
Doing so, the algorithm will almost restart at the accuracy level it reached before mesh adaptation, thus saving many
iterations.
1 Vh uz = 1., lz = 1.;
2 rreal[int] lm = [1];
Then, follows the mesh adaptation loop, and a rendering function, Plot3D, using 3D mesh to display the shape it
is passed with medit (the movemesh23 procedure often crashes when called with ragged shapes).
1 for(int kkk = 0; kkk < nadapt; ++kkk){
2 int iter=0;
3 func sin2 = square(sin(y));
4
16 if(ffplot)
17 plot(Sphere);
18 else
19 medit(cmm, Sphere);
20 }
21 catch(...){
22 cout << "PLOT ERROR" << endl;
23 }
24 return 1;
25 }
26 }
Here are the functions related to the area computation and its shape derivative, according to equations (3.24) and
(3.26):
1 // Surface computation
2 //Maybe is it possible to use movemesh23 to have the surface function less complicated
3 //However, it would not simplify the gradient and the hessian
4 func real Area (real[int] &X){
5 Vh rho;
6 rho[] = X;
7 Vh rho2 = square(rho);
8 Vh rho4 = square(rho2);
9 real res = int2d(Th)(sqrt(rho4*sin2 + rho2*square(dx(rho)) +␣
˓→rho2*sin2*square(dy(rho))));
10 ++iter;
11 if(1)
12 plot(rho, value=true, fill=true, cmm="rho(theta,phi) on [0,2pi]x[0,pi] - S=
˓→ "+res, dim=3);
13 else
14 Plot3D(rho[], "shape_evolution", 1);
15 return res;
16 }
17
The function returning the hessian of the area for a given shape is a bit blurry, thus we won’t show here all of
equation (3.27) coefficients definition, they can be found in the edp file.
1 matrix hessianA;
2 func matrix HessianArea (real[int] &X){
3 Vh rho, rho2;
4 rho[] = X;
5 rho2 = square(rho);
6 Vh sqrtPsi, sqrtPsi3, C00, C01, C02, C11, C12, C22, A;
7 {
8 Vh C0, C1, C2;
9 Vh dxrho2 = dx(rho)*dx(rho), dyrho2 = dy(rho)*dy(rho);
10 sqrtPsi = sqrt( rho2*rho2*sin2 + rho2*dxrho2 + rho2*dyrho2*sin2);
11 sqrtPsi3 = (rho2*rho2*sin2 + rho2*dxrho2 + rho2*dyrho2*sin2)*sqrtPsi;
12 C0 = 2*rho2*rho*sin2 + rho*dxrho2 + rho*dyrho2*sin2;
13 C1 = rho2*dx(rho);
14 C2 = rho2*sin2*dy(rho);
15 C00 = square(C0);
16 C01 = C0*C1;
17 C02 = C0*C2;
18 C11 = square(C1);
19 C12 = C1*C2;
20 C22 = square(C2);
21 A = 6.*rho2*sin2 + dxrho2 + dyrho2*sin2;
22 }
23 varf d2Area (w, v)
24 =int2d(Th)(
25 1./sqrtPsi * (
26 A*w*v
27 + 2*rho*dx(rho)*dx(w)*v
28 + 2*rho*dx(rho)*w*dx(v)
29 + 2*rho*dy(rho)*sin2*dy(w)*v
30 + 2*rho*dy(rho)*sin2*w*dy(v)
31 + rho2*dx(w)*dx(v)
32 + rho2*sin2*dy(w)*dy(v)
33 )
34 + 1./sqrtPsi3 * (
35 C00*w*v
36 + C01*dx(w)*v
37 + C01*w*dx(v)
38 + C02*dy(w)*v
39 + C02*w*dy(v)
40 + C11*dx(w)*dx(v)
41 + C12*dx(w)*dy(v)
42 + C12*dy(w)*dx(v)
43 + C22*dy(w)*dy(v)
44 )
45 )
46 ;
47 hessianA = d2Area(Vh, Vh);
48 return hessianA;
49 }
18 matrix hessianV;
19 func matrix HessianVolume(real[int] &X){
20 Vh rho;
21 rho[] = X;
22 varf d2Volume(w, v) = int2d(Th)(2*rho*sin(y)*v*w);
23 hessianV = d2Volume(Vh, Vh);
24 return hessianV;
25 }
If we want to use the volume as a constraint function we must wrap it and its derivatives in some FreeFEM functions
returning the appropriate types. It is not done in the above functions in cases where one wants to use it as a fitness
function. The lagrangian hessian also has to be wrapped since the Volume is not linear with respect to 𝜌, it has
some non-null second order derivatives.
1 func real[int] ipVolume (real[int] &X){ real[int] vol = [Volume(X)]; return vol; }
2 matrix mdV;
3 func matrix ipGradVolume (real[int] &X) { real[int,int] dvol(1,Vh.ndof); dvol(0,:) =␣
˓→GradVolume(X); mdV = dvol; return mdV; }
4 matrix HLagrangian;
5 func matrix ipHessianLag (real[int] &X, real objfact, real[int] &lambda){
6 HLagrangian = objfact*HessianArea(X) + lambda[0]*HessianVolume(X);
7 return HLagrangian;
8 }
The ipGradVolume function could pose some troubles during the optimization process because the gradient vector
is transformed in a sparse matrix, so any null coefficient will be discarded. Here we create the IPOPT structure
manually and use the checkindex named-parameter to avoid bad indexing during copies. This gradient is actually
dense, there is no reason for some components to be constantly zero:
1 int[int] gvi(Vh.ndof), gvj=0:Vh.ndof-1;
2 gvi = 0;
These two arrays will be passed to IPOPT with structjacc=[gvi,gvj]. The last remaining things are the bound
definitions. The simple lower bound must be equal to the components of the P1 projection of 𝜌𝑜𝑏𝑗𝑒𝑐𝑡 . And we
choose 𝛼 ∈ [0, 1] to set 𝒱max to (1 − 𝛼)𝒱(𝜌𝑜𝑏𝑗𝑒𝑐𝑡 ) + 𝛼 43 𝜋‖𝜌object ‖3∞ :
1 func disc1 = sqrt(1./(RR+(E-RR)*cos(y)*cos(y)))*(1+0.1*cos(7*x));
2 func disc2 = sqrt(1./(RR+(E-RR)*cos(x)*cos(x)*sin2));
3
4 if(1){
5 lb = r0;
6 for (int q = 0; q < 5; ++q){
7 func f = rr*Gaussian(x, y, 2*q*pi/5., pi/3.);
8 func g = rr*Gaussian(x, y, 2*q*pi/5.+pi/5., 2.*pi/3.);
9 lb = max(max(lb, f), g);
10 }
11 lb = max(lb, rr*Gaussian(x, y, 2*pi, pi/3));
12 }
13 lb = max(lb, max(disc1, disc2));
14 real Vobj = Volume(lb[]);
15 real Vnvc = 4./3.*pi*pow(lb[].linfty,3);
16
17 if(1)
18 Plot3D(lb[], "object_inside", 1);
19 real[int] clb = 0., cub = [(1-alpha)*Vobj + alpha*Vnvc];
Calling IPOPT:
1 int res = IPOPT(Area, GradArea, ipHessianLag, ipVolume, ipGradVolume,
2 rc[], ub=ub[], lb=lb[], clb=clb, cub=cub, checkindex=1, maxiter=kkk<nadapt-1 ?␣
˓→40:150,
6 // Plot
7 Plot3D(rc[], "Shape_at_"+kkk, 1);
8 Plot3D(GradArea(rc[]), "ShapeGradient", 1);
Finally, before closing the mesh adaptation loop, we have to perform the said adaptation. The mesh is adaptated
with respect to the 𝑋 = (𝜌, 0, 0) (in spherical coordinates) vector field, not directly with respect to 𝜌, otherwise
the true curvature of the 3D-shape would not be well taken into account.
1 if (kkk < nadapt-1){
2 Th = adaptmesh(Th, rc*cos(x)*sin(y), rc*sin(x)*sin(y), rc*cos(y),
3 nbvx=50000, periodic=[[2, y], [4, y]]);
4 plot(Th, wait=true);
5 startshape = rc;
6 uz = uz;
7 lz = lz;
8 }
Here are some pictures of the resulting surfaces obtained for decreasing values of 𝛼 (and a slightly more complicated
object than two orthogonal discs). We return to the enclosed object when 𝛼 = 0:
Most of the gradient based algorithms of NLopt uses a full matrix approximation of the Hessian, so if you’re planning
to solve a large scale problem, use the IPOPT optimizer which definitely surpass them.
All the NLopt features are identified that way:
1 load "ff-NLopt"
2 //define J, u, and maybe grad(J), some constraints etc...
3 real min = nloptXXXXXX(J, u, //Unavoidable part
4 grad=<name of grad(J)>, //if needed
5 lb= //Lower bounds array
6 ub= //Upper bounds array
7 ... //Some optional arguments:
8 //Constraints functions names,
9 //Stopping criteria,
10 //Algorithm specific parameters,
11 //Etc...
12 );
XXXXXX refers to the algorithm tag (not necessarily 6 characters long). u is the starting position (a real[int] type
array) which will be overwritten by the algorithm, the value at the end being the found 𝑎𝑟𝑔𝑚𝑖𝑛. And as usual, J is
a function taking a real[int] type array as argument and returning a real. grad, lb and ub are “half-optional”
arguments, in the sense that they are obligatory for some routines but not all.
The possible optionally named parameters are the following, note that they are not used by all algorithms (some do
not support constraints, or a type of constraints, some are gradient-based and others are derivative free, etc. . . ). One
can refer to the table after the parameters description to check which are the named parameters supported by a specific
algorithm. Using an unsupported parameter will not stop the compiler work, seldom breaks runtime, and will just
be ignored. When it is obvious you are missing a routine, you will get a warning message at runtime (for example if
you pass a gradient to a derivative free algorithm, or set the population of a non-genetic one, etc. . . ). In the following
description, 𝑛 stands for the dimension of the search space.
Half-optional parameters :
• grad= The name of the function which computes the gradient of the cost function (prototype should be
real[int] → real[int], both argument and result should have the size 𝑛). This is needed as soon as a
gradient-based method is involved, which is ignored if defined in a derivative free context.
• lb/ub = Lower and upper bounds arrays ( real[int] type) of size 𝑛. Used to define the bounds within which
the search variable is allowed to move. Needed for some algorithms, optional, or unsupported for others.
• subOpt : Only enabled for the Augmented Lagrangian and MLSL methods who need a sub-optimizer in order
to work. Just pass the tag of the desired local algorithm with a string.
Constraints related parameters (optional - unused if not specified):
• IConst/EConst : Allows to pass the name of a function implementing some inequality (resp. equality) con-
straints on the search space. The function type must be real[int] → real[int] where the size of the returned
array is equal to the number of constraints (of the same type - it means that all of the constraints are computed
in one vectorial function). In order to mix inequality and equality constraints in a same minimization attempt,
two vectorial functions have to be defined and passed. See example (3.29) for more details about how these
constraints have to be implemented.
• gradIConst/gradEConst : Use to provide the inequality (resp. equality) constraints gradient. These are
real[int] → real[int,int] type functions. Assuming we have defined a constraint function (either in-
equality or equality) with 𝑝 constraints, the size of the matrix returned by its associated gradient must be 𝑝 × 𝑛
(the 𝑖-th line of the matrix is the gradient of the 𝑖-th constraint). It is needed in a gradient-based context as soon
as an inequality or equality constraint function is passed to the optimizer and ignored in all other cases.
• tolIConst/tolEConst : Tolerance values for each constraint. This is an array of size equal to the number of
inequality (resp. equality) constraints. Default value is set to 10−12 for each constraint of any type.
Stopping criteria :
• stopFuncValue : Makes the algorithm end when the objective function reaches this real value.
• stopRelXTol : Stops the algorithm when the relative moves in each direction of the search space is smaller than
this real value.
• stopAbsXTol : Stops the algorithm when the moves in each direction of the search space is smaller than the
corresponding value in this real[int] array.
• stopRelFTol : Stops the algorithm when the relative variation of the objective function is smaller than this
real value.
• stopAbsFTol : Stops the algorithm when the variation of the objective function is smaller than this real value.
• stopMaxFEval : Stops the algorithm when the number of fitness evaluations reaches this integer value.
• stopTime : Stops the algorithm when the optimization time in seconds exceeds this real value. This is not a
strict maximum: the time may exceed it slightly, depending upon the algorithm and on how slow your function
evaluation is.
Note that when an AUGLAG or MLSL method is used, the meta-algorithm and the sub-algorithm may have
different termination criteria. Thus, for algorithms of this kind, the following named parameters has been defined
(just adding the SO prefix - for Sub-Optimizer) to set the ending condition of the sub-algorithm (the meta one
uses the ones above): SOStopFuncValue, SOStopRelXTol, and so on. . . If these are not used, the sub-optimizer
will use those of the master routine.
Other named parameters :
• popSize : integer used to change the size of the sample for stochastic search methods. Default value is a
peculiar heuristic to the chosen algorithm.
• SOPopSize : Same as above, but when the stochastic search is passed to a meta-algorithm.
• nGradStored : The number (integer type) of gradients to “remember” from previous optimization steps:
increasing this increases the memory requirements but may speed convergence. It is set to a heuristic value by
default. If used with AUGLAG or MLSL, it will only affect the given subsidiary algorithm.
The following table sums up the main characteristics of each algorithm, providing the more important information about
which features are supported by which algorithm and what are the unavoidable arguments they need. More details can
be found in NLopt documentation.
Tip
Variational inequality
Let Ω be a domain of R2 , 𝑓1 , 𝑓2 ∈ 𝐿2 (Ω) and 𝑔1 , 𝑔2 ∈ 𝐿2 (𝜕Ω) four given functions with 𝑔1 ≤ 𝑔2 almost
everywhere.
We define the space:
The problem consists in finding (numerically) two functions (𝑢1 , 𝑢2 ) = argmin 𝐽(𝑣1 , 𝑣2 ).
(𝑣1 ,𝑣2 )∈𝑉
This can be interpreted as finding 𝑢1 , 𝑢2 as close as possible (in a certain sense) to the solutions of the Laplace
equation with respectively 𝑓1 , 𝑓2 second members and 𝑔1 , 𝑔2 Dirichlet boundary conditions with the 𝑢1 ≤ 𝑢2
almost everywhere constraint.
Here is the corresponding script to treat this variational inequality problem with one of the NLOpt algorithms.
1 //A brief script to demonstrate how to use the freefemm interfaced nlopt routines
2 //The problem consist in solving a simple variational inequality using one of the
3 //optimization algorithm of nlopt. We restart the algorithlm a few times after
4 //performing some mesh adaptation to get a more precise output
5
6 load "ff-NLopt"
7
8 // Parameters
9 int kas = 3; //choose of the algorithm
10 int NN = 10;
11 func f1 = 1.;
12 func f2 = -1.;
13 func g1 = 0.;
14 func g2 = 0.1;
15 int iter = 0;
16 int nadapt = 2;
17 real starttol = 1e-6;
18 real bctol = 6.e-12;
19
20 // Mesh
21 mesh Th = square(NN, NN);
22
23 // Fespace
24 fespace Vh(Th, P1);
25 Vh oldu1, oldu2;
26
27 // Adaptation loop
28 for (int al = 0; al < nadapt; ++al){
29 varf BVF (v, w) = int2d(Th)(0.5*dx(v)*dx(w) + 0.5*dy(v)*dy(w));
30 varf LVF1 (v, w) = int2d(Th)(f1*w);
31 varf LVF2 (v, w) = int2d(Th)(f2*w);
32 matrix A = BVF(Vh, Vh);
33 real[int] b1 = LVF1(0, Vh), b2 = LVF2(0, Vh);
34
37 Vh In, Bord;
38 Bord[] = Vbord(0, Vh, tgv=1);
39 In[] = Bord[] ? 0:1;
40 Vh gh1 = Bord*g1, gh2 = Bord*g2;
41
49 Au2 -= b2;
50 real val = u1[]'*Au1 + u2[]'*Au2;
51 if (iter%10 == 9)
52 plot(u1, u2, nbiso=30, fill=1, dim=3, cmm="adapt level "+al+" - iteration
˓→"+iter+" - J = "+val, value=1);
53 return val;
54 }
55
126 up = 1000000;
127 lo = -1000000;
128 for (int i = 0; i < Vh.ndof; ++i){
129 if (Bord[][i]){
130 up[i] = gh1[][i] + bctol;
131 lo[i] = gh1[][i] - bctol;
132 up[i+Vh.ndof] = gh2[][i] + bctol;
133 lo[i+Vh.ndof] = gh2[][i] - bctol;
134 }
135 }
136
1 load "mpi-cmaes"
2 ... // Define J, u and all here
3 real min = cmaesMPI(J, u, stopTolFun=1e-6, stopMaxIter=3000);
4 cout << "minimum value is " << min << " for u = " << u << endl;
If the population size is not changed using the popsize parameter, it will use the heuristic value slightly changed
to be equal to the closest greatest multiple of the size of the communicator used by the optimizer. The FreeFEM
mpicommworld is used by default. The user can specify his own MPI communicator with the named parameter comm=,
see the MPI section of this manual for more information about communicators in FreeFEM.
3.6 Parallelization
A first attempt of parallelization of FreeFEM is made here with MPI. An extended interface with MPI has been added
to FreeFEM version 3.5, (see the MPI documentation for the functionality of the language).
3.6.1 MPI
MPI Keywords
The following keywords and concepts are used:
• mpiComm to defined a communication world
• mpiGroup to defined a group of processors in the communication world
• mpiRequest to defined a request to wait for the end of the communication
MPI Constants
• mpisize The total number of processes,
• mpirank the id-number of my current process in {0, ..., mpisize-1},
• mpiUndefined The MPI_Undefined constant,
• mpiAnySource The MPI_ANY_SOURCE constant,
• mpiCommWorld The MPI_COMM_WORLD constant,
• [ . . . ] and all the keywords of MPI_Op for the reduce operator: mpiMAX, mpiMIN, mpiSUM, mpiPROD, mpiLAND,
mpiLOR, mpiLXOR, mpiBAND, mpiBXOR.
MPI Constructor
1 // Parameters
2 int[int] proc1 = [1, 2], proc2 = [0, 3];
3 int color = 1;
4 int key = 1;
5
6 // MPI ranks
7 cout << "MPI rank = " << mpirank << endl;
8
9 // MPI
10 mpiComm comm(mpiCommWorld, 0, 0); //set a MPI_Comm to MPI_COMM_WORLD
11
17 mpiComm ncomm2(comm, color, key); //MPI_Comm_split(MPI_Comm comm, int color, int key,␣
˓→MPI_Comm *ncomm)
18
MPI Functions
6 if (MPIRank == 0) cout << "MPI Comm size = " << MPICommSize << endl;
7 cout << "MPI rank in Comm = " << mpiRank(Comm) << endl;
8
9 mpiRequest Req;
10 mpiRequest[int] ReqArray(10);
11
28 processorblock(mpiAnySource);
29 //return processor i in in Comm in block mode
30 processorblock(i, Comm);
31 }
32
where a processor is just a integer rank, pointer to a MPI_comm and pointer to a MPI_Request, and processorblock
with a special MPI_Request.
where the data type of a can be of type of int, real, complex, int[int], real[int], complex[int],
int[int,int], double[int,int], complex[int,int], mesh, mesh3, mesh[int], mesh3[int], matrix,
matrix<complex>
If a, b are arrays or full matrices of int, real, or complex, we can use the following MPI functions:
1 mpiAlltoall(a, b, [comm]);
2 mpiAllgather(a, b, [comm]);
3 mpiGather(a, b, processor(..) );
4 mpiScatter(a, b, processor(..));
5 mpiReduce(a, b, processor(..), mpiMAX);
6 mpiAllReduce(a, b, comm, mpiMAX);
Thank you to Guy-Antoine Atenekeng Kahou for his help to code this interface.
1 if (mpisize != 2){
2 cout << " sorry, number of processors !=2 " << endl;
3 exit(1);
4 }
5
6 // Parameters
7 verbosity = 0;
8 int interior = 2;
9 int exterior = 1;
10 int n = 4;
11
12 // Mesh
13 border a(t=1, 2){x=t; y=0; label=exterior;}
14 border b(t=0, 1){x=2; y=t; label=exterior;}
15 border c(t=2, 0){x=t; y=1; label=exterior;}
16 border d(t=1, 0){x=1-t; y=t; label=interior;}
17 border e(t=0, pi/2){x=cos(t); y=sin(t); label=interior;}
18 border e1(t=pi/2, 2*pi){x=cos(t); y=sin(t); label=exterior;}
19 mesh[int] Th(mpisize);
20 if (mpirank == 0)
21 Th[0] = buildmesh(a(5*n) + b(5*n) + c(10*n) + d(5*n));
22 else
23 Th[1] = buildmesh(e(5*n) + e1(25*n));
24
25 broadcast(processor(0), Th[0]);
26 broadcast(processor(1), Th[1]);
27
28 // Fespace
29 fespace Vh(Th[mpirank], P1);
(continues on next page)
35 //Problem
36 int i = 0;
37 problem pb (u, v, init=i, solver=Cholesky)
38 = int2d(Th[mpirank])(
39 dx(u)*dx(v)
40 + dy(u)*dy(v)
41 )
42 - int2d(Th[mpirank])(
43 v
44 )
45 + on(interior, u=U)
46 + on(exterior, u= 0 )
47 ;
48
49 // Loop
50 for (i = 0; i < 20; i++){
51 cout << mpirank << " - Loop " << i << endl;
52
53 // Solve
54 pb;
55 //send u to the other proc, receive in U
56 processor(1-mpirank) << u[];
57 processor(1-mpirank) >> U[];
58
59 // Error
60 real err0, err1;
61 err0 = int1d(Th[mpirank],interior)(square(U - u));
62 // send err0 to the other proc, receive in err1
63 processor(1-mpirank) << err0;
64 processor(1-mpirank) >> err1;
65 real err = sqrt(err0 + err1);
66 cout << " err = " << err << " - err0 = " << err0 << " - err1 = " << err1 << endl;
67 if (err < 1e-3) break;
68 }
69 if (mpirank == 0)
70 plot(u, U);
Todo
−∆𝑢 = 𝑓 in Ω
𝑢 = 𝑔 on Γ
1
where 𝑓 and 𝑔 are two given functions of 𝐿2 (Ω) and of 𝐻 2 (Γ),
Lets introduce (𝜋𝑖 )𝑖=1,..,𝑁𝑝 a regular partition of the unity of Ω, q-e-d:
𝑁𝑝
∑︁
0
𝜋𝑖 ∈ 𝒞 (Ω) : 𝜋𝑖 ≥ 0 and 𝜋𝑖 = 1.
𝑖=1
Denote Ω𝑖 the sub domain which is the support of 𝜋𝑖 function and also denote Γ𝑖 the boundary of Ω𝑖 .
The parallel Schwarz method is:
Let ℓ = 0 the iterator and a initial guest 𝑢0 respecting the boundary condition (i.e. 𝑢0|Γ = 𝑔).
∀𝑖 = 1.., 𝑁𝑝 :
−∆𝑢ℓ𝑖 = 𝑓 in Ω𝑖
𝑢ℓ𝑖 = 𝑢ℓ on Γ𝑖 ∖ Γ
𝑢ℓ𝑖 = 𝑔 on Γ𝑖 ∩ Γ
𝑁𝑝
(3.30)
∑︁
𝑢ℓ+1 = 𝜋𝑖 𝑢ℓ𝑖
𝑖=1
After discretization with the Lagrange finite element method, with a compatible mesh 𝒯ℎ𝑖 of Ω𝑖 , i. e., the exist a global
mesh 𝒯ℎ such that 𝒯ℎ𝑖 is include in 𝒯ℎ .
Let us denote:
• 𝑉ℎ𝑖 the finite element space corresponding to domain Ω𝑖 ,
• 𝒩ℎ𝑖 is the set of the degree of freedom 𝜎𝑖𝑘 ,
Γ𝑖
• 𝒩ℎ𝑖 is the set of the degree of freedom of 𝑉ℎ𝑖 on the boundary Γ𝑖 of Ω𝑖 ,
• 𝜎𝑖𝑘 (𝑣ℎ ) is the value the degree of freedom 𝑘,
Γ𝑖
• 𝑉0ℎ𝑖 = {𝑣ℎ ∈ 𝑉ℎ𝑖 : ∀𝑘 ∈ 𝒩ℎ𝑖 , 𝜎𝑖𝑘 (𝑣ℎ ) = 0},
• The conditional expression 𝑎 ? 𝑏 : 𝑐 is defined like in :c`C` of C++ language by
Note
We never use finite element space associated to the full domain Ω because it is too expensive.
Γ𝑖
where 𝑔𝑖𝑘 is the value of 𝑔 associated to the degree of freedom 𝑘 ∈ 𝒩ℎ𝑖 .
In FreeFEM, it can be written has with U is the vector corresponding to 𝑢ℓℎ|𝑖 and the vector U1 is the vector corre-
sponding to 𝑢ℓℎ𝑖 is the solution of:
1 real[int] U1(Ui.n);
2 real[int] b = onG .* U;
3 b = onG ? b : Bi ;
4 U1 = Ai^-1*b;
where onG[𝑖] = (𝑖 ∈ Γ𝑖 ∖ Γ)?1 : 0, and Bi the right of side of the problem, are defined by
1 // Fespace
2 fespace Whi(Thi, P2);
3
4 // Problem
5 varf vPb (U, V)
6 = int3d(Thi)(
7 grad(U)'*grad(V)
8 )
9 + int3d(Thi)(
10 F*V
11 )
12 + on(1, U=g)
13 + on(10, U=G)
14 ;
15
1 macro InitU(n, Vh, Th, aTh, U) Vh[int] U(n); for (int j = 0; j < n; ++j){Th = aTh[j];␣
˓→U[j] = 0;}
First GMRES algorithm: you can easily accelerate the fixed point algorithm by using a parallel GMRES algorithm
after the introduction the following affine 𝒜𝑖 operator sub domain Ω𝑖 .
1 func real[int] DJ0 (real[int]& U){
2 real[int] V(U.n), b = onG .* U;
3 b = onG ? b : Bi ;
4 V = Ai^-1*b;
5 Update(V, U);
6 V -= U;
7 return V;
8 }
Where the parallel MPIGMRES or MPICG algorithm is just a simple way to solve in parallel the following 𝐴𝑖 𝑥𝑖 = 𝑏𝑖 , 𝑖 =
1, .., 𝑁𝑝 by just changing the dot product by reduce the local dot product of all process with the following MPI code:
1 template<class R> R ReduceSum1(R s, MPI_Comm *comm){
2 R r = 0;
3 MPI_Allreduce(&s, &r, 1, MPI_TYPE<R>::TYPE(), MPI_SUM, *comm );
4 return r;
5 }
We have all ingredient to solve in parallel if we have et the partitions of the unity. To build this partition we do:
The initial step on process 1 to build a coarse mesh, 𝒯ℎ * of the full domain, and build the partition 𝜋 function constant
equal to 𝑖 on each sub domain 𝒪𝑖 , 𝑖 = 1, .., 𝑁𝑝 , of the grid with the metis graph partitioner [KARYPIS1995] and on
each process 𝑖 in 1.., 𝑁𝑝 do
1. Broadcast from process 1, the mesh 𝒯ℎ * (call Thii in FreeFEM script), and 𝜋 function,
2. remark that the characteristic function 1I𝒪𝑖 of domain 𝒪𝑖 , is defined by (𝜋 = 𝑖)?1 : 0,
3. Let us call Π2𝑃 (resp. Π2𝑉 ) the 𝐿2 on 𝑃ℎ* the space of the constant finite element function per element on 𝒯ℎ *
(resp. 𝑉ℎ* the space of the affine continuous finite element per element on 𝒯ℎ * ) and build in parallel the 𝜋𝑖
and Ω𝑖 , such that 𝒪𝑖 ⊂ Ω𝑖 where 𝒪𝑖 = 𝑠𝑢𝑝𝑝((Π2𝑉 Π2𝐶 )𝑚 1I𝑂𝑖 ), and 𝑚 is a the overlaps size on the coarse
mesh (generally one), (this is done in function AddLayers(Thii,suppii[],nlayer,phii[]); We choose a
function 𝜋𝑖* = (Π21 Π20 )𝑚 1I𝒪𝑖 so the partition of the unity is simply defined by
𝜋*
𝜋𝑖 = ∑︀𝑁𝑝𝑖
𝑗=1 𝜋𝑗*
The set 𝐽𝑖 of neighborhood of the domain Ω𝑖 , and the local version on 𝑉ℎ𝑖 can be defined the array
jpart and njpart with:
4. We call 𝒯ℎ *𝑖𝑗 the sub mesh part of 𝒯ℎ𝑖 where 𝜋𝑗 are none zero. And thanks to the function trunc to build this
array,
5. At this step we have all on the coarse mesh, so we can build the fine final mesh by splitting all meshes: Thi,
Thij[j], Thij[j] with FreeFEM trunc mesh function which do restriction and slipping.
6. The construction of the send/recv matrices sMj and freefem:`rMj: can done with this code:
To buil a not too bad application, all variables come from parameters value with the following code
1 include "getARGV.idp"
2 verbosity = getARGV("-vv", 0);
3 int vdebug = getARGV("-d", 1);
4 int ksplit = getARGV("-k", 10);
5 int nloc = getARGV("-n", 25);
6 string sff = getARGV("-p, ", "");
7 int gmres = getARGV("-gmres", 3);
8 bool dplot = getARGV("-dp", 0);
9 int nC = getARGV("-N", max(nloc/10, 4));
And small include to make graphic in parallel of distribute solution of vector 𝑢 on mesh 𝑇ℎ with the following interface:
1 include "MPIplot.idp"
2 func bool plotMPIall(mesh &Th, real[int] &u, string cm){
3 PLOTMPIALL(mesh, Pk, Th, u, {cmm=cm, nbiso=20, fill=1, dim=3, value=1});
4 return 1;
5 }
Note
The cmm=cm, ... in the macro argument is a way to quote macro argument so the argument is cmm=cm, ....
Table 3.2: Default sparse solver for real and complex arithmetics when we load a parallel sparse solver library
Libraries Default sparse solver
real complex
MUMPS_FreeFem mumps mumps
real_SuperLU_DIST_FreeFem SuperLU_DIST previous solver
complex_SuperLU_DIST_FreeFem previous solver SuperLU_DIST
real_pastix_FreeFem PaStiX previous solver
complex_pastix_FreeFem previous solver PaStiX
hips_FreeFem hips previous solver
hypre_FreeFem hypre previous solver
parms_FreeFem parms previous solver
We also add functions (see Table 3.3) with no parameter to change the default sparse solver in the .edp file. To use
these functions, we need to load the library corresponding to the solver. An example of using different parallel sparse
solvers for the same problem is given in Direct solvers example.
Table 3.3: Functions that allow to change the default sparse solver for real and complex arithmetics and the result of
these functions
Function default sparse solver
real complex
defaulttoMUMPS() mumps mumps
realdefaulttoSuperLUdist() SuperLU_DIST previous solver
complexdefaulttoSuperLUdist() previous solver SuperLU_DIST
realdefaultopastix() pastix previous solver
complexdefaulttopastix() previous solver pastix
defaulttohips() hips previous solver
defaulttohypre() hypre previous solver
defaulttoparms() parms previous solver
Tip
89 }
90 }
MUMPS solver
Warning
MUMPS parameters:
There are four input parameters in MUMPS. Two integers SYM and PAR, a vector of integer of size 40 INCTL and a
vector of real of size 15 CNTL.
The first parameter gives the type of the matrix: 0 for unsymmetric matrix, 1 for symmetric positive matrix and 2 for
general symmetric.
The second parameter defined if the host processor work during the factorization and solves steps : PAR=1 host processor
working and PAR=0 host processor not working.
The parameter INCTL and CNTL is the control parameter of MUMPS. The vectors ICNTL and CNTL in MUMPS becomes
with index 1 like vector in Fortran. For more details see the MUMPS user’s guide.
We describe now some elements of the main parameters of ICNTL for MUMPS.
• Input matrix parameter The input matrix is controlled by parameters ICNTL(5) and ICNTL(18).
The matrix format (resp. matrix pattern and matrix entries) are controlled by INCTL(5) (resp. INCTL(18)).
The different values of ICNTL(5) are 0 for assembled format and 1 for element format. In the current
release of FreeFEM, we consider that FE matrix or matrix is storage in assembled format. Therefore,
INCTL(5) is treated as 0 value.
The main option for ICNTL(18): INCLTL(18)=0 centrally on the host processor, ICNTL(18)=3 dis-
tributed the input matrix pattern and the entries (recommended option for distributed matrix by developer
of MUMPS). For other values of ICNTL(18) see the MUMPS user’s guide. These values can be used also
in FreeFEM.
The default option implemented in FreeFEM are ICNTL(5)=0 and ICNTL(18)=0.
• Preprocessing parameter The preprocessed matrix 𝐴𝑝 that will be effectively factored is defined by
𝐴𝑝 = 𝑃 𝐷𝑟 𝐴 𝑄𝑐 𝐷𝑐 𝑃 𝑡
where 𝑃 is the permutation matrix, 𝑄𝑐 is the column permutation, 𝐷𝑟 and 𝐷𝑐 are diagonal matrix
for respectively row and column scaling.
The ordering strategy to obtain 𝑃 is controlled by parameter ICNTL(7). The permutation of zero
free diagonal 𝑄𝑐 is controlled by parameter ICNTL(6). The row and column scaling is controlled by
parameter ICNTL(18). These option are connected and also strongly related with ICNTL(12) (see
the MUMPS user’s guide for more details).
The parameters permr, scaler, and scalec in FreeFEM allow to give permutation matrix(𝑃 ), row
scaling (𝐷𝑟 ) and column scaling (𝐷𝑐 ) of the user respectively.
Calling MUMPS in FreeFEM
To call MUMPS in FreeFEM, we need to load the dynamic library MUMPS_freefem.dylib (MacOSX),
MUMPS_freefem.so (Unix) or MUMPS_freefem.dll (Windows).
This is done in typing load "MUMPS_FreeFem" in the .edp file. We give now the two methods to give the option of
MUMPS solver in FreeFEM.
• Solver parameters is defined in .edp file: In this method, we need to give the parameters lparams and
dparams.
These parameters are defined for MUMPS by :
– lparams[0] = SYM, lparams[1] = PAR,
– ∀𝑖 = 1,. . . ,40, lparams[i+1] = ICNTL(i)
– ∀𝑖 = 1,. . . ,15, dparams[i-1] = CNTL(i)
• Reading solver parameters on a file:
The structure of data file for MUMPS in FreeFEM is : first line parameter SYM and second line
parameter PAR and in the following line the different value of vectors ICNTL and CNTL. An example
of this parameter file is given in ffmumpsfileparam.txt.
˓→release of FreeFEM */
˓→release of FreeFEM */
Tip
MUMPS example
A simple example of calling MUMPS in FreeFEM with this two methods is given in the Test solver MUMPS
example.
The package SuperLU_DIST solves linear systems using LU factorization. It is a free scientific library
This library provides functions to handle square or rectangular matrix in real and complex arithmetics. The method
implemented in SuperLU_DIST is a supernodal method. New release of this package includes a parallel symbolic
factorization. This scientific library is written in C and MPI for communications.
SuperLU_DIST parameters:
We describe now some parameters of SuperLU_DIST. The SuperLU_DIST library use a 2D-logical process group.
This process grid is specified by 𝑛𝑝𝑟𝑜𝑤 (process row) and 𝑛𝑝𝑐𝑜𝑙 (process column) such that 𝑁𝑝 = 𝑛𝑝𝑟𝑜𝑤 𝑛𝑝𝑐𝑜𝑙
where 𝑁𝑝 is the number of all process allocated for SuperLU_DIST.
The input matrix parameters is controlled by “matrix=” in sparams for internal parameter or in the third line of pa-
rameters file. The different value are
• matrix=assembled global matrix are available on all process
• matrix=distributedglobal The global matrix is distributed among all the process
• matrix=distributed The input matrix is distributed (not yet implemented)
The option arguments of SuperLU_DIST are described in the section Users-callable routine of the SuperLU users’
guide.
The parameter Fact and TRANS are specified in FreeFEM interfaces to SuperLU_DIST during the different steps. For
this reason, the value given by the user for this option is not considered.
The factorization LU is calculated in SuperLU_DIST on the matrix 𝐴𝑝 .
𝐴𝑝 = 𝑃𝑐 𝑃𝑟 𝐷𝑟 𝐴 𝐷𝑐 𝑃𝑐𝑡
where 𝑃𝑐 and 𝑃𝑟 is the row and column permutation matrix respectively, 𝐷𝑟 and 𝐷𝑐 are diagonal matrix for respectively
row and column scaling.
The option argument RowPerm (resp. ColPerm) control the row (resp. column) permutation matrix. 𝐷𝑟 and 𝐷𝑐 is
controlled by the parameter DiagScale.
The parameter permr, permc, scaler, and scalec in FreeFEM is provided to give row permutation, column permu-
tation, row scaling and column scaling of the user respectively.
The other parameters for LU factorization are ParSymFact and ReplaceTinyPivot. The parallel symbolic factor-
ization works only on a power of two processes and need the ParMetis ordering. The default option argument of
SuperLU_DIST are given in the file ffsuperlu_dist_fileparam.txt.
Calling SuperLU_DIST in FreeFEM
To call SuperLU_DIST in FreeFEM, we need to load the library dynamic correspond to interface. This done by the
following line load "real_superlu _DIST_FreeFem" (resp. load "complex_superlu_DIST_FreeFem") for
real (resp. complex) arithmetics in the file .edp.
Solver parameters is defined in .edp file:
To call SuperLU_DIST with internal parameter, we used the parameters sparams. The value of parameters of Su-
perLU_DIST in sparams are defined by :
• nprow=1,
• npcol=1,
• matrix= distributedgloba,
• Fact= DOFACT,
• Equil=NO,
• ParSymbFact=NO,
• ColPerm= MMD_AT_PLUS_A,
• RowPerm= LargeDiag,
• DiagPivotThresh=1.0,
• IterRefine=DOUBLE,
• Trans=NOTRANS,
• ReplaceTinyPivot=NO,
• SolveInitialized=NO,
• PrintStat=NO,
• DiagScale=NOEQUIL
This value correspond to the parameter in the file ffsuperlu_dist_fileparam.txt. If one parameter is not specified
by the user, we take the default value of SuperLU_DIST.
Reading solver parameters on a file: The structure of data file for SuperLU_DIST in FreeFEM is given in the file
ffsuperlu_dist_fileparam.txt (default value of the FreeFEM interface).
Tip
A simple example of calling SuperLU_DIST in FreeFEM with this two methods is given in the Solver su-
perLU_DIST example.
PaStiX solver
PaStiX (Parallel Sparse matrix package) is a free scientific library under CECILL-C license. This package solves sparse
linear system with a direct and block ILU(k) iterative methods. his solver can be applied to a real or complex matrix
with a symmetric pattern.
PaStiX parameters:
The input matrix parameter of FreeFEM depend on PaStiX interface. matrix = assembled for non distributed
matrix. It is the same parameter for SuperLU_DIST.
There are four parameters in PaStiX : iparm, dparm, perm and invp. These parameters are respectively the integer
parameters (vector of size 64), real parameters (vector of size 64), permutation matrix and inverse permutation matrix
respectively. iparm and dparm vectors are described in PaStiX RefCard.
The parameters permr and permc in FreeFEM are provided to give permutation matrix and inverse permutation matrix
of the user respectively.
Solver parameters defined in .edp file:
To call PaStiX in FreeFEM in this case, we need to specify the parameters lparams and dparams. These parameters
are defined by :
∀𝑖 = 0,. . . ,63, lparams[i] = iparm[i].
∀𝑖 = 0,. . . ,63, dparams[i] = dparm[i].
Reading solver parameters on a file:
The structure of data file for PaStiX parameters in FreeFEM is: first line structure parameters of the matrix and in the
following line the value of vectors iparm and dparm in this order.
An example of this file parameter is given in ffpastix_iparm_dparm.txt with a description of these parameters.
This file is obtained with the example file iparm.txt and dparm.txt including in the PaStiX package.
If no solver parameter is given, we use the default option of PaStiX solver.
Tip
A simple example of calling PaStiX in FreeFEM with this two methods is given in the Solver PaStiX example.
In Table 3.4, we recall the different matrix considering in the different direct solvers.
Table 3.4: Type of matrix used by the different direct sparse solver
direct solver square matrix rectangular matrix
sym sym pattern unsym sym sym pattern unsym
SuperLU_DIST yes yes yes yes yes yes
MUMPS yes yes yes no no no
Pastix yes yes no no no no
pARMS solver
pARMS (parallel Algebraic Multilevel Solver) is a software developed by Youssef Saad and al at University of Min-
nesota.
This software is specialized in the resolution of large sparse non symmetric linear systems of equation. Solvers devel-
oped in pARMS are of type “Krylov’s subspace”.
It consists of variants of GMRES like FGMRES (Flexible GMRES), DGMRES (Deflated GMRES) [SAAD2003] and
BICGSTAB. pARMS also implements parallel preconditioner like RAS (Restricted Additive Schwarz) [CAI1989] and
Schur Complement type preconditioner.
All these parallel preconditioners are based on the principle of domain decomposition. Thus, the matrix 𝐴 is partitioned
into sub matrices 𝐴𝑖 (𝑖 = 1, ..., 𝑝) where p represents the number of partitions one needs. The union of 𝐴𝑖 forms the
original matrix. The solution of the overall system is obtained by solving the local systems on 𝐴𝑖 (see [SMITH1996]).
Therefore, a distinction is made between iterations on 𝐴 and the local iterations on 𝐴𝑖 .
To solve the local problem on 𝐴𝑖 there are several preconditioners as ilut (Incomplete LU with threshold), iluk (In-
complete LU with level of fill in) and ARMS (Algebraic Recursive Multilevel Solver).
Tip
Default parameters
1 load "parms_FreeFem" //Tell FreeFem that you will use pARMS
2
3 // Mesh
4 border C(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
5 mesh Th = buildmesh (C(50));
6
7 // Fespace
8 fespace Vh(Th, P2); Vh u, v;
9
10 // Function
11 func f= x*y;
12
13 // Problem
14 problem Poisson (u, v, solver=sparsesolver)
15 = int2d(Th)(
16 dx(u)*dx(v)
17 + dy(u)*dy(v) )
18 + int2d(Th)(
19 - f*v
20 )
21 + on(1, u=0) ;
22
23 // Solve
24 real cpu = clock();
25 Poisson;
26 cout << " CPU time = " << clock()-cpu << endl;
27
28 // Plot
29 plot(u);
In line 1, the pARMS dynamic library is loaded with interface FreeFEM. After this, in line 15 we specify that the
bilinear form will be solved by the last sparse linear solver load in memory which, in this case, is pARMS.
The parameters used in pARMS in this case are the default one since the user does not have to provide any parameter.
Note
In order to see the plot of a parallel script, run the command FreeFem++-mpi -glut ffglut script.edp
Tip
3 // Parameters
4 real nu = 1.;
5 int[int] iparm(16);
6 real[int] dparm(6);
7 for (int ii = 0; ii < 16; ii++)
8 iparm[ii] = -1;
9 for (int ii = 0; ii < 6; ii++)
10 dparm[ii] = -1.0; iparm[0]=0;
11
12 // Mesh
13 mesh Th = square(10, 10);
14 int[int] wall = [1, 3];
15 int inlet = 4;
16
17 // Fespace
18 fespace Vh(Th, [P2, P2, P1]);
19
20 // Function
21 func uc = 1.;
22
23 // Problem
24 varf Stokes ([u, v, p], [ush, vsh, psh], solver=sparsesolver)
25 = int2d(Th)(
26 nu*(
27 dx(u)*dx(ush)
28 + dy(u)*dy(ush)
29 + dx(v)*dx(vsh)
30 + dy(v)*dy(vsh)
31 )
32 - p*psh*(1.e-6)
33 - p*(dx(ush) + dy(vsh))
34 - (dx(u) + dy(v))*psh
35 )
36 + on(wall, wall, u=0., v=0.)
37 + on(inlet, u=uc, v=0) ;
38
We need two vectors to specify the parameters of the linear solver. In line 5-6 of the example, we have declared
these vectors(int[int] iparm(16); real[int] dparm(6);). In line 7-10 we have initialized these vectors by
negative values.
We do this because all parameters values in pARMS are positive and if you do not change the negative values of
one entry of this vector, the default value will be set.
In Table 3.7 and Table 3.8, we have the meaning of different entries of these vectors.
We run this example on a cluster paradent of Grid5000 and report results in Table 3.5.
In this example, we fix the matrix size (in term of finite element, we fix the mesh) and increase the number of
processors used to solve the linear system. We saw that, when the number of processors increases, the time for
solving the linear equation decreases, even if the number of iteration increases. This proves that, using pARMS
as solver of linear systems coming from discretization of partial differential equation in FreeFEM can decrease
drastically the total time of simulation.
HIPS (Hierarchical Iterative Parallel Solver) is a scientific library that provides an efficient parallel iterative solver for
very large sparse linear systems. HIPS is available as free software under the CeCILL-C licence.
HIPS implements two solver classes which are the iteratives class (GMRES, PCG) and the Direct class. Concerning
preconditionners, HIPS implements a type of multilevel ILU. For further informations on those preconditionners see
the HIPS documentation.
Tip
4 // Parameters
5 int nn = 10;
6 real zmin = 0, zmax = 1;
7 int[int] iparm(14);
8 real[int] dparm(6);
9 for (int iii = 0; iii < 14; iii++)
10 iparm[iii] = -1;
11 for (int iii = 0; iii < 6; iii++)
12 dparm[iii] = -1;
13 iparm[0] = 0; //use iterative solver
14 iparm[1] = 1; //PCG as Krylov method
15 iparm[4] = 0; //Matrix are symmetric
16 iparm[5] = 1; //Pattern are also symmetric
17 iparm[9] = 1; //Scale matrix
18 dparm[0] = 1e-13; //Tolerance to convergence
19 dparm[1] = 5e-4; //Threshold in ILUT
20 dparm[2] = 5e-4; //Threshold for Schur preconditionner
21
22 // Functions
23 func ue = 2*x*x + 3*y*y + 4*z*z + 5*x*y + 6*x*z + 1;
24 func uex = 4*x + 5*y + 6*z;
25 func uey = 6*y + 5*x;
26 func uez = 8*z + 6*x;
27 func f = -18.;
28
29 // Mesh
30 mesh Th2 = square(nn, nn);
31
38 // Fespace
39 fespace Vh2(Th2, P2);
40 Vh2 ux, uz, p2;
41
48 // Macro
49 macro Grad3(u) [dx(u), dy(u), dz(u)] //
50
51 // Problem
52 varf va (u, v)
53 = int3d(Th)(
54 Grad3(v)' * Grad3(u)
55 )
56 + int2d(Th, 2)(
57 u*v
58 )
59 - int3d(Th)(
60 f*v
61 )
62 - int2d(Th, 2)(
69 real cpu=clock();
70 matrix Aa = va(Vh, Vh);
71
74 if (mpirank == 0){
75 cout << "Size of A =" << Aa.n << endl;
76 cout << "Non zero coefficients =" << Aa.nbcoef << endl;
77 cout << "CPU TIME FOR FORMING MATRIX =" << clock()-cpu << endl;
78 }
79
81
82 // Solve
83 u[] = Aa^-1*F[];
84
85 // Plot
86 plot(u);
Tip
Hypre (High Level Preconditioner) is a suite of parallel preconditioner developed at Lawrence Livermore National Lab.
There are two main classes of preconditioners developed in HYPRE: AMG (Algebraic MultiGrid) and Parasails (Par-
allel Sparse Approximate Inverse).
Now, suppose we want to solve 𝐴𝑥 = 𝑏.
At the heart of AMG there is a series of progressively coarser (smaller) representations of the matrix 𝐴. Given an
approximation 𝑥 ˆ to the solution 𝑥, consider solving the residual equation 𝐴𝑒 = 𝑟 to find the error 𝑒, where 𝑟 = 𝑏 − 𝐴ˆ
𝑥.
A fundamental principle of AMG is that it is an algebraically smooth error. To reduce the algebraically smooth errors
further, they need to be represented by a smaller defect equation (coarse grid residual equation) 𝐴𝑐 𝑒𝑐 = 𝑟𝑐 , which is
cheaper to solve. After solving this coarse equation, the solution is then interpolated in fine grid represented here by
matrix 𝐴. The quality of AMG depends on the choice of coarsening and interpolating operators.
The sparse approximate inverse approximates the inverse of a matrix 𝐴 by a sparse matrix 𝑀 . A technical idea to
construct matrix 𝑀 is to minimize the Frobenuis norm of the residual matrix 𝐼 − 𝑀 𝐴. For more details on this
preconditioner technics see [CHOW1997].
HYPRE implement three Krylov subspace solvers: GMRES, PCG and BiCGStab.
Tip
4 // Parameters
5 int nn = 10;
6 int[int] iparm(20);
7 real[int] dparm(6);
8 for (int iii = 0; iii < 20; iii++)
9 iparm[iii] = -1;
10 for (int iii = 0; iii < 6; iii++)
11 dparm[iii] = -1;
12 iparm[0] = 2; //PCG as krylov method
13 iparm[1] = 0; //AMG as preconditionner 2: if ParaSails
14 iparm[7] = 7; //Interpolation
15 iparm[9] = 6; //AMG Coarsen type
16 iparm[10] = 1; //Measure type
17 iparm[16] = 2; //Additive schwarz as smoother
18 dparm[0] = 1e-13; //Tolerance to convergence
19 dparm[1] = 5e-4; //Threshold
20 dparm[2] = 5e-4; //Truncation factor
21
22 ...
23
Table 3.14: Definitions of common entries of iparms and dparms vectors for every preconditioner in HYPRE
iparms[0] Solver identification:
0: BiCGStab, 1: GMRES, 2: PCG. Default=1
iparms[1] Preconditioner identification:
0: BOOMER AMG, 1: PILUT, 2: Parasails, 3: Schwartz Default=0
iparms[2] Maximum of iteration: Default=1000
iparms[3] Krylov subspace dim: Default= 40
iparms[4] Solver print info level: Default=2
iparms[5] Solver log: Default=1
iparms[6] Solver stopping criteria only for BiCGStab : Default=1
dparms[0] Tolerance for convergence: Default=:math:1.0e-11
Table 3.15: Definitions of other entries of iparms and dparms if preconditioner is BOOMER AMG
Table 3.16: Definitions of other entries of iparms and dparms if preconditioner is PILUT
iparms[7] Row size in Parallel ILUT: Default=1000
iparms[8] Set maximum number of iterations: Default=30
dparms[1] Drop tolerance in Parallel ILUT: Default=1e-5
Table 3.17: Definitions of other entries of iparms and dparms if preconditioner is ParaSails
Number of levels in Parallel Sparse Approximate inverse: Default=1
iparms[7]
Symmetric parameter for the ParaSails preconditioner:
iparms[8]
0: nonsymmetric and/or indefinite problem, and nonsymmetric preconditioner
1: SPD problem, and SPD (factored) preconditioner
2: nonsymmetric, definite problem, and SPD (factored) preconditioner
Default=0
Filters parameters. The filter parameter is used to drop small nonzeros in the preconditioner,
dparms[1]
to reduce the cost of applying the preconditioner: Default=0.1
Threshold parameter: Default=0.1
dparms[2]
Table 3.18: Definitions of other entries of iparms and dparms if preconditionner is Schwartz
Conclusion
With the different runs presented here, we wanted to illustrate the gain in time when we increase the number of pro-
cessors used for the simulations. We saw that in every case the time for the construction of the finite element matrix
is constant. This is normal because until now this phase is sequential in FreeFEM. In contrast, phases for solving the
linear system are parallel. We saw on several examples presented here that when we increase the number of processors,
in general we decrease the time used for solving the linear systems. But this is not true in every case. In several case,
when we increase the number of processors the time to convergence also increases. There are two main reasons for this.
First, the increase of processors can lead to the increase of volume of exchanged data across processors consequently
increasing the time for solving the linear systems.
Furthermore, in decomposition domain type preconditioners, the number of processors generally corresponds to the
number of sub domains. In subdomain methods, generally when we increase the number of subdomains we decrease
convergence quality of the preconditioner. This can increase the time used for solving linear equations.
To end this, we should note that good use of the preconditioners interfaced in FreeFEM is empiric, because it is
difficult to know what is a good preconditioner for some type of problems. Although, the efficiency of preconditioners
sometimes depends on how its parameters are set. For this reason we advise the user to pay attention to the meaning
of the parameters in the user guide of the iterative solvers interfaced in FreeFEM.
Domain decomposition
In the previous section, we saw that the phases to construct a matrix are sequential. One strategy to construct the matrix
in parallel is to divide geometrically the domain into subdomains. In every subdomain we construct a local submatrix
and after that we assemble every submatrix to form the global matrix.
We can use this technique to solve PDE directly in domain Ω. In this case, in every subdomains you have to define
artificial boundary conditions to form consistent equations in every subdomains. After this, you solve equation in every
subdomains and define a strategy to obtain the global solution.
In terms of parallel programming for FreeFEM, with MPI, this means that the user must be able to divide processors
avaible for computation into subgroups of processors and also must be able to realize different type of communications
Groups
mpiGroup grpe(mpiGroup gp, KN_<long>): Create MPI_Group from existing group gp by given vector.
Communicators
Communicators is an abstract MPI object which allows MPI user to communicate across group of processors. Com-
municators can be Intra-communicators(involves a single group) or Inter-communicators (involves two groups). When
we not specify type of communicator it will be Intra-communicators
mpiComm cc(mpiComm comm, mpiGroup gp): Creates a new communicator.
comm communicator(handle), gp group which is a subset of the group of comm (handle). Return new communicator
mpiComm cc(mpiGroup gp): Same as previous constructor but default comm here is MPI_COMM_WORLD.
mpiComm cc(mpiComm comm, int color, int key): Creates new communicators based on colors and key. This
constructor is based on MPI_Comm_split routine of MPI.
mpiComm cc(MPIrank p, int key): Same constructor than the last one.
Here colors and comm is defined in MPIrank. This constructor is based on MPI_Comm_split routine of MPI.
Tip
Split communicator
1 mpiComm comm(mpiCommWorld, 0, 0);
2 int color = mpiRank(comm)%2;
3 mpiComm ccc(processor(color, comm), 0);
4 mpiComm qpp(comm, 0, 0);
5 mpiComm cp(ccc, color, 0);
mpiComm cc(mpiComm comm, int high): Creates an intracommunicator from an intercommunicator. comm inter-
communicator, high.
Used to order the groups within comm (logical) when creating the new communicator. This constructor is based on
MPI_Intercomm_merge routine of MPI.
mpiComm cc(MPIrank p1, MPIrank p2, int tag): This constructor creates an intercommuncator from two intra-
communicators. p1 defined local (intra)communicator and rank in local_comm of leader (often 0) while p2 defined
remote communicator and rank in peer_comm of remote leader (often 0). tag Message tag to use in constructing
intercommunicator. This constructor is based on MPI_Intercomm_create.
Tip
Merge
1 mpiComm comm, cc;
2 int color = mpiRank(comm)%2;
3 int rk = mpiRank(comm);
4 int size = mpiSize(comm);
5 cout << "Color values: " << color << endl;
6 mpiComm ccc(processor((rk<size/2), comm), rk);
7 mpiComm cp(cc, color, 0);
8 int rleader;
9 if (rk == 0){ rleader = size/2; }
10 else if (rk == size/2){ rleader = 0; }
11 else{ rleader = 3; }
12 mpiComm qqp(processor(0, ccc), processor(rleader, comm), 12345);
13 int aaa = mpiSize(ccc);
14 cout << "Number of processor: " << aaa << endl;
Process
In FreeFEM we wrap MPI process by function call processor which create internal FreeFEM object call MPIrank.
This mean that do not use MPIrank in FreeFEM script.
processor(int rk): Keep process rank inside object MPIrank. Rank is inside MPI_COMM_WORLD.
processor(int rk, mpiComm cc) and processor(mpiComm cc, int rk) process rank inside communicator
cc.
processor(int rk, mpiComm cc) and processor(mpiComm cc, int rk) process rank inside communicator
cc.
processorblock(int rk): This function is exactlly the same than processor(int rk) but is use in case of block-
ing communication.
processorblock(int rk, mpiComm cc): This function is exactly the same as processor(int rk, mpiComm
cc) but uses a synchronization point.
Global operations
mpiAlltoall(Data a, Data b): Sends data a from all to all processes. Receive buffer is Data b. This is done
inside communicator MPI_COMM_WORLD.
mpiAlltoall(Data a, Data b, mpiComm cc): Sends data a from all to all processes. Receive buffer is Data
b. This is done inside communicator cc.
mpiGather(Data a, Data b, processor(mpiComm, int rk): Gathers together values Data a from a group of
processes. Process of rank rk get data on communicator rk. This function is like MPI_Gather.
mpiAllgather(Data a, Data b): Gathers Data a from all processes and distribute it to all in Data b. This is
done inside communicator MPI_COMM_WORLD. This function is like MPI_Allgather.
mpiAllgather(Data a, Data b, mpiComm cc): Gathers Data a from all processes and distribute it to all in Data
b. This is done inside communicator cc. This function is like MPI_Allgather.
mpiScatter(Data a,Data b,processor(int rk, mpiComm cc)): Sends Data a from one process whith rank
rk to all other processes in group represented by communicator mpiComm cc.
mpiReduce(Data a, Data b, processor(int rk, mpiComm cc), MPI_Op op) Reduces values Data a on
all processes to a single value Data b on process of rank rk and communicator cc.
Operation use in reduce is: MPI_Op op which can be: mpiMAX, mpiMIN, mpiSUM, mpiPROD, mpiLAND, mpiLOR,
mpiLXOR, mpiBAND, mpiBXOR, mpiMAXLOC, mpiMINLOC.
Note that, for all global operations, only int[int] and real[int] are data type take in account in FreeFEM.
3.7.1 Examples
Linear problems
Nonlinear problems
Eigenvalue problems
Miscellaneous
Reproducible science
Article Source
code
Augmented Lagrangian preconditioner for large-scale hydrodynamic stability analysis GitHub
repository
A multilevel Schwarz preconditioner based on a hierarchy of robust coarse spaces GitHub
repository
KSPHPDDM and PCHPDDM: extending PETSc with advanced Krylov methods and robust multi- GitHub
level overlapping Schwarz preconditioners repository
Model problem
The model problem we consider here is the scattering of an incoming acoustic wave 𝑢inc by an obstacle Ω. Thus, we
want to solve the following homogeneous Helmholtz equation written in terms of the scattered field 𝑢:
2
in R3 ∖Ω
⎧
⎨−∆𝑢 − 𝑘 𝑢 = 0
⎪
𝑢 = −𝑢inc on Γ (3.31)
+ radiation condition
⎪
⎩
with the Sommerfeld radiation condition at infinity, which states that there can be only outgoing waves at infinity:
(︂ )︂
𝜕
lim |𝑥| − 𝚤𝑘 𝑢(𝑥) = 0
|𝑥|→∞ 𝜕|𝑥|
and where the total field 𝑢tot = 𝑢inc + 𝑢.
If the wavenumber 𝑘 is constant in R3 ∖Ω, the boundary element method can be applied. It consists in reformulating
the problem in terms of unknowns on the boundary Γ of Ω.
First, let us introduce the Green kernel 𝒢𝑘 , which for the helmholtz equation in 3D is
Let us also introduce the Single Layer Potential SL, which for any 𝑞 ∈ 𝐻 −1/2 (Γ) is defined as
∫︁
SL(𝑞)(𝑥) = 𝒢𝑘 (𝑥 − 𝑦)𝑞(𝑦)𝑑𝜎(𝑦), ∀𝑥 ∈ R3 ∖Γ. (3.33)
Γ
An interesting property of SL is that it produces solutions of the PDE at hand in R3 ∖Γ which satisfy the necessary
conditions at infinity (here the Helmholtz equation and the Sommerfeld radiation condition).
Thus, we now need to find a so-called ansatz 𝑝 ∈ 𝐻 −1/2 (Γ) such that ∀𝑥 ∈ R3 ∖Ω
∫︁
𝑢(𝑥) = SL(𝑝)(𝑥) = 𝒢𝑘 (𝑥 − 𝑦)𝑝(𝑦)𝑑𝜎(𝑦), (3.34)
Γ
Note that knowing 𝑝 on Γ, we can indeed compute 𝑢 anywhere using the potential formulation (3.34). Thus, we
essentially gained one space dimension, as we only have to solve for 𝑝 : Γ → C in (3.35). Another advantage of the
boundary element method is that for a given mesh size, it is usually more accurate than the finite element method.
Of course, these benefits of the boundary element method come with a drawback: after discretization of (3.35), for
example with piecewise linear continuous (P1) functions on Γ, we end up with a linear system whose matrix is full:
because 𝒢𝑘 (𝑥 − 𝑦) never vanishes, every interaction coefficient is nonzero. Thus, the matrix 𝐴 of the linear system
can be very costly to store (𝑁 2 coefficients) and invert (factorization in 𝒪(𝑁 3 )) (𝑁 is the size of the linear system).
Moreover, compared to the finite element method, the matrix coefficients are much more expensive to compute because
of the double integral and the evaluation of the Green function 𝒢𝑘 . Plus, the choice of the quadrature formulas has to
be made with extra care because of the singularity of 𝒢𝑘 .
Depending on the choice of the boundary integral formulation or boundary condition, the Double Layer Potential DL
can also be of use:
∫︁
𝜕
𝑞 ↦→ DL(𝑞)(𝑥) = 𝒢𝑘 (𝑥 − 𝑦)𝑞(𝑦)𝑑𝜎(𝑦).
Γ 𝜕𝑛(𝑦)
Similarly, we have used the Single Layer Operator 𝒮ℒ in our variational problem
∫︁
𝑝, 𝑞 ↦→ 𝒮ℒ(𝑝, 𝑞) = 𝑝(𝑥)𝑞(𝑦)𝒢𝑘 (𝑥 − 𝑦)𝑑𝜎(𝑥, 𝑦).
Γ×Γ
There are three other building blocks that can be of use in the boundary element method, and depending on the problem
and the choice of the formulation a boundary integral method makes use of one or a combination of these building
blocks:
the Double Layer Operator 𝒟ℒ:
∫︁
𝜕
𝑝, 𝑞 ↦→ 𝒟ℒ(𝑝, 𝑞) = 𝑝(𝑥)𝑞(𝑦) 𝒢𝑘 (𝑥 − 𝑦)𝑑𝜎(𝑥, 𝑦)
Γ×Γ 𝜕𝑛(𝑦)
the Transpose Double Layer Operator 𝒯 𝒟ℒ:
∫︁
𝜕
𝑝, 𝑞 ↦→ 𝒯 𝒟ℒ(𝑝, 𝑞) = 𝑝(𝑥)𝑞(𝑦) 𝒢𝑘 (𝑥 − 𝑦)𝑑𝜎(𝑥, 𝑦)
Γ×Γ 𝜕𝑛(𝑥)
the Hypersingular Operator ℋ𝒮:
∫︁
𝜕 𝜕
𝑝, 𝑞 ↦→ ℋ𝒮(𝑝, 𝑞) = 𝑝(𝑥)𝑞(𝑦) 𝒢𝑘 (𝑥 − 𝑦)𝑑𝜎(𝑥, 𝑦)
Γ×Γ 𝜕𝑛(𝑥) 𝜕𝑛(𝑦)
Hierarchical matrices
Although BEMTool can compute the BEM matrix coefficients by accurately and efficiently evaluating the boundary
integral operator, it is very costly and often prohibitive to compute and store all 𝑁 2 coefficients of the matrix. Thus, we
have to rely on a matrix compression technique. To do so, FreeFEM relies on the Hierarchical Matrix, or H-Matrix
format.
Low-rank approximation
where 𝑟 ≤ 𝑁, u𝑗 ∈ C𝑁 , v𝑗 ∈ C𝑁 .
𝑁2
If 𝑟 < 2𝑁 , the computing and storage cost is reduced to 𝒪(𝑟𝑁 ) < 𝒪(𝑁 2 ). We say that B is low rank.
Usually, the matrices we are interested in are not low-rank, but they may be well-approximated by low-rank matrices.
We may start by writing their Singular Value Decomposition (SVD):
𝑁
∑︁
B= 𝜎𝑗 u𝑗 v𝑇𝑗
𝑗=1
where (𝜎𝑗 )𝑁𝑗=1 are the singular values of B in decreasing order, and (u𝑗 )𝑗=1 and (v𝑗 )𝑗=1 its left and right singular
𝑁 𝑁
vectors respectively.
Indeed, if B has fast decreasing singular values 𝜎𝑗 , we can obtain a good approximation of B by truncating the SVD
sum, keeping only the first 𝑟 terms. Although the truncated SVD is actually the best low-rank approximation possible
(Eckart-Young-Mirsky theorem), computing the SVD is costly (𝒪(𝑁 3 )) and requires computing all 𝑁 2 coefficients of
the matrix, which we want to avoid.
Thankfully, there exist several techniques to approximate a truncated SVD by computing only some coefficients of the
initial matrix, such as randomized SVD, or Partially pivoted Adaptive Cross Approximation (partial ACA), which
requires only 2𝑟𝑁 coefficients.
Unfortunately, BEM matrices generally do not have fast decreasing singular values. However, they can exhibit sub-
blocks with rapidly decreasing singular values, thanks to the asymptotically smooth nature of the BEM kernel. Let us
look for example at the absolute value of the matrix coefficients in the 2D (circle) case below:
• blocks near the diagonal contain information about the near-field interactions, which are not low-rank in nature
• blocks away from the diagonal corresponding to the interaction between two clusters of geometric points 𝑋 and
𝑌 satisfying the so-called admissibility condition
max(diam(𝑋), diam(𝑌 )) ≤ 𝜂 dist(𝑋, 𝑌 ) (3.36)
are far-field interactions and have exponentially decreasing singular values. Thus, they can be well-approximated by
low-rank matrices.
The idea is then to build a hierarchical representation of the blocks of the matrix, then identify and compress admis-
sible blocks using low-rank approximation.
We can then build the H-Matrix by taking the following steps:
1. build a hierarchical partition of the geometry, leading to a cluster tree of the unknowns. It can for example be
defined using bisection and principal component analysis.
2. from this hierarchical clustering, define and traverse the block cluster tree representation of the matrix structure,
identifying the compressible blocks using admissibility condition (3.36)
3. compute the low-rank approximation of the identified compressible blocks using e.g. partial ACA ; the remaining
leaves corresponding to near-field interactions are computed as dense blocks.
(0)
J1
(1) (1)
J1 J2
(2)
J1
(1)
J1
⇒ J2
(2)
⇒
(0)
J1
(2)
J3
(1)
J2
(2)
J4
the H-Matrix format is implemented in the C++ library Htool. Htool is a parallel header-only library written by Pierre
Marchand and Pierre-Henri Tournier. It is interfaced with FreeFEM and provides routines to build hierarchical matrix
structures (cluster trees, block trees, low-rank matrices, block matrices) as well as efficient parallel matrix-vector and
matrix-matrix product using MPI and OpenMP. Htool is interfaced with BemTool to allow the compression of BEM
matrices using the H-Matrix format in FreeFEM.
(0)
J1
(1) (1)
J1 J2
J1
(2)
MPI process 1
(1)
J1
J2
(2)
MPI process 2
(0)
J1
J3
(2)
MPI process 3
(1)
J2
J4
(2)
MPI process 4
2D
In 2D, the geometry of the boundary can be defined with the border keyword and discretized by constructing a line
or curve mesh of type meshL using buildmeshL:
With the extract keyword, we can also extract the boundary of a 2D mesh (need to load "msh3"):
1 load "msh3"
2 mesh Th = square(10,10);
3 meshL ThL = extract(Th);
1 load "msh3"
2 meshS ThS = square3(10,10);
3 int[int] labs = [1,2];
4 meshL ThL = extract(ThS, label=labs);
You can find much more information about curve mesh generation here.
3D
In 3D, the geometry of the boundary surface can be discretized with a surface mesh of type meshS, which can be built
by several ways, for example using the square3 constructor:
1 load "msh3"
2 real R = 3, r=1, h=0.2;
3 int nx = R*2*pi/h, ny = 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 meshS ThS = square3(nx,ny,[torex,torey,torez],removeduplicate=true);
1 load "msh3"
2 mesh Th = square(10,10);
3 meshS ThS = movemesh23(Th, transfo=[x,y,cos(x)^2+sin(y)^2]);
1 load "msh3"
2 mesh3 Th3 = cube(10,10,10);
3 int[int] labs = [1,2,3,4];
4 meshS ThS = extract(Th3, label=labs);
You can find much more information about surface mesh generation here.
Depending on whether your problem is posed on a bounded or unbounded domain, you may have to set the orientation of
the outward normal vector 𝑛 to the boundary. You can use the OrientNormal function with the parameter unbounded
set to 0 or 1 (the normal vector 𝑛 will then point to the exterior of the domain you are interested in):
You can use shift + t on a plot of a boundary mesh to display the outward normal vector 𝑛:
−∆𝑢 − 𝑘 2 𝑢 = 0, 𝑘 ∈ C,
with
• 𝑘 = 0 (Laplace)
• 𝑘 ∈ R*+ (Helmholtz)
• 𝑘 ∈ 𝚤R*+ (Yukawa)
FreeFEM can also solve Maxwell’s equations with the Electric Field Integral Equation (EFIE) formulation. More
details are given in the section BEM for Maxwell’s equations.
First, the BEM plugin needs to be loaded:
1 load "bem"
The information about the type of operator and the PDE can be specified by defining a variable of type BemKernel:
1 BemKernel Ker("SL",k=2*pi);
You can choose the type of operator depending on your formulation (see Boundary Integral Operators):
• "SL": Single Layer Operator 𝒮ℒ
• "DL": Double Layer Operator 𝒟ℒ
• "TDL": Transpose Double Layer Operator 𝒯 𝒟ℒ
• "HS": Hyper Singular Operator ℋ𝒮
You can also specify the BEM kernel directly inside the integral:
Depending on the choice of the BEM formulation, there can be additional terms in the variational form. For example,
Second kind formulations have an additional mass term:
We can also define a linear combination of two BEM kernels, which is useful for Combined formulations:
1 complex k=2*pi;
2 BemKernel Ker1("HS", k=k);
3 BemKernel Ker2("DL", k=k);
4 BemKernel Ker = 1./(1i*k) * Ker1 + Ker2;
5 varf vbem(u,v) = int2dx2d(ThS)(ThS)(BEM(Ker,u,v)) - int2d(ThS)(0.5*u*v);
As a starting point, you can find how to solve a 2D scattering problem by a disk using a First kind, Second kind and
Combined formulation, for a Dirichlet (here) and Neumann (here) boundary condition.
1 fespace Uh(ThS,P1);
2 HMatrix<complex> H = vbem(Uh,Uh);
Behind the scenes, FreeFEM is using Htool and BEMTool to assemble the H-Matrix.
Note
Since Htool is a parallel library, you need to use FreeFem++-mpi or ff-mpirun to be able to run your BEM script.
The MPI parallelism is transparent to the user. You can speed up the computation by using multiple cores:
1 ff-mpirun -np 4 script.edp -wg
You can specify the different Htool parameters as below. These are the default values:
1 HMatrix<complex> H = vbem(Uh,Uh,
2 compressor = "partialACA", // or "fullACA", "SVD"
3 eta = 10., // parameter for the admissibility condition
4 eps = 1e-3, // target compression error for each block
5 minclustersize = 10, // minimum block side size min(n,m)
6 maxblocksize = 1000000, // maximum n*m block size
7 commworld = mpiCommWorld); // MPI communicator
You can also set the default parameters globally in the script by changing the value of the global variables htoolEta,
htoolEpsilon, htoolMinclustersize and htoolMaxblocksize.
Once assembled, the H-Matrix can also be plotted with
1 display(H, wait=true);
FreeFEM can also output some information and statistics about the assembly of H:
1 Uh<complex> p, b;
2 varf vrhs(u,v) = -int2d(ThS)(uinc*v);
3 b[] = vrhs(0,Uh);
We can then solve the linear system to obtain 𝑝, with the standard syntax:
1 p[] = H^-1*b[];
Under the hood, FreeFEM solves the linear system with GMRES with a Jacobi (diagonal) preconditioner.
In order to benefit from low-rank compression, instead of using (3.34) to sequentially compute the value 𝑢(𝑥) at each
point of interest 𝑥, we can compute the discretization of the Potential on a target finite element space UhOut defined
on an output mesh ThOut with an H-Matrix.
First, let us define the variational form corresponding to the potential that we want to use to reconstruct our solution.
Similarly to the kernel case, the POT keyword takes the potential as argument. Note that we have a single integral, and
that v plays the role of 𝑥.
We can then assemble the rectangular H-Matrix from the potential variational form:
1 fespace UhOut(ThOut,P1);
2 HMatrix<complex> HP = vpot(Uh,UhOut);
Computing 𝑢 on UhOut is then just a matter of performing the matrix-vector product of HP with p:
1 UhOut<complex> u;
2 u[] = HP*p[];
3 plot(u);
1 load "bem"
2 load "msh3"
3
4 real k = 10;
5
6 int n = 100;
7
14 fespace Uh(ThL,P1);
15 HMatrix<complex> H = vbem(Uh,Uh);
16
22 p[] = H^-1*b[];
23
26 int np = 200;
27 int R = 4;
28 border b1(t=-R, R){x=t; y=-R;}
29 border b2(t=-R, R){x=R; y=t;}
30 border b3(t=-R, R){x=-t; y=R;}
31 border b4(t=-R, R){x=-R; y=-t;}
32 mesh ThOut = buildmesh(b1(np)+b2(np)+b3(np)+b4(np)+circle(-n));
33
34 fespace UhOut(ThOut,P1);
35 HMatrix<complex> HP = vpot(Uh,UhOut);
36
37 UhOut<complex> u, utot;
38 u[] = HP*p[];
39
40 utot = u + uinc;
41 plot(utot,fill=1,value=1,cmm="u_total");
curl𝐸 − 𝚤𝜔𝜇0 𝐻 = 0, in R3 ∖Ω
⎧
⎪
⎪
⎪
curl𝐻 + 𝚤𝜔𝜖0 𝐸 = 0, in R3 ∖Ω
⎪
⎪
⎪
⎨
𝐸 ∧ 𝑛 = −𝐸 𝑖𝑛𝑐 ∧ 𝑛, on Γ (3.37)
⎪ ⃦ ⃦
⃦√
⎪
⎪
⎪ √ 𝑟 ⃦
⎩ lim 𝑟 ⃦ 𝜖0 𝐸 − 𝜇0 𝐻 ∧ ⃦ = 0,
⎪
⎪
𝑟→+∞ ⃦ |𝑟| ⃦
where 𝐸 is the scattered electric field, 𝐻 is the scattered magnetic field, 𝜔 is the angular frequency, 𝜖0 is the vacuum
permittivity and 𝜇0 is the vacuum magnetic permeability. The angular frequency verifies 𝜔 = 2𝜋𝑓 where 𝑓 is the
frequency.
The total electric and magnetic fields are given by
{︃
𝐸 total = 𝐸 inc + 𝐸
(3.38)
𝐻 total = 𝐻 inc + 𝐻.
𝑗 = 𝚤 𝜅 𝑍0 𝑛 ∧ (𝐻 + 𝐻inc ) ,
√︁
where 𝑍0 = 𝜇0
𝜖0 is the vacuum impedance and 𝜅 = 𝜔
𝑐 is the wave number, with 𝑐 = √1
𝜇0 𝜖0 the speed of light.
Note
For the discretization of the EFIE, we use the surface Raviart-Thomas Element of order 0 RT0S:
It is a vector finite element space of size 3. The magnetic current 𝑗 belongs to this space:
The Single Layer Operator 𝒮ℒMA involved in the EFIE (3.39) is defined as a BemKernel with the string "MA_SL":
1 BemKernel KerMA("MA_SL",k=k);
We are working in a vector space with 3 components. Hence, the BEM variational form is:
As before, we can use low-rank compression to build a H-Matrix approximation of the discrete bilinear form (see
Hierarchical matrices):
Following the same steps as in the section Compute the solution, we can reconstruct the scattered electric field 𝐸 using
the Single Layer Potential SLMA :
1 varf vMApot([u1,u2,u3],[v1,v2,v3]) =
2 int2d(ThS)(POT(PotMA,[u1,u2,u3],[v1,v2,v3]));
where [v1,v2,v3] plays the role of the scattered electric field 𝐸 that we want to reconstruct at a set of points [𝑥, 𝑦, 𝑧].
We consider an output mesh ThOut on which we want to reconstruct a P1 approximation of the three components of
𝐸:
1 fespace UhOutV(ThOut,[P1,P1,P1]);
2 // FE function of the scattered field E
3 UhOutV<complex> [Ex, Ey, Ez];
We can build the rectangular H-Matrix corresponding to the variational form for the potential:
Finally, the scattered electric field 𝐸 can be obtained by performing a matrix-vector product with the potential H-Matrix
HpotMA and the discrete magnetic current [mcx,mcy,mcz]:
We can plot the real part of the total electric field with:
(a) real part of the x component of the total electric field (b) real part of the y component of the total electric field
The current definition of FE spaces and variational problems with multiple variables or components is subject to two
limitations:
• When defining periodic vector spaces, all components are considered periodic (see Uh above)
• All variables/components have to be defined on the same mesh
The introduction of composite spaces aims at lifting these limitations and facilitates the definition of coupled problems,
allowing to easily define and solve more general problems mixing different meshes or mesh types.
where 𝑇ℎ𝑖 is the mesh of the 𝑖th FE space and 𝐹 𝐸 𝑖 is the type of finite element:
• scalar FE element P1, P2 . . .
• vector FE element [P1,P1], RT0 . . .
• FE element with periodic boundary P1,periodic=[[2,y],[4,y]]
Each FE space 𝑈ℎ𝑖 can be defined on a different mesh 𝑇ℎ𝑖 ; meshes 𝑇ℎ𝑖 𝑖=1,𝑛 can even be of different types (mesh,
(︀ )︀
First examples
Defining a composite space as a product of FE spaces can be done by writing the product directly with * or by using
the angular bracket syntax < and >. For example:
• vector FE space with a periodic component in one direction:
1 fespace Uh1(Th,P2);
2 fespace Uh2(Th,P2,periodic=[[1,x],[3,x]]);
3 fespace Ph(Th,P1);
4
• composite space with the first component defined on a triangular 2D mesh Th and the second component defined
on a curve mesh ThL (here ThL is the boundary of Th). This can be useful for example for volume-surface
coupling or FEM-BEM coupling:
1 load "msh3"
2 mesh Th = square(50,50); // Th is a 2d volume mesh
3 meshL ThL = extract(Th); // ThL is a 1d curve mesh
4 fespace Uh(Th,P1);
5 fespace UhL(ThL,P1);
6
⎨ −∆u + ∇𝑝 = f in Ω,
⎧
∇ · u = 0 in Ω, (3.42)
u = 𝑔 on Γ,
⎩
The stabilization term in blue is added to the variational form to fix the constant for the pressure (we take for example
𝜖 = 10−10 ).
We choose the P2-iso-P1 finite element for the velocity and pressure. This element requires two different meshes: a
coarser mesh for pressure and a finer mesh for velocity obtained by splitting each triangle of the pressure mesh into
four triangles.
20 Uh [u1,u2],[v1,v2];
21 Ph p,q;
(continues on next page)
29 plot([u1,u2], cmm="u");
30 plot(p, cmm="p");
You can also find this example in the FreeFEM distribution here.
Note that with the problem or solve syntax, the composite nature of the FE space has to be indicated directly in the
problem/solve instruction using angular brackets < and >:
FreeFEM falls back to the “standard” evaluation of the variational form and outputs the following error message: Exec
error : all the finite element spaces must be defined on the same mesh in solve.
by
1 fespace Xh=Uh*Ph;
2
9 matrix M = Stokes(Xh,Xh);
10 real[int] b = Stokes(0,Xh);
11 real[int] sol = M^-1*b;
12
Note
The sign of the linear form is flipped because with the varf/matrix syntax we are solving 𝑀 𝑥 = 𝑏 whereas with
problem or solve we are solving 𝑀 𝑥 − 𝑏 = 0.
Note that in this case the explicit definition of the composite fespace Xh=Uh*Ph is mandatory, as it is used in the
instantiation of the variational form in Stokes(Xh,Xh). However, this time the <, > notation in the varf is optional:
we can also write
In the last instruction, the solution vector obtained when solving the linear system is dispatched to the velocity and
pressure FE functions:
Under the hood, the matrix and right-hand side of a composite problem are assembled in a contiguous way with respect
to the different components. This means that the linear system we are solving has the expected structure
A −𝐵 𝑇
(︂ )︂ (︂ )︂ (︂ )︂
u f
=
−𝐵 −𝜖𝐼 𝑝 0
with MUMPS
With minimal changes in the script, we can parallelize the assembly of the linear system on multiple MPI processes
and solve it in parallel using the MUMPS solver.
First, we need to load the MUMPS and bem plugins (this functionality is implemented in the bem plugin for now but this
will be changed in future releases):
1 load "MUMPS"
2 load "bem"
Now we just need to specify that we want to solve the problem with a sparse direct solver (MUMPS), with the linear
system being distributed over all MPI processes (with master=-1):
1 fespace Xh=Uh*Ph;
2
At the end of the solution phase, all MPI processes hold the global solution of the problem in (u1,u2,p).
In order to use multiple MPI processes, we run the script in parallel on e.g. 4 computing cores with
with PETSc
Alternatively, we can use the PETSc library (see PETSc and SLEPc) to solve the linear system. In contrast to the usual
fully distributed PETSc framework (see PETSc examples), with the composite syntax the parallel data distribution is
hidden to the user and the solution output is given in the global space. This is part of the continuous efforts to hide the
difficulties associated with the parallelization of a user script, making it more transparent to the user.
In order to use PETSc as a solver, we simply load the PETSc plugin and define a PETSc Mat instead of a matrix for
the linear system matrix:
1 load "PETSc"
2 load "bem"
3
4 fespace Xh=Uh*Ph;
5
12 Mat M = Stokes(Xh,Xh);
13 real[int] b = Stokes(0,Xh);
14 real[int] sol = M^-1*b;
15
We can then indicate what type of PETSc solver we want to use with PETSc-specific command-line arguments. We
can start by using a simple distributed LU solver:
We can also specify the solver by putting the corresponding PETSc flags directly in the script, with the set() instruc-
tion:
1 Mat M = Stokes(Xh,Xh);
2 set(M, sparams = "-pc_type lu");
An interesting feature of using PETSc to solve a composite problem is that we can easily define a fieldsplit precondi-
tioner combining separate preconditioners defined for each variable of the composite space, as the underlying composite
PETSc operator already carries the block structure corresponding to the different variables.
This means that we can reference each variable – or split – right out of the box in order to define our fieldsplit precon-
ditioner. They come in the same order as in the definition of the composite space ; in our Stokes example, the velocity
space is split 0 and the pressure space is split 1. As an illustration, we can use the Schur complement method to solve
our Stokes problem, using a multigrid HYPRE preconditioner for the velocity block:
1 Mat M = Stokes(Xh,Xh);
2 set(M, sparams = "-pc_type fieldsplit -pc_fieldsplit_type schur -fieldsplit_0_pc_type␣
˓→hypre");
3.10 Plugins
3.10.1 gsl
The interface with gsl spline is available in FreeFEM, the seven kind of spline are
0. gslinterpcspline: default type of spline
1. gslinterpakima
2. gslinterpsteffen
3. gslinterplinear
4. gslinterppolynomial
5. gslinterpcsplineperiodic
6. gslinterpakimaperiodic
A brief wing example given all the syntax:
1 load "gsl"
2
3 // Parameters
4 int n = 10;
5 real[int, int] dspline(2,n+1); //data points to define the spline
6 for(int i = 0; i <= n; ++i){ //set data points
7 real xx = square(real(i)/n);
8 real yy = sin(xx*pi*2);
9 dspline(0, i) = xx;
10 dspline(1, i) = yy;
11 }
12
13 // GSL splines
14 gslspline spline1(gslinterpcspline, dspline); //define the spline1
15 gslspline spline11(dspline); //define the spline11
16 gslspline spline2(gslinterpsteffen, dspline); //define the spline2
17 gslspline spline3(gslinterpcspline, dspline(0, :), dspline(1, :));
18 gslspline spline33(dspline(0, :), dspline(1, :)); //define the spline3
19 spline1 = spline2; //copy spline2 in spline1
20
21 real t = 1.;
22 real s1 = spline1(t); //evaluate the function spline1 at t
23 cout << "spline1(t) = " << s1 << endl;
24 real ds1 = spline1.d(t); //evaluate the derivative of function spline1 at t
25 cout << "spline1.d(t) = " << ds1 << endl;
26 real dds1 = spline1.dd(t); //evaluate the second derivative of function spline1 at t
27 cout << "spline1.dd(t) = " << dds1 << endl;
3.10.2 ffrandom
Plugin to linux random functions.
The range of the random generator is from 0 to (231 ) − 1.
1 load "ffrandom"
2
15 srandom(10);
16 cout << random() << endl;
17 cout << random() << endl;
18 cout << random() << endl;
• long ffsem_wait(ff_Psem sem); the semaphore referenced by sem is locked. When calling sem_wait(), if
the semaphore’s value is zero, the calling thread will block until the lock is acquired or until the call is interrupted
by a signal.
Alternatively, the sem_trywait() function will fail if the semaphore is already locked, rather than blocking on
the semaphore
• long ffsem_trywait(ff_Psem p);
Secondly, the mmap functions:
• typedef struct FF_P_mmap *ff_Pmmap; the pointer to data structure
• ff_Psem ffmmap_malloc(); malloc an empty data structure
• void ffmmap_del(ff_Pmmap p); clean and free the pointer
• void ffmmap_destroy(ff_Pmmap p); clean, close the data structure
• void ffmmap_init0(ff_Pmmap p); make a correct empty of the data structure
• long ffmmap_msync(ff_Pmmap p, long off, long ln); call writes modified whole pages back to the
filesystem and updates the file modification time. Only those pages containing addr and len-1 succeeding
locations will be examined.
• void ffmmap_init(ff_Pmmap p, const char *nmm, long len); allocate memory, or map files or de-
vices into memory.
• long ffmmap_read(ff_Pmmap p, void *t, size_t n, size_t off); read n bytes from the mmap at
memory off in pointer t.
• long ffmmap_write(ff_Pmmap p, void *t, size_t n, size_t off); write n bytes to the mmap at
memory off in pointer t.
The FreeFEM corresponding functions:
• Pmmap sharedata(filename, 1024); new type to store the mmap informations of name store in string
filename with 1024 is the size the sharedata zone and file.
• Psemaphore smff("ff-slave", creat); new type to store the semaphore of name ff-slave where creat
is a boolean to create or use a existing semaphore.
• Wait(sem) the semaphore referenced by sem is locked. When calling Wait(sem), if the semaphore’s value is
zero, the calling thread will block until the lock is acquired or until the call is interrupted by a signal. Alternatively,
the trywait(sem) function will fail if the semaphore is already locked, rather than blocking on the semaphore.
• Post(sem) the semaphore referenced by sem is unlocked, the value of the semaphore is incremented, and all
threads which are waiting on the semaphore are awakened.
• Read(sharedata ,offset, data); read the variable data from the place offset in sharedata mmap.
• Write(sharedata, offset, data); write the variable data at the place offset in sharedata mmap.
The full example:
The FFMaster.c file:
1 #include "libff-mmap-semaphore.h"
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 ff_Psem sem_ff, sem_c; //the semaphore for mutex
6
17 sem_ff = ffsem_malloc();
18 sem_c = ffsem_malloc();
19 shd = ffmmap_malloc();
20
25 status = 1;
26 ffmmap_write(shd, &status, sizeof(status), 8);
27 ffmmap_msync(shd, 0, 32);
28
29 char ff[1024];
30 sprintf(ff, "FreeFem++ FFSlave.edp -nw -ns -v %d&", debug);
31 system(ff); //lauch FF++ in batch no graphics
32 if(debug) printf("cc: before wait\n");
33
49 status = 0; //end
50 ffmmap_write(shd, &status, sizeof(status), 8);
51 ffsem_post(sem_c);
52 printf("End Master \n");
53 ffsem_wait(sem_ff);
54 ffsem_del(sem_ff);
55 ffsem_del(sem_c);
56 ffmmap_del(shd);
57 return 0;
58 }
1 load "ff-mmap-semaphore"
2
8 // Mesh
9 mesh Th = square(10, 10);
10 int[int] Lab = [1, 2, 3, 4];
11
12 // Fespace
13 fespace Vh(Th, P1);
14 Vh u, v;
15
16 // Macro
17 macro grad(u) [dx(u), dy(u)] //
18
19 int status = 1;
20 cout << " FF status = " << status << endl;
21 real cff, rff;
22
23 // Problem
24 problem Pb (u, v)
25 = int2d(Th)(
26 grad(u)'*grad(v)
27 )
28 - int2d(Th)(
29 cff*v
30 )
31 + on(Lab, u=0)
32 ;
33
37 while (1){
38 if (verbosity > 9) cout << " FF: before FF wait \n";
39 Wait(smc); //wait from cint write ok
40 Read(sharedata, 0, cff);
41 Read(sharedata, 8, status);
42
43 cout << " After wait .. FF " << cff << " " << status << endl;
44 if(status <= 0) break;
45
46 // Solve
47 Pb;
48 rff = int2d(Th)(u*u);
49 cout << " ** FF " << cff << " " << rff << endl;
50
51 // Write
52 Write(sharedata, 16, rff);
53 Post(smff); //unlock cc
(continues on next page)
1 cc -c libff-mmap-semaphore.c
2 cc FFMaster.c -o FFMaster libff-mmap-semaphore.o -g -pthread
3 ff-c++ -auto ff-mmap-semaphore.cpp
4 ./FFMaster
The output:
3.11 Developers
3.11.1 File formats
Mesh file data structure
The mesh data structure, output of a mesh generation algorithm, refers to the geometric data structure and in some case
to another mesh data structure.
In this case, the fields are
1 MeshVersionFormatted 0
2
3 Dimension [DIM](int)
4
5 Vertices
6 [Number of vertices](int)
7 X_1(double) Y_1(double) (Z_1(double)) Ref_1(int)
8 ...
9 X_nv(double) Y_nv(double) (Z_nv(double)) Ref_nv(int)
10
11 Edges
12 [Number of edges](int)
13 Vertex1_1(int) Vertex2_1(int) Ref_1(int)
14 ...
15 Vertex1_ne(int) Vertex2_ne(int) Ref_ne(int)
16
17 Triangles
18 [Number of triangles](int)
19 Vertex1_1(int) Vertex2_1(int) Vertex3_1(int) Ref_1(int)
20 ...
21 Vertex1_nt(int) Vertex2_nt(int) Vertex3_nt(int) Ref_nt(int)
22
23 Quadrilaterals
24 [Number of Quadrilaterals](int)
25 Vertex1_1(int) Vertex2_1(int) Vertex3_1(int) Vertex4_1(int) Ref_1(int)
26 ...
27 Vertex1_nq(int) Vertex2_nq(int) Vertex3_nq(int) Vertex4_nq(int) Ref_nq(int)
28
29 Geometry
30 [File name of geometric support](char*)
31
32 VertexOnGeometricVertex
33 [Number of vertex on geometric vertex](int)
(continues on next page)
38 EdgeOnGeometricEdge
39 [Number of geometric edge](int)
40 Edge_1(int) EdgeGeometry_1(int)
41 ...
42 Edge_neg(int) EdgeGeometry_neg(int)
43
44 CrackedEdges
45 [Number of cracked edges](int)
46 Edge1_1(int) Edge2_1(int)
47 ...
48 Edge1_nce(int) Edge2_nce(int)
1 MeshSupportOfVertices
2 [File name of mesh support](char*)
3
4 VertexOnSupportVertex
5 [Number of vertex on support vertex](int)
6 Vertex_1(int) VertexSupport_1(int)
7 ...
8 Vertex_nvsv(int) VertexSupport_nvsv(int)
9
10 VertexOnSupportEdge
11 [Number of vertex on support edge](int)
12 Vertex_1(int) EdgeSupport_1(int) USupport_1(double)
13 ...
14 Vertex_nvse(int) EdgeSupport_nvse(int) USupport_nvse(double)
15
16 VertexOnSupportTriangle
17 [Number of vertex on support triangle](int)
18 Vertex_1(int) TriangleSupport_1(int) USupport_1(double) VSupport_1(double)
19 ...
20 Vertex_nvst(int) TriangleSupport_nvst(int) USupport_nvst(double) VSupport_nvst(double)
21
22 VertexOnSupportQuadrilaterals
23 [Number of vertex on support quadrilaterals]
24 Vertex_1(int) TriangleSupport_1(int) USupport_1(double) VSupport_1(double)
25 ...
26 Vertex_nvsq(int) TriangleSupport_nvsq(int) USupport_nvsq(double) VSupport_nvsq(double)
7 ...
8
Metric file
A metric file can be of two types, isotropic or anisotropic.
The isotropic file is such that
1 [Number of vertices](int) 1
2 h_0(double)
3 ...
4 h_nv(double)
1 [Number of vertices](int) 3
2 a11_0(double) a21_0(double) a22_0(double)
3 ...
4 a11_nv(double) a21_nv(double) a22_nv(double)
AM Files
In Fortran the am files are read as follows:
AMDBA Files
In Fortran the amdba files are read as follows:
msh Files
First, we add the notions of boundary edges
• nbbe the number of boundary edge
• nube(1:2, 1:nbbe) an integer array giving the two vertex numbers of boundary edges
• refbe(1:nbbe) an integer array giving the reference numbers of boundary edges
In Fortran the msh files are read as follows:
ftq Files
In Fortran the ftq files are read as follows:
1 open(1,file='xxx.ftq',form='formatted',status='old')
2 read (1,*) nbv,nbe,nbt,nbq
3 read (1,*) (k(j),(nu(i,j),i=1,k(j)),reft(j),j=1,nbe)
4 read (1,*) ((c(i,k),i=1,2),refs(k),j=1,nbv)
5 close(1)
where if k(j) = 3 when the element j is a triangle and k(j) = 4 when the the element j is a quadrilateral.
The following fields begin with one of the following keyword: SolAtVertices, SolAtEdges, SolAtTriangles,
SolAtQuadrilaterals, SolAtTetrahedra, SolAtPentahedra, SolAtHexahedra.
In each field, we give then in the next line the number of elements in the solutions (SolAtVertices: number of
vertices, SolAtTriangles: number of triangles, . . . ). In other lines, we give the number of solutions, the type of
solution (1: scalar, 2: vector, 3: symmetric tensor). And finally, we give the values of the solutions on the elements.
The file must be ended with the keyword End.
The real element of symmetric tensor :
3𝑑 3𝑑 3𝑑
⎛ ⎞
𝑆𝑇𝑥𝑥 𝑆𝑇𝑥𝑦 𝑆𝑇𝑥𝑧 (︂ 2𝑑 2𝑑
)︂
𝑆𝑇𝑥𝑥 𝑆𝑇𝑥𝑦
3𝑑
𝑆𝑇 = ⎝ 𝑆𝑇𝑦𝑥3𝑑 3𝑑
𝑆𝑇𝑦𝑦 3𝑑 ⎠
𝑆𝑇𝑦𝑧 𝑆𝑇 2𝑑
= 2𝑑 2𝑑 (3.44)
3𝑑 3𝑑 3𝑑 𝑆𝑇𝑦𝑥 𝑆𝑇𝑦𝑦
𝑆𝑇𝑧𝑥 𝑆𝑇𝑧𝑦 𝑆𝑇𝑧𝑧
1 SolAtTetrahedra
2 [Number of tetrahedra](int)
3 [Number of solutions](int) [Type of solution 1](int) ... [Type of soution nt](int)
4
9 ...
10
where 𝑉 𝑉 3 = [𝑆𝑇𝑥𝑥
3𝑑 3𝑑
, 𝑆𝑇𝑦𝑥 3𝑑
, 𝑆𝑇𝑦𝑦 3𝑑
, 𝑆𝑇𝑧𝑥 3𝑑
, 𝑆𝑇𝑧𝑦 3𝑑
, 𝑆𝑇𝑧𝑧 ].
For a two dimensional mesh Th, A real scalar functions 𝑓 2, a vector fields Ψ = [Ψ1, Ψ2] and a symmetric tensor 𝑆𝑇 2𝑑
(3.44) at triangles is stored in the file f2PsiST2dTh3.solb using :
where 𝑉 𝑉 2 = [𝑆𝑇𝑥𝑥
2𝑑 2𝑑
, 𝑆𝑇𝑦𝑥 2𝑑
, 𝑆𝑇𝑦𝑦 ]
The arguments of savesol functions are the name of a file, a mesh and solutions. These arguments must be given in
this order.
The parameters of this keyword are :
• order = 0 is the solution is given at the center of gravity of elements. 1 is the solution is given at the vertices
of elements.
In the file, solutions are stored in this order : scalar solutions, vector solutions and finally symmetric tensor solutions.
Tip
Tip
The
∫︀ degrees of freedom are the flux through an edge 𝑒 of the mesh, where the flux of the function f : R −→ R
2 2
is 𝑒 f .𝑛𝑒 , 𝑛𝑒 is the unit normal of edge 𝑒 (this implies a orientation of all the edges of the mesh, for example we
can use the global numbering of the edge vertices and we just go to small to large number).
To compute this flux, we use a quadrature formula with one point, the middle point of the edge. Consider a triangle
𝑇 with three vertices (a, b, c).
Let denote the vertices numbers by 𝑖𝑎 , 𝑖𝑏 , 𝑖𝑐 , and define the three edge vectors e0 , e1 , e2 by 𝑠𝑔𝑛(𝑖𝑏 − 𝑖𝑐 )(b − c),
𝑠𝑔𝑛(𝑖𝑐 − 𝑖𝑎 )(c − a), 𝑠𝑔𝑛(𝑖𝑎 − 𝑖𝑏 )(a − b).
The three basis functions are:
𝑠𝑔𝑛(𝑖𝑏 − 𝑖𝑐 ) 𝑠𝑔𝑛(𝑖𝑐 − 𝑖𝑎 ) 𝑠𝑔𝑛(𝑖𝑎 − 𝑖𝑏 )
𝜔𝐾
0 = (𝑥 − 𝑎), 𝜔𝐾
1 = (𝑥 − 𝑏), 𝜔𝐾
2 = (𝑥 − 𝑐),
2|𝑇 | 2|𝑇 | 2|𝑇 |
• 𝛼0 = −e02 , 𝛼1 = e01 ,
𝛼2 = −e12 , 𝛼3 = e11 , 𝛼4 = −e22 , 𝛼5 = e21 (effectively, the vector (−e𝑚 2∫︀ , e1 ) is orthogonal to the edge
𝑚
e𝑚 = (𝑒𝑚 1 , 𝑒𝑚
2 ) with a length equal to the side of the edge or equal to 𝑒𝑚
1).
• 𝑖𝑘 = {0, 0, 1, 1, 2, 2},
• 𝑝𝑘 = {0, 0, 1, 1, 2, 2} , 𝑗𝑘 = {0, 1, 0, 1, 0, 1, 0, 1}.
1 #include "error.hpp"
2 #include "rgraph.hpp"
3 using namespace std;
4 #include "RNM.hpp"
5 #include "fem.hpp"
6 #include "FESpace.hpp"
7 #include "AddNewFE.h"
8
26 void FB(const bool *watdd, const Mesh &Th, const Triangle &K,
27 const R2 &PHat, RNMK_ &val) const;
28
where the array data is formed with the concatenation of five array of size NbDoF and one array of size N.
This array is:
1 int TypeOfFE_RTortho::Data[] = {
2 //for each df 0, 1, 3:
3 3, 4, 5, //the support of the node of the df
4 0, 0, 0, //the number of the df on the node
5 0, 1, 2, //the node of the df
6 0, 0, 0, //the df come from which FE (generally 0)
7 0, 1, 2, //which are the df on sub FE
8 0, 0
9 }; //for each component j=0, N-1 it give the sub FE associated
where the support is a number 0, 1, 2 for vertex support, 3, 4, 5 for edge support, and finally 6 for element support.
The function to defined the function 𝜔 𝐾
𝑖 , this function return the value of all the basics function or this derivatives in
array val, computed at point Phat on the reference triangle corresponding to point R2 P=K(Phat); on the current
triangle K.
The index 𝑖, 𝑗, 𝑘 of the array 𝑣𝑎𝑙(𝑖, 𝑗, 𝑘) correspond to:
• 𝑖 is the basic function number on finite element 𝑖 ∈ [0, 𝑁 𝑜𝐹 [
• 𝑗 is the value of component 𝑗 ∈ [0, 𝑁 [
• 𝑘 is the type of computed value 𝑓 (𝑃 ), 𝑑𝑥(𝑓 )(𝑃 ), 𝑑𝑦(𝑓 )(𝑃 ), ... 𝑖 ∈ [0, last_operatortype[.
Note
For optimization, this value is computed only if whatd[k] is true, and the numbering is defined with
1 enum operatortype {
2 op_id = 0,
3 op_dx = 1, op_dy = 2,
4 op_dxx = 3,op_dyy = 4,
5 op_dyx = 5,op_dxy = 5,
6 op_dz = 6,
7 op_dzz = 7,
8 op_dzx = 8, op_dxz = 8,
9 op_dzy = 9, op_dyz = 9
10 };
11 const int last_operatortype = 10;
1 void TypeOfFE_RTortho::FB(const bool *whatd, const Mesh &Th, const Triangle & K,
2 const R2 &PHat,RNMK_ &val) const
3 {
4 R2 P(K(PHat));
5 R2 A(K[0]), B(K[1]), C(K[2]);
6 R l0 = 1 - P.x-P.y;
7 R l1 = P.x, l2 = P.y;
8 assert(val.N() >= 3);
9 assert(val.M() == 2);
10 val = 0;
11 R a = 1./(2*K.area);
12 R a0 = K.EdgeOrientation(0) * a;
13 R a1 = K.EdgeOrientation(1) * a;
14 R a2 = K.EdgeOrientation(2) * a;
15
Try with ./load.link command in examples++-load/ and see BernardiRaugel.cpp or Morley.cpp new finite
element examples.
Otherwise with static link (for expert only), add
To inforce in loading of this new finite element, we have to add the two new lines close to the end of files src/femlib/
FESpace.cpp like:
1 autoreconf
2 ./reconfigure
3 make
For codewarrior compilation add the file in the project an remove the flag in panal PPC linker FreeFm++ Setting
Dead-strip Static Initializition Code Flag.
Note
In the sub directory include, they are all the FreeFEM include file to make the link with FreeFEM.
Note
If you try to load dynamically a file with command load "xxx" - Under Unix (Linux or MacOs), the file xxx.
so will be loaded so it must be either in the search directory of routine dlopen (see the environment variable
$LD_LIBRARY_PATH) or in the current directory, and the suffix ".so" or the prefix "./" is automatically added.
• Under Windows, the file xxx.dll will be loaded so it must be in the loadLibary search directory which includes
the directory of the application,
1 #include <iostream>
2 #include <cfloat>
3 using namespace std;
4 #include "error.hpp"
5 #include "AFunction.hpp"
6 #include "rgraph.hpp"
7 #include "RNM.hpp"
8 #include "fem.hpp"
9 #include "FESpace.hpp"
10 #include "MeshPoint.hpp"
11
Now the Problem is to build the link with FreeFEM, to do that we need two classes, one to call the function
myfunction.
All FreeFEM evaluable expression must be a C++ struct/class which derivate from E_F0. By default this expression
does not depend of the mesh position, but if they derivate from E_F0mps the expression depends of the mesh position,
and for more details see [HECHT2002].
To finish we must add this new function in FreeFEM table, to do that include :
1 void init(){
2 Global.Add("myfunction", "(", new OneOperator0s<double>(myfunction));
3 }
4 LOADFUNC(init);
Under Windows, launch FreeFEM with the mouse (or ctrl O) on the example.
𝑗 ′ =0 𝑗=0
Note
The value 𝑛 is given by 𝑠𝑖𝑧𝑒(𝑓 )/𝑚, and the numbering is row-major order.
√ √
So the classical discrete DFFT is 𝑓ˆ = dfft(𝑓, −1)/ 𝑛 and the reverse dFFT 𝑓 = dfft(𝑓ˆ, 1)/ 𝑛
Note
√ 𝑚−1
∑︁ 𝑛−1
∑︁ ′
𝑓 (𝑥, 𝑦) = 1/ 𝑁 𝑓ˆ𝑖+𝑛𝑗 𝑒𝜀2𝜋𝑖(𝑥𝑗+𝑦𝑗 )
𝑗 ′ =0 𝑗=0
and we have
𝑓𝑘+𝑛𝑙 = 𝑓 (𝑘/𝑛, 𝑙/𝑚)
So
2 ˜ 2 ˜ 2 ̂︁
𝑘𝑙 = −((2𝜋) ((𝑘) + (𝑙) ))𝑓𝑘𝑙
[
∆𝑓
The Bernardi-Raugel finite element is meant to solve the Navier Stokes equations in 𝑢, 𝑝 formulation; the velocity space
𝑏𝑟
𝑃𝐾 is minimal to prove the inf-sup condition with piecewise constant pressure by triangle.
The finite element space 𝑉ℎ is
𝑉ℎ = {𝑢 ∈ 𝐻 1 (Ω)2 ; 𝑏𝑟
∀𝐾 ∈ 𝑇ℎ , 𝑢|𝐾 ∈ 𝑃𝐾 }
where
𝑏𝑟
𝑃𝐾 = 𝑠𝑝𝑎𝑛{𝜆𝐾 𝐾 𝐾 𝐾
𝑖 𝑒𝑘 }𝑖=1,2,3,𝑘=1,2 ∪ {𝜆𝑖 𝜆𝑖+1 𝑛𝑖+2 }𝑖=1,2,3
with notation 4 = 1, 5 = 2 and where 𝜆𝐾 𝑖 are the barycentric coordinates of the triangle 𝐾, (𝑒𝑘 )𝑘=1,2 the canonical
basis of R2 and 𝑛𝐾
𝑘 the outer normal of triangle 𝐾 opposite to vertex 𝑘.
See BernardiRaugel.cpp.
A way to check the finite element
1 load "BernardiRaugel"
2
3 // Macro
4 //a macro the compute numerical derivative
5 macro DD(f, hx, hy) ( (f(x1+hx, y1+hy) - f(x1-hx, y1-hy))/(2*(hx+hy)) ) //
6
7 // Mesh
(continues on next page)
10 // Parameters
11 real x1 = 0.7, y1 = 0.9, h = 1e-7;
12 int it1 = Th(x1, y1).nuTriangle;
13
14 // Fespace
15 fespace Vh(Th, P2BR);
16 Vh [a1, a2], [b1, b2], [c1, c2];
17
18
A real example using this finite element, just a small modification of the Navier-Stokes P2-P1 example, just the begen-
ning is change to
1 load "BernardiRaugel"
2
3 real s0 = clock();
4 mesh Th = square(10, 10);
5 fespace Vh2(Th, P2BR);
6 fespace Vh(Th, P0);
7 Vh2 [u1, u2], [up1, up2];
8 Vh2 [v1, v2];
And the plot instruction is also changed because the pressure is constant, and we cannot plot isovalues of peacewise
constant functions.
3.12 ffddm
In the acronym ffddm, ff stands for FreeFEM and ddm for domain decomposition methods. The idea behind ffddm is
to simplify the use of parallel solvers in FreeFEM: distributed direct methods and domain decomposition methods.
Parallelism is an important issue because, since about 2004, the clock speed of cores stagnates at 2-3 GHz. The increase
in performance is almost entirely due to the increase in the number of cores per processor. All major processor vendors
are producing multicore chips and now every machine is a parallel machine. Waiting for the next generation machine
does not guarantee anymore a better performance of a software. To keep doubling performance parallelism must double.
It implies a huge effort in algorithmic development.
Thanks to ffddm, FreeFEM users have access to high-level functionalities for specifying and solving their finite element
problems in parallel. The first task handled by ffddm is the data distribution among the processors. This is done via an
overlapping domain decomposition and a related distributed linear algebra. Then, solving a linear system is possible
either via an interface to the parallel MUMPS solver or by using domain decomposition methods as preconditioners
to the GMRES Krylov method. The ffddm framework makes it easy to use scalable Schwarz methods enhanced
by a coarse space correction built either from a coarse mesh or a GenEO (Generalized Eigenvalue in the Overlap)
coarse space, see also the book An Introduction to Domain Decomposition Methods: algorithms, theory, and parallel
implementation. State-of-the-art three level methods are also implemented in ffddm.
The ffddm framework is entirely written in the FreeFEM language and the ‘idp’ scripts can be found here (‘ffddm*.idp’
files). It makes it also a very good tool for learning and prototyping domain decomposition methods without compro-
mising efficiency.
ffddm can also act as a wrapper for the HPDDM library. HPDDM is an efficient implementation of various domain
decomposition methods and a variety of Krylov subspace algorithms, with advanced block and recycling methods for
solving sequences of linear systems with multiple right-hand sides: GMRES and Block GMRES, CG, Block CG, and
Breakdown-Free Block CG, GCRO-DR and Block GCRO-DR. For more details on how to use HPDDM within ffddm,
see the ffddm documentation.
Getting Started
This example solves a Laplace problem in 2D in parallel with a two-level GenEO domain decomposition method. To
try this example, just copy and paste the script above in a file ‘test.edp’ and run it on 2 cores with
Citing ffddm
When citing fdddm in a publication, please cite the following:
@misc{FFD:Tournier:2019,
author = {Tournier, Pierre-Henri and Jolivet, Pierre and Nataf, Fr{\'e}d{\'e}ric},
howpublished = {{https://doc.freefem.org/documentation/ffddm/index.html}},
title = {{FFDDM}: FreeFem Domain Decomposition Method},
year = {2019}}
Mesh Decomposition
The starting point is a collection of 𝑁 sub-meshes (𝑇 ℎ𝑖 )𝑁
𝑖=1 that together form a global mesh
𝑇 ℎ := ∪𝑁
𝑖=1 𝑇 ℎ𝑖 .
These meshes may be overlapping or not. This decomposition induces a natural decomposition of the global finite
element space 𝑉 ℎ on 𝑇 ℎ into 𝑁 local finite element spaces (𝑉 ℎ𝑖 )𝑁
𝑖=1 each of them defined on 𝑇 ℎ𝑖 .
Note By global, we mean that the corresponding structure can be refered to in the code (most often only) by its local
values. In computer science term, it corresponds to a distributed data where each piece of data is stored by a MPI
process.
𝒩 = ∪𝑁
𝑖=1 𝒩𝑖 ,
Note
In mathematical terms, it can be described as follows for a real valued problem. Let 𝑅𝑖 be the restriction operator
from R#𝒩 to R#𝒩𝑖 , where #𝒩𝑖 denotes the number of elements of 𝒩𝑖 . A collection of local vectors (V𝑖 )1≤𝑖≤𝑁 ∈
Π𝑁𝑖=1 R
#𝒩𝑖
is a distributed vector iff there exists a global vector V ∈ R#𝒩 such that for all subset 1 ≤ 𝑖 ≤ 𝑁 , we
have:
V𝑖 = 𝑅𝑖 V .
We will also say that the collection of local vectors (V𝑖 )1≤𝑖≤𝑁 is consistent. For a complex valued problem, simply
replace R with C.
For instance if a degree of freedom is shared by 𝑘 subdomains defining the corresponding entry of the diagonal matrix
𝐷 to be 1/𝑘 yields partition of unity matrices. The matrices 𝑅𝑖 and 𝐷𝑖 are the heart of distributed linear algebra.
For two global vectors U and V of size #𝒩 , the formula for the scalar product V𝑇 U = (U, V) in terms of their
distributed vector counterparts makes use of the partition of unity matrices (𝐷𝑖 )1≤𝑖≤𝑁 introduced above:
(︃ 𝑁
)︃ 𝑁 𝑁
∑︁ ∑︁ ∑︁
𝑇
(U, V) = U, 𝑅𝑖 𝐷𝑖 𝑅𝑖 V = (𝑅𝑖 U, 𝐷𝑖 𝑅𝑖 V) = (U𝑖 , 𝐷𝑖 V𝑖 ) .
𝑖=1 𝑖=1 𝑖=1
Local scalar products are performed concurrently. Thus, the implementation is parallel except for the sum which corre-
sponds to a MPI_Reduce call across the 𝑁 MPI processes. Note also that the implementation relies on the knowledge of
a partition of unity so that the FreeFEM syntax is dscalprod(Di,u,v) or equivalently myFEprefix#scalprod(u,v)
where myFEprefix is a user defined prefix for the finite element space decomposition, see the ffddm documentation.
Update
From a collection of local vectors (U𝑖 )1≤𝑖≤𝑁 , it is possible ensure consistency of the duplicated data by modifying
the distributed vector (U𝑖 )1≤𝑖≤𝑁 by calling the function myFEprefix#update(Ui, true) where myFEprefix is
the user defined prefix that refers to the finite element space decomposition. This function performs the following
operation for all 1 ≤ 𝑖 ≤ 𝑁 :
𝑁
∑︁
U𝑖 ← 𝑅𝑖 𝑅𝑗𝑇 𝐷𝑗 U𝑗
𝑗=1
Note
where 𝒪(𝑖) is the set of neighbors of subdomain 𝑖. Therefore, the matrix vector product is computed in three steps:
𝐴 X = RHS .
ffddm offers two parallel solvers: direct factorization and iterative preconditioned solvers via Schwarz domain decom-
position methods.
In order to benefit from the sparsity of the matrix arising from a finite element discretization of a partial differential
equation, a variant of Gauss elimination, the frontal method, that automatically avoids a large number of operations
involving zero terms was developed. A frontal solver builds a 𝐿𝑈 or Cholesky decomposition of a sparse matrix given
as the assembly of element matrices by eliminating equations only on a subset of elements at a time. This subset is
called the front and it is essentially the transition region between the part of the system already finished and the part
not touched yet. These methods are basically sequential since the unknowns are processed the one after another or one
front after another. In order to benefit from multicore processors, a multifrontal solver is an improvement of the frontal
solver that uses several independent fronts at the same time. The fronts can be worked on by different processors, which
enables parallel computing. ffddm provides an interface to the parallel sparse direct solver MUMPS. These methods
have the advantage to be very robust and to have a predictable cost. The main drawback is the memory requirement
which can be prohibitive especially for three-dimensional problems.
Schwarz methods
These methods are part of the large family of preconditioned iterative solvers. When considering the solve of the
equation 𝐴 X = RHS, a preconditioner is a linear operator that approximates the inverse of 𝐴 and whose cost of the
associated matrix-vector product is much cheaper than solving the original linear system. It enables to accelerate the
solution of the latter with Krylov type methods such as the conjugate gradient (in the symmetric positive definite case),
GMRES or BiCGSTAB in the general case. Two options are possible.
Left preconditioning: the preconditioner is applied to the left of the equation
𝑀 −1 𝐴 X = 𝑀 −1 RHS .
and the Krylov method is applied to the left preconditioned system with a residual that is preconditioner dependent.
𝐴 𝑀 −1 Y = RHS where X = 𝑀 −1 Y.
and the Krylov method is applied to the right preconditioned system with a residual that is preconditioner independent.
In both cases, if the preconditioner is efficient the number of iterations to get a converged solution is much smaller
than the number of iterations of the Krylov method applied to the original equation 𝐴 X = RHS. Although right
preconditioning seems more intricate, it is much safer to use since the convergence is checked on a residual that does
not depend on the preconditioner.
In the sequel, we consider the solution of the equation 𝐴 X = RHS preconditioned by domain decomposition methods
and with a flexible GMRES Krylov method which is thus necessarily right preconditioned.
where for each subdomain 𝑗 the restriction matrix 𝑅𝑗 and the partition of unity matrix 𝐷𝑗 have been introduced above.
Note that in the original ASM (additive Schwarz method) preconditioner the partition of unity is dropped. The ap-
−1
plication of the operator 𝑀𝑅𝐴𝑆 to a global right hand side RHS is detailed below. Recall that this global vector is
distributed among processes via the local vectors (RHS𝑖 )𝑁 𝑖=1 . Let 𝐴𝑗 denote the local matrix (𝑅𝑗 𝐴 𝑅𝑗 ). The local
𝑇
−1
vector in subdomain 𝑖 resulting from the matrix vector product 𝑀𝑅𝐴𝑆 RHS consists in computing:
𝑁
∑︁ ∑︁
𝑅𝑖 𝑅𝑗𝑇 𝐷𝑗 𝐴−1
𝑗 RHS𝑗 = 𝐷𝑖 𝐴−1
𝑖 RHS𝑖 + (𝑅𝑖 𝑅𝑗𝑇 ) 𝐷𝑗 𝐴−1
𝑗 RHS𝑗 .
𝑗=1 𝑗∈𝒪(𝑖)
This task is performed by first solving concurrently on all subdomains a linear system for Y𝑗 for all 1 ≤ 𝑗 ≤ 𝑁 :
𝐴𝑗 Y𝑗 = RHS𝑗 .
Each local vector Y𝑗 is weighted by the partition of unity matrix 𝐷𝑗 . Then data transfers between neighboring sub-
domains implement the 𝑅𝑖 𝑅𝑗𝑇 𝐷𝑗 Y𝑗 formula. The contribution from neighboring subdomains are summed locally.
This pattern is very similar to that of the update procedure.
The ORAS preconditioner may be seen as a variant of the RAS preconditioner. It reads:
𝑁
∑︁
−1
𝑀𝑅𝐴𝑆 := 𝑅𝑗𝑇 𝐷𝑗 𝐵𝑗−1 𝑅𝑗
𝑗=1
where 𝐵𝑗 are local matrices of size #𝒩𝑗 × #𝒩𝑗 for 1 ≤ 𝑗 ≤ 𝑁 . This variant is very useful when dealing with
wave propagation phenomena such as Helmholtz problems in acoustics or Maxwell system in the frequency domain
for electromagnetism. Defining 𝐵𝑗 as the discretization of the physical equation with impedance conditions on the
boundary of the subdomain has been proved to be a good choice.
The RAS and ORAS methods are called a one-level method in the sense that sub-domains only interact with their
direct neighbors. For some problems such as Darcy problems or static elasticity problems and when the number of
subdomains is large, such one-level methods may suffer from a slow convergence. The fix is to add to the preconditioner
an auxiliary coarse problem that couples all subdomains at each iteration and is inexpensive to calculate.
In mathematical terms, we first choose a full rank rectangular matrix 𝑍 ∈ R#𝒩 ×𝑁 𝐶 where 𝑁 𝐶 ≪ #𝒩 denotes the
dimension of the coarse space spanned by the columns of 𝑍. We also pick a coarse matrix 𝐴𝐶 ∈ R𝑁𝐶 ×𝑁𝐶 . A generic
one-level method preconditioner 𝑀1−1 is enriched by a solve on the coarse space. The simplest correction formula is
additive:
𝑀2−1 := 𝑍 𝐴−1 𝑇 −1
𝐶 𝑍 + 𝑀1
Coarse Mesh
A first possibility is to discretize the problem on a coarse mesh, following the same principle as multi-grid methods.
For 3-D problems, a coarsening of the mesh size by a factor 2, reduces by a factor 23 = 8 the size of the coarse problem
which is then easier to solve by a direct method. Then, 𝑍 is the interpolation matrix from the coarse finite element
space to the fine one.
GenEO
For highly heterogeneous or anisotropic problems, two level methods based on coarse meshes might fail and a more
sophisticated construction must be used. A provable robust coarse space called GenEO is built by first solving the
following local generalized eigenvalue problem in parallel for each subdomain 1 ≤ 𝑖 ≤ 𝑁 , where 𝐴Neu
𝑖 denotes the
local matrix resulting from the variational formulation:
The eigenvectors selected to enter the coarse space correspond to eigenvalues 𝜆𝑖,𝑘 ≥ 𝜏 , where the threshold parameter
𝜏 is user-defined. The precise formulas are given in this documentation. From a mathematical point of view, it has been
proved that for a symmetric positive definite matrix 𝐴, the spectrum of the preconditioned by the two-level method with
1
a GenEO coarse space lies in the interval [ , 𝑘0 ].
1 + 𝑘1 𝜏
Note A heuristic that justifies this construction is as follows. We first introduce the Additive Schwarz method (ASM)
which can be seen as a symmetrized variant of the RAS preconditioner:
𝑁
∑︁
−1
𝑀𝐴𝑆𝑀 := 𝑅𝑗𝑇 𝐴−1
𝑗 𝑅𝑗 .
𝑗=1
−1
It can be proved that the lower bound for the eigenvalue of 𝑀𝐴𝑆𝑀 𝐴 is close to zero (which is bad for convergence)
whereas the upper bound depends only on the number of neigbors of a subdomain (which is good for convergence).
−1
Second, we also introduce the following preconditioner 𝑀𝑁 𝑁:
𝐷𝑖 (𝐴Neu
∑︁
−1 −1
𝑀𝑁 𝑁 := 𝑗 ) 𝐷𝑗 .
1≤𝑗≤𝑁
−1
We have a very good lower bound for the preconditioned operator 𝑀𝑁 𝑁 𝐴 that does not depend on the number of
subdomains but only on the maximum multiplicity of intersections 𝑘1 (which is good for convergence). But the upper
bound for this preconditioner is very large (which is bad for convergence).
−1 −1 Neu −1
Now, if we compare formulas for 𝑀𝑁 𝑁 and 𝑀𝐴𝑆𝑀 , we may suspect that vectors V𝑖𝑘 for which 𝐷𝑖 (𝐴𝑖 ) 𝐷𝑖 V𝑖𝑘
−1
and 𝐴𝑖 V𝑖𝑘 have very different values are responsible for the slow convergence and should contribute to the coarse
space. This is a way to interpret the above generalized eigenvalue problem which controls the lower bound of the
two-level preconditioned system.
3 include "ffddm.idp"
4
5 load "msh3"
6
14 // EOM
15
16 // Domain decomposition
17 ffddmbuildDmesh( LapMesh , ThGlobal , mpiCommWorld )
18
25 real[int] rhsi(0);
26 ffddmbuildrhs( Lap , Varf , rhsi )
27
28 LapFEVhi def(ui);
29
30 //Direct solve
31 ui[] = Lapdirectsolve(rhsi);
32
33 Lapwritesummary
34
35 ffddmplot(LapFE,ui,"u");
1 ffddmbuildDmesh(prmesh,Th,comm)
decomposes the mesh Th into overlapping submeshes. The mesh will be distributed over the mpi ranks of commu-
nicator comm. This will create and expose variables whose names will be prefixed by prmesh, see below (# is the
concatenation operator). The way the initial mesh Th is partitioned depends on the value of ffddmpartitioner.
The size of the overlap between subdomains (its width in terms of number of mesh elements) is given by ffddmoverlap.
The level of refinement of the resulting submeshes with respect to the input mesh Th is given by ffddmsplit.
If ffddmexclude ̸= 0, the first ffddmpCS mpi ranks of comm will be excluded from the spatial domain decomposition,
in order to dedicate them later to the coarse problem (for two-level preconditioners).
The label of the new border of the submeshes (the interface between the subdomains) is given by ffddminterfacelabel.
defines:
• int prmesh#npart number of subdomains for this decomposition; should be equal to mpiSize(comm) - ffd-
dmexclude * ffddmpCS
• int prmesh#pCS equal to ffddmpCS
• int prmesh#exclude equal to ffddmexclude
• int prmesh#excluded true if ffddmexclude is true (̸= 0) and mpiRank(comm) < prmesh#pCS. In this case,
this mpi rank will be excluded from the spatial domain decomposition and will only work on the coarse problem.
• mpiComm prmesh#commddm mpi communicator for ranks participating in the spatial domain decompo-
sition (ranks 0 to prmesh#npart - 1 in comm if prmesh#exclude is false, ranks prmesh#pCS to
prmesh#pCS+prmesh#npart - 1 otherwise)
• mpiComm prmesh#commCS mpi communicator for ranks participating in the assembly and resolution of the
coarse problem for two-level preconditioners (ranks 0 to prmesh#pCS - 1 in comm)
• mpiComm prmesh#commself self mpi communicator (this mpi rank only), used for factorizing local matrices
• meshN[int] prmesh#aTh array (size prmesh#npart) of local meshes of the subdomains. In the standard par-
allel case, only the local mesh for this mpi rank prmesh#aTh[mpiRank(prmesh#commddm)] is defined (unless
this mpi rank is excluded from the spatial domain decomposition, i.e. prmesh#excluded = 1, see below). In
the sequential case, all local meshes are defined.
• meshN prmesh#Thi the local mesh of the subdomain for this mpi rank, i. e.
prmesh#aTh[mpiRank(prmesh#commddm)] in the parallel case
• int prmesh#numberIntersection the number of neighbors for this mpi rank
• int[int] prmesh#arrayIntersection the list of neighbor ranks in prmesh#commddm for this mpi rank
Remark for sequential use (see -seqddm):
• meshN[int] prmesh#aTh array (size prmesh#npart) of local meshes of the subdomains
1 ffddmbuildDfespace(prfe,prmesh,scalar,def,init,Pk)
builds the local finite element spaces and associated distributed operators on top of the mesh decomposition prmesh.
This will create and expose variables whose names will be prefixed by prfe, see below. It is assumed that ffddmbuild-
Dmesh has already been called with prefix prmesh in order to build the mesh decomposition.
The local finite element spaces of type Pk (where Pk is the type of finite element: P1, [P2,P2,P1], . . . ) are defined on
the local meshes of the subdomains based on the mesh decomposition previously created with prefix prmesh.
scalar determines the type of data for this finite element: real or complex.
Two macros, def and init, are needed: def specifies how to define a finite element function in the finite element space
Pk, and init specifies how to interpolate a scalar function onto the (possibly multiple) components of Pk. Two examples
are given below:
For scalar P2 finite elements and complex-valued problems:
In practice, this builds the necessary distributed operators associated to the finite element space: the local partition of
unity functions (𝐷𝑖 )𝑖=1,...,𝑁 (see prfe#Dk and prfe#Dih below) as well as the function prfe#update (see below)
which synchronizes local vectors (𝑢𝑖 )𝑖=1,...,𝑁 between neighboring subdomains, performing the equivalent of 𝑢𝑖 =
∑︀𝑁 ∑︀𝑁
𝑅𝑖 ( 𝑗=1 𝑅𝑗𝑇 𝑢𝑗 ) or 𝑢𝑖 = 𝑅𝑖 ( 𝑗=1 𝑅𝑗𝑇 𝐷𝑗 𝑢𝑗 ) in a distributed parallel environment.
prfe#scalprod (see below) performs the parallel scalar product for vectors defined on this finite element.
defines:
• prfe#prmesh macro, saves the parent prefix prmesh of the mesh decomposition
• prfe#K macro, saves the type of data scalar for this finite element space (real or complex)
• func prfe#fPk saves the type of finite element Pk, e.g. P1, [P2,P2,P1], . . .
• fespace prfe#Vhi the local finite element space for this mpi rank, defined on the local mesh prmesh#Thi
• int prfe#Ndofglobal the total number of degrees of freedom 𝑛 for this finite element discretization
• prfe#mdef macro, saves the macro def giving the definition of a finite element function in the finite element
space Pk
• prfe#minit macro, saves the macro init specifying how to interpolate a scalar function onto the (possibly
multiple) components of a finite element function of Pk. This is used to create the local partition of unity function
in prfe#Vhi, by interpolating the local P1 partition of unity function onto the components of prfe#Vhi. For
non Lagrange finite element spaces (e.g. RT0, Edge03d, . . . ), see ffddmbuildDfespaceEdge.
• prfe#K[int][int] prfe#Dk array (size prmesh#npart) of local partition of unity vectors in the subdomains,
equivalent to (𝐷𝑖 )𝑖=1,...,𝑁 . In the standard parallel case, only the local partition of unity vector for this mpi rank
prfe#Dk[mpiRank(prmesh#commddm)] is defined (unless this mpi rank is excluded from the spatial domain
decomposition, i. e. prmesh#excluded = 1). In the sequential case, all local partition of unity vectors are
defined.
• matrix<prfe#K>[int] prfe#Dih array (size prmesh#npart) similar to prfe#Dk but in matrix form, allow-
ing for easier matrix-matrix multiplications. prfe#Dih[i] is a diagonal matrix, with the diagonal equal to
prfe#Dk[i].
• fespace prfe#Vhglob the global finite element space defined on the global mesh prmesh#Thglob. Defined
only if -noGlob is not used.
• matrix<prfe#K>[int] prfe#Rih array (size prmesh#npart) of restriction matrices from the global finite
element space to the local finite element spaces on the local submeshes of the subdomains. In the standard
parallel case, only the restriction matrix for this mpi rank prfe#Rih[mpiRank(prmesh#commddm)] is defined
(unless this mpi rank is excluded from the spatial domain decomposition, i. e. prmesh#excluded = 1). In
the sequential case, all restriction matrices are defined. The restriction matrices prfe#Rih are defined only if
-noGlob is not used.
• func int prfe#update(scalar[int] ui, bool scale) The function prfe#update synchronizes the lo-
cal vector ui between subdomains by exchanging the values of ui shared with neighboring subdomains (in the
overlap region) using point-to-point MPI communications.
∑︀𝑁 If scale is true, ui is multiplied by the∑︀
local partition of
𝑁
unity beforehand. This is equivalent to 𝑢𝑖 = 𝑅𝑖 ( 𝑗=1 𝑅𝑗𝑇 𝑢𝑗 ) when scale is false and 𝑢𝑖 = 𝑅𝑖 ( 𝑗=1 𝑅𝑗𝑇 𝐷𝑗 𝑢𝑗 )
when scale is true.
• func scalar prfe#scalprod(scalar[int] ai, scalar[int] bi) The function prfe#scalprod com-
putes the global scalar product of∑︀
two vectors whose local restriction to the subdomain of this mpi rank are ai
𝑁
and bi. The result is computed as 𝑗=1 (𝐷𝑗 𝑎𝑗 , 𝑏𝑗 ).
1 ffddmsetupOperator(pr,prfe,Varf)
builds the distributed operator associated to the variational problem given by Varf, on top of the distributed finite
element space prfe. This will create and expose variables whose names will be prefixed by pr, see below. It is assumed
that ffddmbuildDfespace has already been called with prefix prfe in order to define the distributed finite element space.
In practice, this builds the so-called local ‘Dirichlet’ matrices 𝐴𝑖 = 𝑅𝑖 𝐴𝑅𝑖𝑇 , the restrictions of the global operator 𝐴
to the subdomains (see pr#aRdbelow). The matrices correspond to the discretization of the bilinear form given by the
macro Varf, which represents the abstract variational form of the problem. These matrices are then used to implement
the action of the global operator 𝐴 on a local vector (the parallel matrix-vector product with 𝐴), see pr#A below.
At this point, we already have the necessary data to be able to solve the problem with a parallel direct solver (MUMPS),
which is the purpose of the function pr#directsolve (see below). See ffddmbuildrhs for building the right-hand side.
The macro Varf is required to have three parameters: the name of the variational form, the mesh, and the finite element
space. The variational form given in this ‘abstract’ format will then be used by ffddm to assemble the discrete operators
by setting the appropriate mesh and finite element space as parameters. An example is given below:
5 ffddmsetupOperator(myprefix,myFEprefix,myVarf)
Remark In this simple example, the third parameter VhName is not used. However, for more complex cases such as
non-linear or time dependent problems where the problem depends on a solution computed at a previous step, it is
useful to know for which discrete finite element space the variational form is being used. See for example TODO
defines:
• pr#prfe macro, saves the parent prefix prfe of the finite element space
• int pr#verbosity the level of verbosity for this problem, initialized with the value of ffddmverbosity
• pr#writesummary macro, prints a summary of timings for this problem, such as the time spent to assemble
local matrices or solve the linear system.
• matrix<prfe#K> pr#Aglobal the global matrix 𝐴 corresponding to the discretization of the variational form
given by the macro Varf on the global finite element space prfe#Vhglob. Defined only in the sequential case.
• matrix<prfe#K>[int] pr#aRd array (size prfe#prmesh#npart) of so-called local ‘Dirichlet’ matrices in
the subdomains; these are the restrictions of the global operator to the subdomains, equivalent to 𝐴𝑖 = 𝑅𝑖 𝐴𝑅𝑖𝑇
with 𝐴 the global matrix corresponding to the discretization of the variational form given by the macro Varf
on the global finite element space. In the standard parallel case, only the local matrix for this mpi rank
pr#aRd[mpiRank(prmesh#commddm)] is defined (unless this mpi rank is excluded from the spatial domain
decomposition, i. e. prmesh#excluded = 1). In the sequential case, all local matrices are defined.
• func prfe#K[int] pr#A(prfe#K[int] &ui) The function pr#A computes the parallel matrix-vector prod-
uct,∑︀
i.e. the action of the global operator 𝐴 on the local vector 𝑢𝑖 . The computation is equivalent to
𝑁
𝑅𝑖 ( 𝑗=1 𝑅𝑗𝑇 𝐷𝑗 𝐴𝑗 𝑢𝑗 ) and is performed in parallel using local matrices pr#aRd and the function prfe#update.
In the sequential case, the global matrix pr#Aglobal is used instead.
• func prfe#K[int] pr#AT(prfe#K[int] &ui) Similarly to pr#A, The function pr#AT computes the action
of 𝐴𝑇 , the transpose of the global operator 𝐴, on 𝑢𝑖 .
• func prfe#K[int] pr#directsolve(prfe#K[int]& rhsi) The function pr#directsolve allows to
solve the linear system 𝐴𝑥 = 𝑏 in parallel using the parallel direct solver MUMPS. The matrix is given to
MUMPS in distributed form through the local matrices pr#aRd. The input rhsi is given as a distributed vector
(rhsi is the restriction of the global right-hand side 𝑏 to the subdomain of this mpi rank, see ffddmbuildrhs) and
the returned vector is local as well.
Remark: rectangular operators
It is possible to define a non-square distributed operator where the variational form takes two different finite element
spaces of unknown and test functions. This is done through macro ffddmsetupOperatorRect which takes two FE
prefixes (which must be defined on the same mesh prefix), see below:
5 ffddmsetupOperatorRect(myprefix,myFEprefixV,myFEprefixP,myVarf)
1 ffddmbuildrhs(pr,Varfrhs,rhs)
builds the right-hand side associated to the variational form given by Varfrhs for the problem corresponding to prefix
pr. The resulting right-hand side vector rhs corresponds to the discretization of the abstract linear form given by the
macro Varfrhs (see ffddmsetupOperator for more details on how to define the abstract variational form as a macro).
The input vector rhs is resized and contains the resulting local right-hand side 𝑅𝑖 𝑏, the restriction of the global right-
hand side 𝑏 to the subdomain of this mpi rank. In the sequential case, the global right-hand side vector 𝑏 is assembled
instead.
An example is given below:
5 real[int] rhsi(0);
6 ffddmbuildrhs(myprefix,myVarfrhs,rhsi)
1 ffddmsetupPrecond(pr,VarfPrec)
builds the one level preconditioner for problem pr. This will create and expose variables whose names will be prefixed
by pr, see below. It is assumed that ffddmsetupOperator has already been called with prefix pr in order to define the
problem to solve.
In practice, this builds and performs the factorization of the local matrices used in the one level preconditioner. The
local matrices depend on the choice of ffddmprecond and VarfPrec, see pr#aRbelow.
defines:
• string pr#prec equal to ffddmprecond. Sets the type of one level preconditioner 𝑀1−1 to be used: “asm”
(Additive Schwarz), “ras” (Restricted Additive Schwarz), “oras” (Optimized Restricted Additive Schwarz), “soras”
(Symmetric Optimized Restricted Additive Schwarz) or “none” (no preconditioner).
• matrix<pr#prfe#K>[int] pr#aR array (size prfe#prmesh#npart) of local matrices used for the one level
preconditioner. Each mpi rank of the spatial domain decomposition performs the 𝐿𝑈 (or 𝐿𝐷𝐿𝑇 ) factorization
of the local matrix corresponding to its subdomain using the direct solver MUMPS.
– If VarfPrec is not a previously defined macro (just put null for example), the matrices pr#aR are set
to be equal to the so-called local ‘Dirichlet’ matrices pr#aRd (see ffddmsetupOperator). This is for the
∑︀𝑁
classical ASM preconditioner 𝑀1−1 = 𝑀ASM
−1
= 𝑖=1 𝑅𝑖𝑇 𝐴−1 −1
𝑖 𝑅𝑖 or classical RAS preconditioner 𝑀1 =
−1 ∑︀𝑁 −1
𝑀RAS = 𝑖=1 𝑅𝑖𝑇 𝐷𝑖 𝐴𝑖 𝑅𝑖 (it is assumed that ffddmprecond is equal to “asm” or “ras”).
– If VarfPrec is a macro, it is assumed that VarfPrec defines an abstract bilinear form (see ffddmsetupOp-
erator for more details on how to define the abstract variational form as a macro).
∗ If ffddmprecond is equal to “asm” or “ras”, the matrices pr#aR will be assembled as local ‘Dirichlet’
matrices in the same manner as pr#aRd, but using the bilinear form defined by VarfPrec instead. This
−1
= 𝑖=1 𝑅𝑖𝑇 (𝐴Prec
∑︀𝑁
defines the ASM preconditioner as 𝑀1−1 = 𝑀ASM −1
𝑖 ) 𝑅𝑖 and the RAS precondi-
−1
= 𝑖=1 𝑅𝑖𝑇 𝐷𝑖 (𝐴Prec Prec Prec 𝑇
𝑁
tioner as 𝑀1−1 = 𝑀RAS−1
, where 𝑅𝑖 .
∑︀
𝑖 ) 𝑅 𝑖 𝐴 𝑖 = 𝑅 𝑖 𝐴
∗ If ffddmprecond is equal to “oras” or “soras”, the matrices pr#aR will correspond to the discretization
of the variational form VarfPrec in the subdomains Ω𝑖 . In particular, various boundary conditions can
be imposed at the interface between subdomains (corresponding to mesh boundary of label ffddminter-
facelabel set by the parent call to ffddmbuildDmesh), such as Optimized Robin boundary conditions.
−1
= 𝑖=1 𝑅𝑖𝑇 𝐷𝑖 (𝐵𝑖Prec ) 𝑅𝑖 and the SORAS
∑︀𝑁
We note the ORAS preconditioner as 𝑀1−1 = 𝑀ORAS −1
−1
= 𝑖=1 𝑅𝑖𝑇 𝐷𝑖 (𝐵𝑖Prec ) 𝐷𝑖 𝑅𝑖 .
𝑁
preconditioner as 𝑀1−1 = 𝑀SORAS
−1 ∑︀
• func pr#prfe#K[int] pr#PREC1(pr#prfe#K[int] &ui) The function pr#PREC1 computes the parallel
application of the one level preconditioner 𝑀1−1 , i.e. the action of 𝑀1−1 on the local vector 𝑢𝑖 . In the sequential
case, it computes the action of 𝑀1−1 on a global vector. The action of the inverse of local matrices pr#aRd is
computed by forward-backward substitution using their 𝐿𝑈 (or 𝐿𝐷𝐿𝑇 ) decomposition.
• func pr#prfe#K[int] pr#PREC(pr#prfe#K[int] &ui) The function pr#PREC corresponds to the action
of the preconditioner 𝑀 −1 for problem pr. It coincides with the one level preconditioner pr#PREC1 after the
call to ffddmsetupPrecond. If a second level is subsequently added (see the next section about Two level precon-
ditioners), it will then coincide with the two level preconditioner 𝑀2−1 (see pr#PREC2level).
• func pr#prfe#K[int] pr#fGMRES(pr#prfe#K[int]& x0i, pr#prfe#K[int]& bi, real eps, int
nbiter, string sprec) The function pr#fGMRES allows to solve the linear system 𝐴𝑥 = 𝑏 in parallel using
the flexible GMRES method preconditioned by 𝑀 −1 . The action of the global operator 𝐴 is given by pr#A, the
action of the preconditioner 𝑀 −1 is given by pr#PREC and the scalar products are computed by pr#scalprod.
More details are given in the section Solving the linear system.
1 ffddmgeneosetup(pr,Varf)
This builds the GenEO coarse space for problem pr. This will create and expose variables whose names will be prefixed
by pr, see below. It is assumed that ffddmsetupPrecond has already been called for prefix pr in order to define the one
level preconditioner for problem pr. The GenEO coarse space is 𝑍 = (𝑅𝑖𝑇 𝐷𝑖 𝑉𝑖,𝑘 )𝑖=1,...,𝑁
𝜆𝑖,𝑘 ≥𝜏 , where 𝑉𝑖,𝑘 are eigenvectors
corresponding to eigenvalues 𝜆𝑖,𝑘 of the following local generalized eigenvalue problem in subdomain 𝑖:
𝐷𝑖 𝐴𝑖 𝐷𝑖 𝑉𝑖,𝑘 = 𝜆𝑖,𝑘 𝐴Neu
𝑖 𝑉𝑖,𝑘 ,
where 𝐴Neu
𝑖 is the local Neumann matrix of subdomain 𝑖 (with Neumann boundary conditions at the subdomain inter-
face).
In practice, this builds and factorizes the local Neumann matrices 𝐴Neu
𝑖 corresponding to the abstract bilinear form given
by the macro Varf (see ffddmsetupOperator for more details on how to define the abstract variational form as a macro).
In the GenEO method, the abstract bilinear form Varf is assumed to be the same as the one used to define the problem
pr through the previous call to ffddmsetupOperator. The local generalized eigenvalue problem is then solved in each
subdomain to find the eigenvectors 𝑉𝑖,𝑘 corresponding to the largest eigenvalues 𝜆𝑖,𝑘 (see pr#Z below). The number
of computed eigenvectors 𝜈 is given by ffddmnu. The eigenvectors selected to enter 𝑍 correspond to eigenvalues 𝜆𝑖,𝑘
larger than 𝜏 , where the threshold parameter 𝜏 is given by ffddmtau. If ffddmtau = 0, all ffddmnu eigenvectors are
selected. Finally, the coarse space operator 𝐸 = 𝑍 𝑇 𝐴𝑍 is assembled and factorized (see pr#E below).
defines:
• pr#prfe#K[int][int] pr#Z array of local eigenvectors 𝑍𝑖,𝑘 = 𝐷𝑖 𝑉𝑖,𝑘 obtained by solving the local gener-
alized eigenvalue problem above in the subdomain of this mpi rank using Arpack. The number of computed
eigenvectors 𝜈 is given by ffddmnu. The eigenvectors selected to enter 𝑍 correspond to eigenvalues 𝜆𝑖,𝑘 larger
than 𝜏 , where the threshold parameter 𝜏 is given by ffddmtau. If ffddmtau = 0, all ffddmnu eigenvectors are
selected.
• matrix<pr#prfe#K> pr#E the coarse space operator 𝐸 = 𝑍 𝑇 𝐴𝑍. The matrix pr#E is assembled in parallel
and is factorized by the parallel direct solver MUMPS using the first pr#prfe#prmesh#pCS ranks of the mpi
communicator, with mpi rank 0 as the master process. The number of mpi processes dedicated to the coarse
problem is set by the underlying mesh decomposition of problem pr, which also specifies if these mpi ranks are
excluded from the spatial decomposition or not. These parameters are set by ffddmpCS and ffddmexclude when
calling ffddmbuildDmesh (see ffddmbuildDmesh for more details).
1 ffddmcoarsemeshsetup(pr,Thc,VarfEprec,VarfAprec)
builds the coarse space for problem pr from a coarse problem which corresponds to the discretization of a variational
form on a coarser mesh Thc of Ω. This will create and expose variables whose names will be prefixed by pr, see below.
It is assumed that ffddmsetupPrecond has already been called for prefix pr in order to define the one level preconditioner
for problem pr. The abstract variational form for the coarse problem can differ from the original problem pr and is
given by macro VarfEprec (see ffddmsetupOperator for more details on how to define the abstract variational form as
a macro). For example, absorption can be added in the preconditioner for wave propagation problems, see examples
for Helmholtz and Maxwell equations in the Examples section.
The coarse space 𝑍 corresponds to the interpolation operator from the coarse finite element space to the original finite
element space of the problem. Thus, the coarse space operator 𝐸 = 𝑍 𝑇 𝐴Eprec 𝑍 corresponds to the matrix of the
problem given by VarfEprec discretized on the coarse mesh Thc and is assembled as such.
Similarly, VarfAprec specifies the global operator involved in multiplicative coarse correction formulas. For example,
−1
𝑀2,ADEF1 = 𝑀1−1 (𝐼 − 𝐴Aprec 𝑄) + 𝑄 (where 𝑄 = 𝑍𝐸 −1 𝑍 𝑇 ). 𝐴Aprec defaults to 𝐴 if VarfAprec is not a valid macro
(you can put null for example).
defines:
• meshN pr#ThCoarse the coarse mesh Thc
• fespace pr#VhCoarse the coarse finite element space of type pr#prfe#fPk defined on the coarse mesh
pr#ThCoarse
• matrix<pr#prfe#K> pr#AglobEprec the global matrix 𝐴Aprec corresponding to the discretization of the vari-
ational form given by the macro VarfAprec on the global finite element space pr#prfe#Vhglob. Defined only
in the sequential case. pr#AglobEprec is equal to pr#Aglobal if VarfAprec is not a valid macro.
• matrix<pr#prfe#K> pr#aRdEprec the local ‘Dirichlet’ matrix corresponding to VarfAprec; it is the local
restriction of the global operator 𝐴Aprec to the subdomain, equivalent to 𝐴Aprec
𝑖 = 𝑅𝑖 𝐴Aprec 𝑅𝑖𝑇 with 𝐴Aprec the
global matrix corresponding to the discretization of the variational form given by the macro VarfAprec on the
global finite element space. Defined only if this mpi rank is not excluded from the spatial domain decomposition,
i. e. prmesh#excluded = 0. pr#aRdEprec is equal to pr#aRd[mpiRank(prmesh#commddm)] if VarfAprec
is not a valid macro.
• func pr#prfe#K[int] pr#AEprec(pr#prfe#K[int] &ui) The function pr#AEprec computes the parallel
Aprec
matrix-vector product,
∑︀𝑁 i.e. the action of the global operator 𝐴 on the local vector 𝑢𝑖 . The computation is
equivalent to 𝑅𝑖 ( 𝑗=1 𝑅𝑗𝑇 𝐷𝑗 𝐴Aprec
𝑗 𝑢𝑗 ) and is performed in parallel using local matrices pr#aRdEprec and the
function pr#prfe#update. In the sequential case, the global matrix pr#AglobEprec is used instead.
• matrix<pr#prfe#K> pr#ZCM the interpolation operator 𝑍 from the coarse finite element space pr#VhCoarse
to the global finite element space pr#prfe#Vhglob. Defined only in the sequential case.
• matrix<pr#prfe#K> pr#ZCMi the local interpolation operator 𝑍𝑖 from the coarse finite element space
pr#VhCoarse to the local finite element space pr#prfe#Vhi. Defined only if this mpi rank is not excluded
from the spatial domain decomposition, i. e. prmesh#excluded = 0. pr#ZCMi is used for the parallel applica-
tion of 𝑍 and 𝑍 𝑇 .
• matrix<pr#prfe#K> pr#ECM the coarse space operator 𝐸 = 𝑍 𝑇 𝐴Eprec 𝑍. The matrix pr#ECM is assembled
by discretizing the variational form given by VarfEprec on the coarse mesh and factorized by the parallel direct
solver MUMPS using the first pr#prfe#prmesh#pCS ranks of the mpi communicator, with mpi rank 0 as the
master process. The number of mpi processes dedicated to the coarse problem is set by the underlying mesh
decomposition of problem pr, which also specifies if these mpi ranks are excluded from the spatial decom-
position or not. These parameters are set by ffddmpCS and ffddmexclude when calling ffddmbuildDmesh (see
ffddmbuildDmesh for more details).
solves the linear system for problem pr using the flexible GMRES algorithm with preconditioner 𝑀 −1 (corresponding
to pr#PREC). Returns the local vector corresponding to the restriction of the solution to pr#prfe#Vhi. x0i and bi are
local distributed vectors corresponding respectively to the initial guess and the right-hand side (see ffddmbuildrhs). eps
is the stopping criterion in terms of the relative decrease in residual norm. If eps < 0, the residual norm itself is used
instead. itmax sets the maximum number of iterations. sp selects between the "left" or "right" preconditioning
variants: left preconditioned GMRES solves 𝑀 −1 𝐴𝑥 = 𝑀 −1 𝑏, while right preconditioned GMRES solves 𝐴𝑀 −1 𝑦 =
𝑏 for 𝑦, with 𝑥 = 𝑀 −1 𝑦.
You can also choose to replace only the Krylov solver, by defining the macro pr#withhpddmkrylov before using
ffddmsetupOperator. Doing so, a call to pr#fGMRES will call the HPDDM Krylov solver, with ffddm providing the
operator and preconditioner through pr#A and pr#PREC. You can then pass HPDDM options to the Krylov solver
through command-line arguments:
For example, using restarted GCRO-DR(40) and recycling 10 Ritz vectors at each restart:
Advanced use
Interpolation between two distributed finite element spaces
The parallel interpolation of a distributed finite element function to another distributed finite element space can be
computed using the prfe#transferfromVhi macro. Internally, it uses the transfer macro from the macro_ddm.
idp script. The macro is prefixed by the source finite element prefix prfe and is used a follows:
1 prfe#transferfromVhi(us,Vht,Pkt,rest)
where us is distributed source FE function defined on prfe#Vhi, Vht is the target local finite element space, Pkt is
the approximation space corresponding to Vht and rest is the target local FE function defined on Vht. You can find
an example below:
1 macro dimension()2//
2
3 include "ffddm.idp"
4
9 ffddmbuildDmesh(Ms,Ths,mpiCommWorld)
10
11 ffddmbuildDmesh(Mt,Tht,mpiCommWorld)
12
13 func Pk = [P2,P2];
14
15 macro def(u)[u,u#2]//
16 macro init(u)[u,u]//
17 ffddmbuildDfespace(FEs,Ms,real,def,init,Pk)
18 ffddmbuildDfespace(FEt,Mt,real,def,init,Pk)
19
22 FEtVhi def(ut);
23
24 FEstransferfromVhi(us,FEtVhi,Pk,ut)
25
26 ffddmplot(FEs,us,"u source");
27 ffddmplot(FEt,ut,"u target");
For Lagrange finite elements, the partition of unity (𝐷𝑖 )𝑖=1,...,𝑁 (see prfe#Dk and prfe#Dih) is built by interpolating
the local P1 partition of unity function onto the components of the Pk finite element space prfe#Vhi. For non Lagrange
finite element spaces, such as Raviart–Thomas or Nédélec edge elements, the definition of the degrees of freedom can
be more involved, and interpolating the P1 partition of unity functions directly is inappropriate. The idea is then to use
a “pseudo” finite element Pkpart derived from Pk which is suitable for interpolating the P1 partition of unity, in the
sense that it will produce a partition of unity for Pk.
For example, for first-order Nédélec edge elements (Edge03d), whose degrees of freedom are the circulations along
the edges, we define the “pseudo” finite element Edge03ds0 which can be seen as a scalar Lagrange counterpart: the
numbering of the degrees of freedom is the same, but they correspond to the value at the edge midpoints.
For Lagrange finite elements, the distributed finite element spaces are built using ffddmbuildDfespace. Here you must
use ffddmbuildDfespaceEdge, which builds the distributed finite element space using a “pseudo” finite element to
build the partition of unity:
1 ffddmbuildDfespaceEdge(prfe,prmesh,scalar,def,init,Pk,defpart,initpart,Pkpart)
where macros defpart and initpart specify how to define and interpolate a function in the ‘pseudo’ finite element
space Pkpart, similar to def and init for Pk.
An example with first-order Nédélec edge elements (Edge03d + Edge03ds0) for Maxwell equations can be found in
Maxwell-3d-simple.edp, see the Examples section.
We have seen in the Two level preconditioners section that two level methods produce a ‘coarse space operator’ 𝐸 that
needs to be inverted at each iteration. By default the coarse space operator matrix is factorized by the direct solver
MUMPS. This can become a bottleneck and hinder scalability for large problems, where 𝐸 can become too large to be
factorized efficiently. To remedy this, we can instead opt to use an iterative method to solve the coarse problem at each
iteration. Moreover, in order to retain robustness, a DD preconditioner can be used to solve the inner coarse problem
more efficiently.
When the coarse problem comes from a coarse mesh discretization, a natural way to do inexact coarse solve is to use a
one level domain decomposition method on the coarse problem, with the same subdomain partitioning for the coarse
and fine meshes. This means that each processor is associated to one spatial subdomain and hosts the two local (nested)
coarse and fine submeshes corresponding to this subdomain, as well as the corresponding local matrices for the two
discretizations. This natural choice offers interesting benefits:
• We naturally recover a load-balanced parallel implementation, provided that the initial partitioning is balanced.
• The communication pattern between neighboring subdomains is the same for the coarse and fine discretizations.
• The assembly and the application of the interpolation operator 𝑍 (and 𝑍 𝑇 ) between the fine and the coarse spaces
can be computed locally in each subdomain and require no communication.
In ffddm, the first step is to build the two nested mesh decompositions using ffddmbuildDmeshNested:
1 ffddmbuildDmeshNested(prmesh,Thc,s,comm)
decomposes the coarse mesh Thc into overlapping submeshes and creates the fine decomposition by locally refining
submeshes by a factor of s, i.e. splitting each mesh element into 𝑠𝑑 elements, 𝑠 ≥ 1. This will create and expose
variables corresponding to both decompositions, prefixed by prmesh for the fine mesh and by prmesh#Coarse for the
coarse mesh (see ffddmbuildDmesh). It also sets the integer variable prmesh#binexactCS to 1, which specifies that
any two level method defined on mesh prefix prmesh will use inexact coarse solves.
The distributed finite element spaces, operators and preconditioners can then be defined for both decompositions. Here
is an example where the coarse problem is solved using a one level method:
19 [...]
20 u[] = PBfGMRES(x0, rhs, 1.e-6, 200, "right");
Remarks:
• Note that the different prefixes need to match: prefixes for the coarse decomposition have to be those of the fine
decomposition, appended with Coarse.
• The operator and preconditioner for the coarse problem have to be defined before those of the fine problem,
because the pr#Q function is actually defined by ffddmsetupPrecond and involves a call to pr#CoarsefGMRES
(which is defined by ffddmsetupPrecond for the coarse problem) for the iterative solution of the coarse problem
if pr#prfe#prmesh#binexactCS ̸= 0.
• In this case, ffddmcoarsemeshsetup does not use Thc or VarfEprec and only builds the local interpolation
matrices between fine and coarse local finite element spaces pr#prfe#Vhi and pr#prfe#CoarseVhi to be able
to apply 𝑍 and 𝑍 𝑇 .
• The GMRES tolerance for the inner solution of the coarse problem is set by ffddminexactCStol and is equal to
0.1 by default.
In practice, these methods can give good results for wave propagation problems, where the addition of artificial ab-
sorption in the preconditioner helps with the convergence of the one level method for the inner solution of the coarse
problem. You can find an example for Maxwell equations in Maxwell_Cobracavity.edp, see the Examples section.
More details can be found here and in
M. Bonazzoli, V. Dolean, I. G. Graham, E. A. Spence, P.-H. Tournier. Domain decomposition precondi-
tioning for the high-frequency time-harmonic Maxwell equations with absorption. Mathematics of Com-
putation, 2019. DOI: https://doi.org/10.1090/mcom/3447
3.12.3 Parameters
Command-line arguments
• -ffddm_verbosity N, the level of verbosity of ffddm, see ffddmverbosity (default 3).
• -seqddm N use ffddm in sequential mode, with N the number of subdomains.
• -noGlob if present, do not define any global quantity (such as saving the global mesh for plotting or building
the global restriction matrices). Cannot be used in sequential mode or with plotting.
• -ffddm_partitioner N specifies how to partition the initial domain, see ffddmpartitioner (default 1, metis).
• -ffddm_overlap N specifies the width of the overlap region between subdomains, see ffddmoverlap (default
1).
• -ffddm_master_p N, number of master processes for the coarse problem (for two level preconditioners), see
ffddmpCS (default 1).
• -ffddm_master_exclude 0|1 exclude master processes from the domain decomposition, see ffddmexclude
(default 0).
• -ffddm_split N, level of refinement of the local submeshes with respect to the initial global mesh, see ffddm-
split (default 1).
• -ffddm_gmres_restart N, restart parameter for GMRES, see ffddmrestart (default 200).
• -ffddm_schwarz_method S, specifies the type of one level preconditioner 𝑀1−1 : “asm” (Additive Schwarz),
“ras” (Restricted Additive Schwarz), “oras” (Optimized Restricted Additive Schwarz), “soras” (Symmetric Opti-
mized Restricted Additive Schwarz) or “none” (no preconditioner), see ffddmprecond (default “ras”).
• -ffddm_geneo_nu N, number of local eigenvectors to compute in each subdomain when solving the local gen-
eralized eigenvalue problem for the GenEO method, see ffddmnu (default 20).
• -ffddm_geneo_threshold R, threshold parameter for selecting local eigenvectors when solving the local gen-
eralized eigenvalue problems for the GenEO method, see ffddmtau (default 0.5). If the command-line parameter
-ffddm_geneo_nu N is used, then ffddmtau is initialized to 0.
• -ffddm_schwarz_coarse_correction S, specifies the coarse correction formula to use for the two level
preconditioner: “AD” (Additive), “BNN” (Balancing Neumann-Neumann), “ADEF1” (Adapted Deflation Variant
1), “ADEF2” (Adapted Deflation Variant 2), “RBNN1” (Reduced Balancing Variant 1), “RBNN2” (Reduced
Balancing Variant 2) or “none” (no coarse correction), see ffddmcorrection (default “ADEF1”).
• -ffddm_inexactCS_tol R, specifies the GMRES tolerance for the inner solution of the coarse problem when
using a two level method with approximate coarse solves, see ffddminexactCStol (default 0.1).
Global parameters
• ffddmverbosity initialized by command-line argument -ffddm_verbosity N, specifies the level of verbosity
of ffddm (default 3).
• ffddmpartitioner initialized by command-line argument -ffddm_partitioner N, specifies how to partition
the initial domain:
– N=0: user-defined partition through the definition of a macro, see ffddmbuildDmesh
– N=1: use the automatic graph partitioner metis (default)
– N=2: use the automatic graph partitioner scotch
• ffddmoverlap initialized by command-line argument -ffddm_overlap N, specifies the number of layers of mesh
elements in the overlap region between subdomains ; N >= 0 (default 1). Remark The actual width of the overlap
region between subdomains is 2N, since each subdomain is extended by N layers of elements in a symmetric way.
Remark 2 if ffddmoverlap = 0, the construction is a bit different, since only interface unknowns are shared. In
that case, the user is required to define the macro prmesh#mminoverlap to 1 before calling ffddmbuildDmesh.
It can be done e.g. on the command line: -ffddm_overlap 0 -DMESHmminoverlap=1, where MESH is your
distributed mesh prefix.
• ffddminterfacelabel the label of the new border of the subdomain meshes (the interface between the sub-
domains) (default 10). Used for imposing problem-dependent boundary conditions at the interface between
subdomains for the preconditioner, for example optimized Robin boundary conditions (see ORAS).
• ffddmpCS initialized by command-line argument -ffddm_master_p N, number of mpi processes used for the
assembly and resolution of the coarse problem for two level preconditioners (default 1).
• ffddmexclude initialized by command-line argument -ffddm_master_exclude, 0 or 1 (default 0). If true, mpi
ranks participating in the assembly and resolution of the coarse problem for two level preconditioners will be
excluded from the spatial domain decomposition and will only work on the coarse problem.
• ffddmsplit initialized by command-line argument ffddm_split N, level of refinement of the local submeshes
with respect to the initial global mesh (default 1). This is useful for large problems, where we want to avoid
working with a very large global mesh. The idea is to start from a coarser global mesh, and generate finer local
meshes in parallel during the mesh decomposition step in order to reach the desired level of refinement for the
subdomains. For example, calling ffddmbuildDmesh with ffddmsplit = 3 will generate local submeshes where
each mesh element of the initial mesh is split into 3𝑑 elements.
• ffddmrestart initialized by command-line argument ffddm_gmres_restart N, restart parameter for GMRES
(maximum dimension of the Krylov subspace). Restarts the method every N inner iterations (default 200).
• ffddmprecond initialized by command-line argument -ffddm_schwarz_method S, specifies the type of one
level preconditioner 𝑀1−1 to build when calling ffddmsetupPrecond: “asm” (Additive Schwarz), “ras” (Restricted
Additive Schwarz), “oras” (Optimized Restricted Additive Schwarz), “soras” (Symmetric Optimized Restricted
Additive Schwarz) or “none” (no preconditioner). Default is “ras”. See ffddmsetupPrecond for more details.
• ffddmnu initialized by command-line argument -ffddm_geneo_nu N, number of local eigenvectors to compute
in each subdomain when solving the local generalized eigenvalue problem for the GenEO method (default 20).
See ffddmgeneosetup for more details.
• ffddmtau initialized by command-line argument -ffddm_geneo_threshold R, threshold parameter for selecting
local eigenvectors when solving the local generalized eigenvalue problems for the GenEO method (default 0.5). If
the command-line parameter -ffddm_geneo_nu N is used, then ffddmtau is initialized to 0. See ffddmgeneosetup
for more details.
• ffddmcorrection initialized by command-line argument -ffddm_schwarz_coarse_correction S, specifies the
coarse correction formula to use for the two level preconditioner: “AD” (Additive), “BNN” (Balancing Neumann-
Neumann), “ADEF1” (Adapted Deflation Variant 1), “ADEF2” (Adapted Deflation Variant 2), “RBNN1” (Re-
duced Balancing Variant 1), “RBNN2” (Reduced Balancing Variant 2) or “none” (no coarse correction). Default
is “ADEF1”. See the section about Two level preconditioners for more details.
• ffddminexactCStol initialized by command-line argument -ffddm_inexactCS_tol R, GMRES tolerance for
the inner solution of the coarse problem when using a two level method with approximate coarse solves (default
0.1). See the section about Approximate coarse solves for two level methods for more details.
3.12.4 Tutorial
Authors: Pierre-Henri Tournier - Frédéric Nataf - Pierre Jolivet
What is ffddm ?
• ffddm implements a class of parallel solvers in FreeFEM: overlapping Schwarz domain decomposition methods
• The entire ffddm framework is written in the FreeFEM language ffddm aims at simplifying the use of
parallel solvers in FreeFEM
You can find the ffddm scripts here (‘ffddm*.idp’ files) and examples here
• ffddm provides a set of high-level macros and functions to
– handle data distribution: distributed meshes and linear algebra
– build DD preconditioners for your variational problems
– solve your problem using preconditioned Krylov methods
• ffddm implements scalable two level Schwarz methods, with a coarse space correction built either from a coarse
mesh or a GenEO coarse space Ongoing research: approximate coarse solves and three level methods
• ffddm can also act as a wrapper for the HPDDM library.
HPDDM is an efficient C++11 implementation of various domain decomposition methods and Krylov
subspace algorithms with advanced block and recycling techniques More details on how to use HPDDM
within ffddm here
See documentation
Build a collection of 𝑁 overlapping sub-meshes (𝑇 ℎ𝑖 )𝑁
𝑖=1 from the global mesh 𝑇 ℎ
10 medit("Th"+mpirank, MThi);
See documentation
builds the local finite element spaces and associated distributed operators on top of the mesh decomposition prmesh
• scalar: type of data for this finite element: real or complex
• Pk: your type of finite element: P1, [P2,P2,P1], . . .
• def, init: macros specifying how to define and initialize a Pk FE function
prfe#Vhi is the local FE space defined on prmesh#Thi for each mpi process
Example for P2 complex:
Natural decomposition of the set of d.o.f.’s 𝒩 of 𝑉 ℎ into the 𝑁 subsets of d.o.f.’s (𝒩𝑖 )𝑁
𝑖=1 each associated with the
local FE space 𝑉 ℎ𝑖
𝒩 = ∪𝑁
𝑖=1 𝒩𝑖 ,
V𝑖 = 𝑅𝑖 V, 𝑖 = 1, ..., 𝑁
where V is the corresponding global vector and 𝑅𝑖 is the restriction operator from 𝒩 into 𝒩𝑖
Remark 𝑅𝑖𝑇 is the extension operator: extension by 0 from 𝒩𝑖 into 𝒩
Partition of unity
synchronizes local vectors V𝑖 between subdomains ⇒ exchange the values of 𝑚𝑎𝑡ℎ𝑏𝑓 𝑉𝑖 shared with neighbors in the
overlap region
⎛ ⎞
𝑁
∑︁ ∑︁
V𝑖 ← 𝑅𝑖 ⎝ 𝑅𝑗𝑇 𝐷𝑗 V𝑗 ⎠ = 𝐷𝑖 V𝑖 + 𝑅𝑖 𝑅𝑗𝑇 𝐷𝑗 V𝑗
𝑗=1 𝑗∈𝒪(𝑖)
where 𝒪(𝑖) is the set of neighbors of subdomain $i$. Exchange operators 𝑅𝑖 𝑅𝑗𝑇 correspond to neighbor-to-neighbor
MPI communications
1 FEupdate(vi, false);
⎛ ⎞
𝑁
∑︁
V𝑖 ← 𝑅𝑖 ⎝ 𝑅𝑗𝑇 V𝑗 ⎠
𝑗=1
1 FEupdate(vi, true);
⎛ ⎞
𝑁
∑︁
V𝑖 ← 𝑅𝑖 ⎝ 𝑅𝑗𝑇 𝐷𝑗 V𝑗 ⎠
𝑗=1
3 include "ffddm.idp"
4
15 FEVhi vi = x;
16 medit("v"+mpirank, MThi, vi);
17
18 vi[] = FEDk[mpirank];
19 medit("D"+mpirank, MThi, vi);
20
21 vi = 1;
22 FEupdate(vi[],true);
(continues on next page)
25 FEupdate(vi[],false);
26 ffddmplot(FE,vi,"multiplicity")
See documentation
builds the distributed operator associated to your variational form on top of the distributed FE prfe
Varf is a macro defining your abstract variational form
Warning
only true because 𝐷𝑖 𝑅𝑖 𝐴 = 𝐷𝑖 𝐴𝑖 𝑅𝑖 due to the fact that 𝐷𝑖 vanishes at the interface !!
∑︀𝑁
pr#A applies 𝐴 to a distributed vector: U𝑖 ← 𝑅𝑖 𝑗=1 𝑅𝑗𝑇 𝐷𝑗 𝐴𝑗 V𝑗
⇒ multiply by 𝐴𝑖 + prfe#update
3 include "ffddm.idp"
4
25 ui[] = PBA(bi[]);
26 ffddmplot(FE, ui, "A*b")
See documentation
1 mesh Th = square(100,100);
1 mesh Th = square(100,100);
2 ffddmbuildDmesh(M, Th, mpiCommWorld)
See documentation
1 ui[] = PBdirectsolve(bi[]);
Solve the linear system with the parallel direct solver MUMPS
See documentation
We have 𝐴 and 𝑏 in distributed form, we can solve the linear system 𝐴𝑢 = 𝑏 using the parallel direct solver MUMPS
See documentation
1 ffddmsetupPrecond( pr , VarfPrec )
⇒ apply 𝐴−1
𝑖 (forward/backward substitutions) + prfe#update
See documentation
1 func K[int] pr#fGMRES(K[int]& x0i, K[int]& bi, real eps, int itmax, string sp)
𝑀2−1 = 𝑀1−1 + 𝑍𝐸 −1 𝑍 𝑇
How to choose 𝑍 ?
1 ffddmgeneosetup( pr , Varf )
The GenEO method builds a robust coarse space for highly heterogeneous or anisotropic SPD problems
with 𝐴Neu
𝑖 the local Neumann matrices built from Varf (same Varf as Step 3)
The GenEO coarse space is 𝑍 = (𝑅𝑖𝑇 𝐷𝑖 𝑉𝑖,𝑘 )𝑖=1,...,𝑁
𝜆𝑖,𝑘 ≥𝜏 The eigenvectors 𝑉𝑖,𝑘 selected to enter the coarse space corre-
spond to eigenvalues 𝜆𝑖,𝑘 ≥ 𝜏 , where 𝜏 is a threshold parameter
1
Theorem the spectrum of the preconditioned operator lies in the interval [ , 𝑘0 ] where 𝑘0 − 1 is
1 + 𝑘1 𝜏
the # of neighbors and 𝑘1 is the multiplicity of intersections ⇒ 𝑘0 and 𝑘1 do not depend on 𝑁 nor on the
PDE
For non SPD problems, an alternative is to build the coarse space by discretizing the PDE on a coarser mesh Thc
𝑍 will be the interpolation matrix from the coarse FE space 𝑉 ℎ𝑐 to the original FE space 𝑉 ℎ
⇒ 𝐸 = 𝑍 𝑇 𝐴𝑍 is the matrix of the problem discretized on the coarse mesh
The variational problem to be discretized on Thc is given by macro VarfEprec
Or, define pr#withhpddmkrylov to use HPDDM only for the Krylov method
Example here: Helmholtz problem with multiple rhs solved with Block GMRES
Some results: 2-level DD for Maxwell equations, scattering from the COBRA cavity
f = 10 GHz
f = 16 GHz
Some results: 2-level DD for Maxwell equations, scattering from the COBRA cavity
• order 2 Nedelec edge FE
• fine mesh: 10 points per wavelength
• coarse mesh: 3.33 points per wavelength
• two level ORAS preconditioner with added absorption
• f = 10 GHz: 𝑛 ≈ 107 million, 𝑛𝑐 ≈ 4 million
f = 16 GHz: 𝑛 ≈ 198 million, 𝑛𝑐 ≈ 7.4 million
→ coarse problem too large for a direct solver ⇒ inexact coarse solve: GMRES + one level ORAS preconditioner
3.12.5 Examples
FOUR
LANGUAGE REFERENCES
In essence FreeFEM is a compiler: its language is typed, polymorphic, with exception and reentrant. Every variable
must be declared of a certain type, in a declarative statement; each statement are separated from the next by a semicolon
;.
The language allows the manipulation of basic types integers (int), reals (real), strings (string), arrays (example:
real[int]), bi-dimensional (2D) finite element meshes (mesh), 2D finite element spaces (fespace), analytical func-
tions (func), arrays of finite element functions (func[basic_type]), linear and bilinear operators, sparse matrices,
vectors , etc. For example:
The life of a variable is the current block {...}, except the fespace variable, and the variables local to a block are
destroyed at the end of the block as follows.
Tip
Example
1 real r = 0.01;
2 mesh Th = square(10, 10); //unit square mesh
3 fespace Vh(Th, P1); //P1 Lagrange finite element space
4 Vh u = x + exp(y);
5 func f = z*x + r*log(y);
6 plot(u, wait=true);
7 { // new block
8 real r = 2; //not the same r
9 fespace Vh(Th, P1); //error because Vh is a global name
10 }// end of block
11 //here r back to 0.01
The type declarations are mandatory in FreeFEM; in the end this feature is an asset because it is easy to make bugs in
a language with many implicit types.
The variable name is just an alphanumeric string, the underscore character _ is not allowed, because it will be used as
an operator in the future.
385
FreeFEM Documentation, Release 4.13
4.1 Types
4.1.1 Standard types
int
Integer value (equivalent to long in C++).
1 int i = 0;
bool
Boolean value.
1 bool b = true;
Tip
real
Real value (equivalent to double in C++).
1 real r = 0.;
complex
Complex value (equivalent to two double or complex<double> in C++).
1 complex c = 0. + 1i;
Tip
Example
1 complex a = 1i, b = 2 + 3i;
2 cout << "a + b = " << a + b << endl;
3 cout << "a - b = " << a - b << endl;
4 cout << "a*b = " << a*b << endl;
5 cout << "a/b = " << a/b << endl;
Note
string
String value.
Note
1 int i = 1;
2 real r = 1.;
3 string s = "the int i = " + i +", the real r = " + r + ", the complex z = " + (1. + 1i);
1 s(4:3) = "+++";
1 string s2 = s1(5:10);
Note
Label
A label can be defined with the border:
1 border b(t=0., 1.){x=cos(2.*pi*t); y=sin(2.*pi*t); label=1;}
Note
Inner variable
Note
From vector
A border can be defined from two vectors using P.x and P.y:
1 border b(t=0, vectorX.n-1){P.x=vectorX[t]; P.y=vectorY[t];}
mesh
2D Mesh type (see Mesh Generation).
1 mesh Th;
mesh3
3D mesh type (see Mesh Generation).
1 mesh3 Th;
A finite element space is based on a mesh (Th) with an element definition, scalar (P1) or vector ([P2, P2, P1]).
Available finite element space:
Generic:
• P0 / P03d
• P0Edge
• P1 / P13d
• P1dc
• P1b / P1b3d
• P1bl / P1bl3d
• P1nc
• P2 / P23d
• P2b
• P2dc
• P2h
• RT0 / RT03d
• RT0Ortho
• Edge03d
Using Element_P3:
• P3
Using Element_P3dc:
• P3dc
Using Element_P4:
• P4
Using Element_P4dc:
• P4dc
Using Element_PkEdge:
• P1Edge
• P2Edge
• P3Edge
• P4Edge
• P5Edge
Using Morlay:
• P2Morley
Using HCT :
• HCT
Using BernardiRaugel:
• P2BR
Using Element_Mixte:
• RT1
• RT1Ortho
• RT2
• RT2Ortho
• BDM1
• BDM1Ortho
Using Element_Mixte3d:
• Edge13d
• Edge23d
Using Element_QF:
• FEQF
Note
Macro concatenation
You can use the C concatenation operator ## inside a macro using #.
If Ux and Uy are defined as finite element function, you can define:
1 macro Grad(U) [grad(U#x), grad(U#y)] // End of macro
NewMacro / EndMacro
Warning
IFMACRO
Check if a macro exists and check its value.
ENDIFMACRO
Note
Elementary functions
Class of basic functions (polynomials, exponential, logarithmic, trigonometric, circular) and the functions obtained
from those by the four arithmetic operations
Random functions
FreeFEM includes the Mersenne Twister random number generator. It is a very fast and accurate random number
generator of period 2219937 − 1.
See randint32(), randint31(), randreal1(), randreal2(), randreal3(), randres53(), randinit(seed).
In addition, the ffrandom plugin interface random, srandom and srandomdev functions of the Unix libc library.
The range is 0 − −231 − 1.
Note
gsl plugin equally allows usage of all random functions of the gsllib, see gsl external library.
FE-functions
Finite element functions are also constructed like elementary functions by an arithmetic formula involving elementary
functions.
The difference is that they are evaluated at declaration time and FreeFEM stores the array of its values at the places
associated with he degree of freedom of the finite element type. By opposition, elementary functions are evaluated only
when needed. Hence FE-functions are not defined only by their formula but also by the mesh and the finite element
which enter in their definitions.
If the value of a FE-function is requested at a point which is not a degree of freedom, an interpolation is used, leading
to an interpolation error, while by contrast, an elementary function can be evaluated at any point exactly.
Warning
The plot command only works for real or complex FE-functions, not for elementary functions.
1 Laplacian;
Note
Solver
A solver can be specified in the problem definition:
1 problem Laplacian(u, uh, solver=CG) = ...
Note
Stop test
|𝜀|
||𝐴𝑥 − 𝑏|| <
||𝐴𝑥0 − 𝑏||
Note
Reconstruction
The keyword init controls the reconstruction of the internal problem matrix.
If init is set to false or 0, the matrix is reconstructed et each problem calls (or after a mesh modification), else
the previously constructed matrix is used.
1 problem Laplacian(u, uh, init=1) = ...
Note
Preconditioning
A preconditioner can be specified in the problem definition:
1 problem Laplacian(u, uh, precon=P) = ...
Note
Note
Pivot tolerance
The tolerance of the pivot in UMFPACK, LU, Crout, Cholesky factorization can be modified in the problem defini-
tion:
Note
UMFPACK
Two specific parameters for the UMFPACK can be modifed:
• Tolerance of the pivot sym
• strategy
1 problem Laplacian(u, uh, solver=LU, tolpivotsym=1e-1, strategy=0) = ...
Note
dimKrylov
Dimension of the Krylov space
solve
Solve type.
Identical to problem but automatically solved.
Usage of solve is detailled in the tutorials.
varf
Variational form type.
4.1.7 Array
An array stores multiple objects, and there are 2 kinds of arrays:
• the first is similar to vector, i.e. array with integer indices
• the second is array with string indices
In the first case, the size of the array must be known at execution time, and implementation is done with the KN<> class
and all the vector operator of KN<> are implemented.
Arrays can be set like in Matlab or Scilab with the operator ::, the array generator of a:c is equivalent to a:1:c, and
the array set by a:b:c is set to size ⌊|(𝑏 − 𝑎)/𝑐| + 1⌋ and the value 𝑖 is set by 𝑎 + 𝑖(𝑏 − 𝑎)/𝑐.
There are int,real, complex array with, in the third case, two operators (.im, .re) to generate the real and imagi-
nary real array from the complex array (without copy).
Note
Quantiles are points taken at regular intervals from the cumulative distribution function of a random variable. Here
the array values are random.
This statistical function a.quantile(q) computes 𝑣 from an array 𝑎 of size 𝑛 for a given number 𝑞 ∈]0, 1[ such
that:
#{𝑖/𝑎[𝑖] < 𝑣} ∼ 𝑞 * 𝑛
1 int n = 5;
2 real[int] Ai(n);
3 for (int i = 0; i < n; i++)
4 Ai[i] = i;
5 cout << Ai << endl;
5
0 1 2 3 4
Array index
Array index can be int or string:
Array size
The size of an array is obtained using the keyword n:
Array sort
To sort an array:
1 Ai.sort;
Double array
A double array (matrix) can be defined using two indexes:
The minimum and maximum values of an array (simple or double) can be obtained using:
Tip
Array of FE functions
It is also possible to make an array of FE functions, with the same syntax, and we can treat them as vector valued
function if we need them.
The syntax for space or vector finite function is
8 // Array of Array
9 real [int][int] V(10);
10 matrix[int] B(10);
11 real [int, int][int] A(10);
Tip
Example
In the following example, Poisson’s equation is solved for 3 different given functions 𝑓 = 1, sin(𝜋𝑥) cos(𝜋𝑦), |𝑥 −
1||𝑦 − 1|, whose solutions are stored in an array of FE function.
1 // Mesh
2 mesh Th = square(20, 20, [2*x, 2*y]);
3
4 // Fespace
5 fespace Vh(Th, P1);
6 Vh u, v, f;
7
8 // Problem
9 problem Poisson (u, v)
10 = int2d(Th)(
11 dx(u)*dx(v)
12 + dy(u)*dy(v)
13 )
14 + int2d(Th)(
15 - f*v
16 )
17 + on(1, 2, 3, 4, u=0)
18 ;
19
34 // Plot
35 for (int i = 0; i < 3; i++)
36 plot(uu[i], wait=true);
See FE array example.
Map arrays
3 map["1"] = 2.0;
4 map[2] = 3.0; //2 is automatically cast to the string "2"
5
It is just a map of the standard template library so no operations on vector are allowed, except the selection of an item.
4.1.8 matrix
Defines a sparse matrix.
Matrices can be defined like vectors:
1 A = [I, J, C];
Note
1 matrix<real> A = ...
2 matrix<complex> Ai = ...
Note
Solver
See problem.
The default solver is GMRES.
1 matrix A = vLaplacian(Uh, Uh, solver=sparsesolver);
or
1 set(A , solver=sparsesolver);
Note
Factorize
If true, the factorization is done for LU, Cholesky or Crout.
1 matrix A = vLaplacian(Uh, Uh, solver=LU, factorize=1);
or
1 set(A , solver=LU, factorize=1);
Note
Stop test
See problem.
Note
Note
Preconditioning
See problem.
Note
Pivot tolerance
See problem.
Note
UMFPACK
See problem.
Note
dimKrylov
See problem.
Note
datafilename
Name of the file containing solver parameters, see Parallel sparse solvers
Note
lparams
Vector of integer parameters for the solver, see Parallel sparse solvers
Note
dparams
Vector of real parameters for the solver, see Parallel sparse solvers
Note
sparams
String parameters for the solver, see Parallel sparse solvers
Tip
To modify the solver, the stop test,. . . after the matrix construction, use the set keyword.
Matrix size
The size of a matrix is obtain using:
Matrix resize
To resize a matrix, use:
1 A.resize(n, m);
Warning
Matrix diagonal
The diagonal of the matrix is obtained using:
Matrix renumbering
Complex matrix
Use .im and .re to get the imaginary and real part of a complex matrix, respectively:
1 matrix<complex> C = ...
2 matrix R = C.re;
3 matrix I = C.im;
1 real d = A' * B;
1 matrix C = A * B'
Matrix inversion
See Matrix inversion example.
4.2.2 ARGV
Array that contains all the command line arguments.
4.2.3 BoundaryEdge
Return 1 if the current edge is on a boundary, 0 otherwise.
1 real B = int2d(Th)(BoundaryEdge);
4.2.4 CG
Conjugate gradient solver.
Usable in problem and solve definition
Or in matrix construction
Or in set function
1 set(A, solver=CG);
4.2.5 Cholesky
Cholesky solver.
4.2.6 Crout
Crout solver.
4.2.7 edgeOrientation
Sign of 𝑖 − 𝑗 if the current edge is [𝑞𝑖 , 𝑞𝑗 ].
4.2.8 false
False boolean value.
1 bool b = false;
4.2.9 GMRES
GMRES solver (Generalized minimal residual method).
4.2.10 hTriangle
Size of the current triangle.
4.2.11 include
Include an external library.
1 include "iovtk"
4.2.12 InternalEdge
Return 0 if the current edge is on a boundary, 1 otherwise.
1 real I = int2d(Th)(InternalEdge);
4.2.13 label
Label number of a boundary if the current point is on a boundary, 0 otherwise.
4.2.14 lenEdge
Length of the current edge.
For an edge [𝑞𝑖 , 𝑔𝑗 ], return |𝑞𝑖 − 𝑞𝑗 |.
4.2.15 load
Load a script.
1 load "Element_P3"
4.2.16 LU
LU solver.
4.2.17 N
Outward unit normal at the current point if it is on a curve defined by a border. N.x, N.y, N.z are respectively the
𝑥, 𝑦 and 𝑧 components of the normal.
1 func Nx = N.x;
2 func Ny = N.y;
3 func Nz = N.z;
4.2.18 nTonEdge
Number of adjacent triangles of the current edge.
4.2.19 nuEdge
Index of the current edge in the triangle.
1 real nE = int2d(Th)(nuEdge);
4.2.20 nuTriangle
Index of the current triangle.
1 fespace Vh(Th, P0);
2 Vh n = nuTriangle;
4.2.21 P
Current point.
1 real cx = P.x;
2 real cy = P.y;
3 real cz = P.z;
4.2.22 pi
Pi = 3.14159.
1 real Pi = pi;
4.2.23 region
Region number of the current point. If the point is outside, then region == notaregion where notaregion is a
FreeFEM integer constant.
1 int R = Th(xR, yR).region;
4.2.24 sparsesolver
Sparse matrix solver.
4.2.25 true
True boolean value.
1 bool b = true;
4.2.26 verbosity
Verbosity level.
1 int Verbosity = verbosity;
2 verbosity = 0;
4.2.27 version
FreeFEM version.
4.2.28 volume
Volume of the current tetrahedra.
1 fespace Vh0(Th, P0);
2 Vh0 V = volume;
4.2.29 x
The 𝑥 coordinate at the current point.
1 real CurrentX = x;
4.2.30 y
The 𝑦 coordinate at the current point.
1 real CurrentY = y;
4.2.31 z
The 𝑧 coordinate at the current point.
1 real CurrentZ = z;
4.3.1 int1d
Quadrature formula on an edge.
Notations
|𝐷| is the measure of the edge 𝐷.
For a shake of simplicity, we denote:
𝑓 (x) = 𝑔(𝑡)
with 0 ≤ 𝑡 ≤ 1; x = (1 − 𝑡)x0 + 𝑡x1 .
qf1pE
or
qf2pE
or
qf3pE (default)
or
qf4pE
or
qf5pE
or
qf1pElump
|𝐷|
∫︁
𝑓 (x) ≈ (𝑔 (0) + 𝑔 (1))
𝐷 2
4.3.2 int2d
Note
qf1pT
or
qf2pT
or
qf5pT (default)
or
qf1pTlump
qf2pT4P1
qf7pT
or
qf9pT
or
4.3.3 int3d
qfV1
or
qfV2
or
qfV5 (default)
or
qfV1lump
4.4 Operators
4.4.1 Addition operator +
1 real a = 1. + 2.;
1 int i = 0;
2 ++i;
Post-increment:
1 int i = 0;
2 i++;
1 int i = 0;
2 --i;
Post-decrement:
1 int i = 0;
2 i--;
Warning
This operator can not be used to directly create a matrix, see Matrix inversion.
Note
Tip
3 cout << a << " + " << b << " = " << a + b << endl;
4 cout << a << " - " << b << " = " << a - b << endl;
5 cout << a << " * " << b << " = " << a * b << endl;
6 cout << a << " / " << b << " = " << a / b << endl;
7 cout << a << " % " << b << " = " << a % b << endl;
8 cout << a << " ^ " << b << " = " << a ^ b << endl;
9 cout << "( " << a << " < " << b << " ? " << a << " : " << b << ") = " << (a < b ? a :␣
˓→b) << endl;
Tip
3 cout << a << " + " << b << " = " << a + b << endl;
4 cout << a << " - " << b << " = " << a - b << endl;
5 cout << a << " * " << b << " = " << a * b << endl;
6 cout << a << " / " << b << " = " << a / b << endl;
7 cout << a << " % " << b << " = " << a % b << endl;
8 cout << a << " ^ " << b << " = " << a ^ b << endl;
9 cout << "( " << a << " < " << b << " ? " << a << " : " << b << ") = " << (a < b ? a :␣
˓→b) << endl;
4.5 Loops
See Loop example.
4.5.1 for
For loop.
4.5.2 if
If condition.
1 if (condition){
2 ...
3 }
4 else{
5 ...
6 }
4.5.3 else
See if .
4.5.4 while
While loop.
1 while (condition){
2 ...
3 }
4.5.5 continue
Continue a loop.
4.5.6 break
Break a loop.
1 while (condition1){
2 ...
3 if (condition) break;
4 ...
5 }
4.5.7 try
Try a part of code.
1 try{
2 ...
3 }
4 catch(...){
5 ...
6 }
4.5.8 catch
Catch an error, see try
1 for [i, ai : a]
If real[int] a(10, 11), then i=0:9, j=1:10 and aij is a reference to a(i, j).
See Implicit loop example.
4.6 I/O
See I/O example
See File stream example.
4.6.1 cout
Standard C++ output device (default: console).
4.6.2 cin
Standard C++ input device (default: keyboard).
4.6.3 endl
End of line.
4.6.4 ifstream
Open a file in read mode.
1 ifstream file("file.txt");
Note
4.6.5 ofstream
Open a file in write mode.
1 ofstream file("file.txt");
Note
4.6.6 append
Append data to an existing file.
4.6.7 binary
Write a file in binary.
4.6.8 seekg
Set the file position.
1 file.seekg(Pos);
4.6.9 tellg
Get the file position.
4.6.10 flush
Flush the buffer of the file.
1 file.flush
4.6.11 getline
Get the current line.
1 string s;
2 getline(file, s);
precision
Set the number of digits printed to the right of the decimal point. This applies to all subsequent floating point numbers
written to that output stream. However, this won’t make floating-point “integers” print with a decimal point. It’s
necessary to use fixed for that effect.
1 int np = f.precision(n)
scientific
Formats floating-point numbers in scientific notation
1 f.scientific
fixed
Used fixed point notation for floating-point numbers. Opposite of scientific.
1 f.fixed
showbase
Converts insertions to an external form that can be read according to the C++ lexical conventions for integral constants.
By default, showbase is not set.
1 f.showbase
noshowbase
Unset showbase flags.
1 f.noshowbase
showpos
Inserts a plus sign (+) into a decimal conversion of a positive integral value.
1 f.showpos
noshowpos
Unset showpos flags.
1 f.noshowpos
default
Reset all the previous flags to the default expect precision.
1 f.default
setw
Behaves as if member width were called with n as argument on the stream on which it is inserted as a manipulator (it
can be inserted on output streams).
1 f.setw(n)
4.7 Functions
4.7.1 abs
Return the absolute value.
1 real a = abs(b);
Parameters:
• b (int, real, complex, fespace function, real[int] or real[int, int])
Output:
• a (int, real, real[int] or real[int, int])
4.7.2 acos
arccos function.
Parameter:
• x (real, real[int] or real[int, int])
Output:
• theta (real, real[int] or real[int, int])
4.7.3 acosh
1 real theta = acosh(x);
(︁ √︀ )︁
arccosh(𝑥) = ln 𝑥 + 𝑥2 − 1
Parameter:
• x (real)
Output:
• theta (real)
4.7.4 adaptmesh
Mesh adaptation function.
1 mesh Thnew = adaptmesh(Th, [fx, fy], hmin=HMin, hmax=HMax, err=Err, errg=ErrG, nbvx=NbVx,
˓→ nbsmooth=NbSmooth, nbjacoby=NbJacoby, ratio=Ratio, omega=Omega, iso=Iso,␣
˓→periodic=Periodic);
Parameters:
• Th (mesh) Mesh to refine
• [fx, fy] (func or fespace function), scalar or vectorial Function to follow for the mesh adaptation
• hmin= (real) Minimum edge size
• hmax= (real) Maximum edge size
• err= (real) Error level (P1 interpolation)
• errg= (real) Relative geometrical error
• nbvx= (int) Maximum number of vertices
• nbsmooth= (int) Number of smoothing iterations
• nbjacoby= (int) Number of iterations for the smoothing procedure
• ratio= (real) Ratio of the triangles
• omega= (real) Relaxation parameter for the smoothing procedure
• iso= (bool) Isotropic adaptation (if true)
• abserror= (bool) Error (if true) - Relative error (if false)
• cutoff= (real) Lower limit of the relative error evaluation
• verbosity= (real) Verbosity level
• inquire= (bool) If true, inquire graphically
• splitpbedge= (bool) If true, split all internal edges in half
• maxsubdiv= (int) Bound the maximum subdivisions
• rescaling= (bool) Rescale the function in [0, 1]
• keepbackvertices= (bool) If true, try to keep vertices of the original mesh
• IsMetric= (bool) If true, the metric is defined explicitly
• power= (int) Exponent of the Hessian
4.7.5 adj
Adjacent triangle of the triangle 𝑘 by the edge 𝑒
1 int T = Th[k].adj(e);
Parameter:
• e (int) Edge number
Output:
• T (int) Triangle number
4.7.6 AffineCG
Affine conjugate gradient solver
Used to solve a problem like 𝐴𝑥 = 𝑏
Parameters:
• A (matrix) Matrix of the problem 𝐴𝑥 = 𝑏
• x (real[int]) Solution vector
• precon= (real[int]) Preconditionning function
• nbiter= (int) Maximum number of iterations
• eps= (real)
Convergence criterion
If 𝜀 > 0: test ||𝐴(𝑥)||𝑝 ≤ 𝜖||𝐴(𝑥0 )||𝑝
If 𝜀 < 0: test ||𝐴(𝑥)||2𝑝 ≤ |𝜖|
• veps= (real) Same as eps, but return -eps
• stop= (func) Convergence criterion as a function
Prototype is func bool StopFunc (int Iter, real[int] U, real[int] g)
u: current solution, g: current gradient (not preconditionned)
Output:
• Conv (int) 0: converged - !0: not converged
4.7.7 AffineGMRES
Affine GMRES solver
Parameters and output are the same as AffineCG
4.7.8 arg
Return the argument of a complex number.
1 real a = arg(c);
Parameters:
• c (complex)
Output:
• r (real)
4.7.9 asin
arcsin function.
Parameter:
• x (real, real[int] or real[int, int])
Output:
• theta (real, real[int] or real[int, int])
4.7.10 asinh
1 real theta = asinh(x);
(︁ √︀ )︁
arcsinh(𝑥) = ln 𝑥 + 𝑥2 + 1
Parameter:
• x (real)
Output:
• theta (real)
4.7.11 assert
Verify if a condition is true (same as C), if not the program stops.
1 assert(x==0)
Parameter:
• Boolean condition
Output:
• None
4.7.12 atan
arctan function.
Parameter:
• x (real)
Output:
• theta (real)
4.7.13 atan2
(︁ 𝑦 )︁
arctan function, returning the correct sign for 𝜃.
𝑥
1 real theta = atan2(y, x)
Parameter:
• x (real)
Output:
• theta (real)
4.7.14 atanh
1 real theta = atanh(x);
Parameter:
• x (real)
Output:
• theta (real)
4.7.15 atoi
Convert a string to an interger.
1 int a = atoi(s);
Parameter:
• s (string)
Output:
• a (int)
4.7.16 atof
Convert a string to a real.
1 real a = atof(s);
Parameter:
• s (string)
Output:
• a (real)
4.7.17 BFGS
Todo
todo
4.7.18 buildmesh
Build a 2D mesh using border elements.
Parameters:
• b1, b2, b3, b4 (border)
Geometry border, b1(nn) means b1 border discretized by nn vertices
• points (real[int, int]) [Optional]
Specify a set of points
The size of Points array is (nbp, 2), containing a set of nbp points with x and y coordinates
4.7.19 ceil
Round fractions up of 𝑥.
1 int c = ceil(x);
Parameter:
• x (real)
Output:
• c (int)
4.7.20 change
Change a property of a mesh.
Parameters:
• Th (mesh) Original mesh
• label= L (int[int]) Pair of old and new label
• region= R (int[int]) Pair of old and new region
• flabel= l (func int) Function of int given the new label
• fregion= r (func int) Function of int given the new region
Output:
• Thnew (mesh) Mesh with changed parameters
4.7.21 checkmovemesh
Check a movemesh without mesh generation.
Parameters:
Same as movemesh
Output:
• minT (real) Minimum triangle area
4.7.22 chi
Characteristic function of a mesh.
Parameters:
• Th (mesh or mesh3)
• x (real) Position 𝑥
• y (real) Position 𝑦
Output:
• IsInMesh (int) 1 if (𝑥, 𝑦) ∈ Th 0 if (𝑥, 𝑦) ̸∈ Th
4.7.23 clock
Get the clock in second.
1 real t = clock();
Parameter:
• None
Output:
• t (real) Current CPU time
4.7.24 complexEigenValue
Same as EigenValue for complex problems.
4.7.25 conj
Caculate the conjuguate of a complex number.
1 complex C1 = 1 + 1i;
2 complex C2 = conj(C1);
Parameter:
• C1 (complex) Complex number
Output:
• C2 (complex) Conjuguate of C1
4.7.26 convect
Characteristics Galerkin method.
ẋ𝜏 = u(x𝜏 )
x𝜏 = x
Parameters:
• ux (fespace function) Velocity: 𝑥 component
• uy (fespace function) Velocity: 𝑦 component
• uz (fespace function) 3D only
Velocity: 𝑧 component
• dt (real) Time step
• c (fespace function) Function to convect
Output:
• cgm (real) Result
4.7.27 copysign
C++ copysign function.
4.7.28 cos
cos function.
1 real x = cos(theta);
Parameters:
• theta (real or complex)
Output:
• x (real or complex)
4.7.29 cosh
cosh function.
1 real x = cosh(theta);
𝑒𝑥 + 𝑒−𝑥
cosh(𝑥) =
2
Parameters:
• theta (real)
Output:
• x (real)
4.7.30 diffnp
Arithmetic useful function.
4.7.31 diffpos
Arithmetic useful function.
4.7.32 dist
Arithmetic useful function.
4.7.33 dumptable
Show all types, operators and functions in FreeFEM.
1 dumptable(out);
Parameters:
• out (ostream) cout of ofstream file.
Output:
• None
4.7.34 dx
𝑥 derivative.
1 Uh up = dx(u);
𝜕𝑢
𝜕𝑥
Parameters:
• u (fespace function)
Output:
• up (fespace function)
4.7.35 dxx
𝑥 double derivative.
1 Uh upp = dxx(u);
𝜕2𝑢
𝜕𝑥2
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.36 dxy
𝑥𝑦 derivative.
1 Uh upp = dxy(u);
𝜕2𝑢
𝜕𝑥𝜕𝑦
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.37 dxz
𝑥𝑧 derivative.
1 Uh upp = dxz(u);
𝜕2𝑢
𝜕𝑥𝜕𝑧
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.38 dy
𝑦 derivative.
1 Uh up = dy(u);
𝜕𝑢
𝜕𝑦
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.39 dyx
𝑦𝑥 derivative.
1 Uh upp = dyx(u);
𝜕2𝑢
𝜕𝑦𝜕𝑥
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.40 dyy
𝑦 double derivative.
1 Uh upp = dyy(u);
𝜕2𝑢
𝜕𝑥2
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.41 dyz
𝑦𝑧 derivative.
1 Uh upp = dyz(u);
𝜕2𝑢
𝜕𝑦𝜕𝑧
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.42 dz
𝑧 derivative.
1 Uh up = dz(u);
𝜕𝑢
𝜕𝑧
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.43 dzx
𝑧𝑥 derivative.
1 Uh upp = dzx(u);
𝜕2𝑢
𝜕𝑧𝜕𝑥
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.44 dzy
𝑧𝑦 derivative.
1 Uh upp = dzy(u);
𝜕2𝑢
𝜕𝑧𝜕𝑦
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.45 dzz
𝑧 double derivative.
1 Uh upp = dzz(u);
𝜕2𝑢
𝜕𝑧 2
Parameters:
• u (fespace function)
Output:
• upp (fespace function)
4.7.46 EigenValue
Compute the generalized eigenvalue of 𝐴𝑢 = 𝜆𝐵𝑢. The shifted-inverse method is used by default with sigma=𝜎, the
shift of the method. The function EigenValue can be used for either matrices or functions returning a matrix vector
product. The use of the matrix version is shown below.
Parameters:
• A, B: matrices of same size
• nev=n: number of desired eigenvalues given by an integer n
• sym=: the problem is symmetric or not
• tol=: the relative accuracy to which eigenvalues are to be determined
• value=: an array to store the real part of the eigenvalues
• ivalue=: an array to store the imaginary part of the eigenvalues
• vector=: a Finite Element function array to store the eigenvectors
• sigma=: the shift value
• Other parameters are available for more advanced use: see the ARPACK documentation.
Output: The output is the number of converged eigenvalues, which can be different than the number of requested
eigenvalues given by nev=. Note that the eigenvalues and the eigenvectors are stored for further purposes using the
parameters value= and vector=.
4.7.47 emptymesh
Build an empty mesh.
Useful to handle Lagrange multipliers in mixed and Mortar methods.
Parameters:
• Th (mesh) Mesh to empty
• ssd (int[int]) Pseudo subregion label
Output:
• eTh (mesh) Empty mesh
4.7.48 erf
The error function:
∫︁ 𝑥
2
𝑒𝑟𝑓 (𝑥) = √ exp(−𝑡2 )𝑑𝑡
𝑝𝑖 0
Parameters:
• x (real)
Output:
• err (real)
4.7.49 erfc
Complementary of the error function:
Parameters:
• x (real)
Output:
• err (real)
4.7.50 exec
Execute an external command.
1 int v = exec(command);
Parameters:
• command (string) Command to execute
Output:
• v (int) Value returned by the command
4.7.51 exit
Exit function, equivalent to return.
1 exit(N);
Parameters:
• N (int) Return value
Output:
• None
4.7.52 exp
Exponential function.
1 real a = exp(b);
Parameters:
• b (real or complex)
Output:
• a (real or complex)
4.7.53 fdim
Positive difference (cmath function).
Parameters:
• a (real)
• b (real)
Output:
• fd (real) If 𝑥 > 𝑦, return 𝑥 − 𝑦If 𝑥 ≤ 𝑦, return 0
4.7.54 floor
Floor function.
1 real a = floor(b);
4.7.55 fmax
Maximum (cmath function).
Parameters:
• a (real)
• b (real)
Output:
• Max (real)
4.7.56 fmin
Minimum (cmath function).
Parameters:
• a (real)
• b (real)
Output:
• Min (real)
4.7.57 fmod
Remainder of 𝑎/𝑏 (cmath function).
Parameters:
• a (real)
• b (real)
Output:
• Mod (real)
4.7.58 imag
Imaginary part of a complex number.
1 complex c = 1. + 1i;
2 real Im = imag(c);
4.7.59 int1d
1D integral.
Used in problem, solve or varf definition to impose a boundary condition only (FreeFEM does not support 1D simu-
lation), or outside to calculate a quantity.
Parameters:
• Th (mesh) Mesh where the integral is calculated
• Label (int) [Optional]
Label of the 1D border Default: all borders of the mesh
• qfe= (quadrature formula) [Optional] (qf3E by default)
Quadrature formula, see quadrature formulae
• qforder= (quadrature formula) [Optional]
Quadrature order, see quadrature formulae
Output:
• Depending on the situation: In a problem, solve or varf definition: Non relevant.
Outside: real (example: real l = int1d(Th, 1)(1.);).
Warning
In a problem, solve or varf definition, the content of int1d must be a linear or bilinear form.
4.7.60 int2d
2D integral.
Or
Used in problem, solve or varf definition to: - Calculate integral in 2D simulation - Impose a boundary condition in
3D simulation Or outside to calculate a quantity.
Parameters:
• Th (mesh, mesh3 , meshS`or :freefem:`meshL) Mesh where the integral is calculated
• Region (int) [Optional] Label of the 2D region (2D simulation or Surface simulation) Default: all regions of
the mesh
• Label (int) [Optional] Label of the 2D border (3D simulation) Default: all borders of the mesh
• qft= (quadrature formula) [Optional] (qf5T by default)
Quadrature formula, see quadrature formulae
• qforder= (quadrature formula) [Optional]
Quadrature order, see quadrature formulae
Output:
• Depending on the situation: In a problem, solve or varf definition: Non relevant. Outside: real (example:
real s = int2d(Th, 1)(1.);).
Warning
In a problem, solve or varf definition, the content of the int2d must be a linear or bilinear form.
4.7.61 int3d
3D integral.
Used in problem, solve or varf definition to calculate integral in 3D simulation, or outside to calculate a quantity.
Parameters:
• Th (mesh3) Mesh where the integral is calculated
• Region (int) [Optional]
Label of the 3D region
Default: all regions of the mesh
Warning
In a problem, solve or varf definition, the content of the int3d must be a linear or bilinear form.
4.7.62 intalledges
Integral on all edges.
1 intalledges(Th, [Region])(
2 ...
3 )
Parameters:
• Th (mesh) Mesh where the integral is calculated
• Region (int) [Optional]
Label of the region
Default: all regions of the mesh
Output:
• Non relevant
4.7.63 intallfaces
Intergal on all faces.
Same as intalledges for mesh3.
4.7.64 interpolate
Interpolation operator from a finite element space to another.
Parameters:
• Wh (fespace) Target finite element space
• Vh (fespace) Original finite element space
• inside= (bool) If true, create a zero extension outside the Vh domain
• t= (bool) If true, return the transposed matrix
• op= (int) 0: interpolate the function (default value) 1: interpolate 𝜕𝑥 2: interpolate 𝜕𝑦 3: interpolate 𝜕𝑧
• U2Vc= (int[int]) Array of the same size of Wh describing which component of Vhis interpolated in Wh
Output:
• I (matrix) Interpolation matrix operator
4.7.65 invdiff
Arithmetic useful function.
4.7.66 invdiffnp
Arithmetic useful function.
4.7.67 invdiffpos
Arithmetic useful function.
4.7.68 isInf
The C++ isInf function.
1 bool b = isInf(a);
4.7.69 isNaN
The C++ isNan function.
1 bool b = isNaN(a);
4.7.70 isNormal
The C++ isNormal function.
4.7.71 j0
Bessel function of first kind, order 0.
1 real b = j0(x);
Parameters:
• x (real)
Output:
• b (real)
4.7.72 j1
Bessel function of first kind, order 1.
1 real b = j1(x);
Parameters:
• x (real)
Output:
• b (real)
4.7.73 jn
Bessel function of first kind, order n.
Parameters:
• n (int)
• x (real)
Output:
• b (real)
4.7.74 jump
Jump function across an edge.
1 intalledges(
2 ... jump(c) ...
3 )
Parameters:
• c (fespace function) Discontinuous function
Output:
• Non relevant
4.7.75 LinearCG
Linear CG solver
Parameters and output are the same as AffineCG
4.7.76 LinearGMRES
Linear GMRES solver
Parameters and output are the same as AffineCG
4.7.77 lgamma
Natural logarithm of the absolute value of the Γ function of 𝑥.
1 real lg = lgamma(x);
Parameters:
• x (real)
Output:
• lg (real)
4.7.78 log
Natural logarithm.
1 real l = log(x);
Parameters:
• x (real or complex)
Output:
• l (real or complex)
Note
Complex value
For complex value, the log function is defined as:
4.7.79 log10
Common logarithm.
1 real l = log10(x);
Parameters:
• x (real)
Output:
• l (real)
4.7.80 lrint
Integer value nearest to 𝑥.
1 int l = lrint(a);
Parameters:
• a (real)
Output:
• l (int)
4.7.81 lround
Round a value, and return an integer value.
1 int l = lround(a);
Parameters:
• a (real)
Output:
• l (int)
4.7.82 ltime
Return the current time since the Epcoh.
1 int t = ltime();
Parameter:
• None
Output:
• t (int)
4.7.83 max
Maximum value of two, three or four values.
Parameters:
• a (int or real)
• b (int or real)
• c (int or real) [Optional]
• d (int or real) [Optional]
Output:
• b (int or real)
4.7.84 min
Minimum value of two, three or four values.
Parameters:
• a (int or real)
• b (int or real)
• c (int or real) [Optional]
• d (int or real) [Optional]
Output:
• b (int or real)
4.7.85 movemesh
Move a mesh.
Parameters:
• Th (mesh of mesh3) Mesh to move
• Dx (fespace function) Displacement along 𝑥
• Dy (fespace function) Displacement along 𝑦
• Dz (fespace function) 3D only
Displacement along 𝑧
• region= (int) [Optional] 3D only
Set label to tetrahedra
• label= (int[int]) [Optional] 3D only
Set label of faces (see change for more information)
• facemerge= (int) [Optional] 3D only
If equal to 1, some faces can be merged during the mesh moving Default: 1
• ptmerge= (real) [Optional] 3D only
Criteria to define when two points merge
• orientation= (int) [Optional] 3D only
If equal to 1, allow orientation reverse if tetrahedra is not positive Default: 1
Output:
• MovedTh (mesh or mesh3) Moved mesh
4.7.86 NaN
C++ nan function.
1 real n = NaN([String]);
Parameters:
• String (string) Default: ""
4.7.87 NLCG
Non-linear conjugate gradient.
Parameters and output are the same as AffineCG
4.7.88 on
Dirichlet condition function.
1 problem (u, v)
2 ...
3 + on(Label, u=uD)
4 ...
Warning
Parameters:
• Label (int or border in 2D)
Boundary reference where to impose the Dirichlet condition
• uD (fespace function, func or real or int)
Dirichlet condition (u is an unknown of the problem)
Output:
• Non relevant
4.7.89 plot
Plot meshes and results.
Note
Only one of Th, u or [Ux, Uy] / [Ux, Uy, Uz] is needed for the plot command.
Parameters:
• Th (mesh or mesh3) Mesh to display
• u (fespace function) Scalar fespace function to display
• [Ux, Uy] / [Ux, Uy, Uz] (fespace function array) Vectorial fespace function to display
• [Ux, Uy] ([real[int], real[int]]) Couple a real array to display a curve
• wait= (bool) If true, wait before continue
• ps= (string) Name of the file to save the plot (.ps or .eps format)
4.7.90 polar
Polar coordinates.
Parameters:
• a (real)
• b (real)
Output:
• p (complex)
4.7.91 pow
Power function.
𝑝 = 𝑎𝑏
Parameters:
• a (real)
• b (real)
Output:
• p (real)
4.7.92 projection
Arithmetic useful function.
1 projection(a, b, x) = min(max(a, x), b)*(a < b) + min(max(b, x), a)*(1-(a < b));
Parameters:
• a (real)
• b (real)
• x (real)
Output:
• p (real)
4.7.93 randinit
Initialize the state vector by using a seed.
1 randinit(seed);
Parameters:
• seed (int)
Output:
• None
4.7.94 randint31
Generate unsigned int (31 bits) random number.
1 int r = randint31();
Parameters:
• None
Output:
• r (int)
4.7.95 randint32
Generate unsigned int (32 bits) random number.
1 int r = randint32();
Parameters:
• None
Output:
• r (int)
4.7.96 randreal1
Generate uniform real in [0, 1] (32 bits).
1 real r = randreal1();
Parameters:
• None
Output:
• r (real)
4.7.97 randreal2
Generate uniform real in [0, 1) (32 bits).
1 real r = randreal2();
Parameters:
• None
Output:
• r (real)
4.7.98 randreal3
Generate uniform real in (0, 1) (32 bits).
1 real r = randreal3();
Parameters:
• None
Output:
• r (real)
4.7.99 randres53
Generate uniform real in [0, 1) (53 bits).
1 real r = randres53();
Parameters:
• None
Output:
• r (real)
4.7.100 readmesh
Read a 2D mesh file at different formats (see Mesh Generation).
1 mesh Th = readmesh(MeshFileName);
Parameters:
• MeshFileName (string)
Output:
• Th (mesh)
4.7.101 readmesh3
Read a 3D mesh file at different formats (see Mesh Generation).
1 mesh3 Th = readmesh3(MeshFileName);
Parameters:
• MeshFileName (string)
Output:
• Th (mesh3)
4.7.102 real
Return the real part of a complex number.
1 real r = real(c);
Parameters:
• c (complex)
Output:
• r (real)
4.7.103 rint
Integer value nearest to 𝑥 (real value).
1 real r = rint(a);
Parameters:
• a (real)
Output:
• r (real)
4.7.104 round
Round a value (real value).
1 real r = round(a);
Parameters:
• a (real)
Output:
• r (real)
4.7.105 savemesh
Save a 2D or 3D mesh in different formats (see Mesh Generation 2D and Mesh Generation 3D).
1 savemesh(Th, MeshFileName);
Parameters:
• Th (mesh or mesh3)
• MeshFileName (string)
Output:
• None
4.7.106 set
Set a property to a matrix. See matrix.
4.7.107 sign
Sign of a value.
1 int s = sign(a);
Parameters:
• a (real or int)
Output:
• s (int)
4.7.108 signbit
C++ signbit function
1 int s = signbit(a);
4.7.109 sin
sin function.
1 real x = sin(theta);
Parameter:
• theta (real or complex)
Output:
• x (real or complex)
4.7.110 sinh
sinh function.
1 real x = sinh(theta);
𝑒𝑥 − 𝑒−𝑥
sinh(𝑥) =
2
Parameter:
• theta (real)
Output:
• x (real)
4.7.111 sort
Sort two array in parallel
1 sort(A, B);
Parameters:
• A (real[int])
• B (int[int])
Output:
• None
A is sorted in ascending order, B is sorted as A.
4.7.112 splitmesh
Split mesh triangles according to a function.
1 Th = splitmesh(Th0, f);
Parameters:
• Th0 (mesh)
• f (func or fespace function)
Output:
• Th (mesh)
4.7.113 sqrt
Square root
1 real s = sqrt(a);
Parameter:
• a (real)
Output:
• s (real)
4.7.114 square
1. Square of a number.
1 real S = square(a);
Parameter:
• a (real)
Output:
• S (real)
2. Build a structured square mesh.
Parameters:
• nnX (int) Discretization along 𝑥
• nnY (int) Discretization along 𝑦
• L (real) [Optional] Length along 𝑥
• H (real) [Optional] Height along 𝑦
• flags= (int) [Optional]
• label= (int[int]) [Optional]
• region= (int) [Optional]
Structured mesh type, see Mesh Generation chapter for more information
Output:
• Th (mesh)
4.7.115 storagetotal
1 int total = storagetotal();
4.7.116 storageused
1 int used = storageused();
4.7.117 strtod
C++ strtod function
Parameter:
• text (string)
Output:
• number (real)
4.7.118 strtol
C++ strtol function
Parameter:
• text (string)
4.7.119 swap
Swap values.
1 swap(a, b);
Parameters:
• a (real)
• b (real)
Output:
• None
4.7.120 system
Execute a system command.
Parameter:
• Command (string) System command
Output:
• Res (int) Value returned by the system command
Note
On Windows, the full path of the command is needed. For example, to execute ls.exe:
1 int Res = exec("C:\\cygwin\\bin\\ls.exe");
4.7.121 tan
tan function.
1 real x = tan(theta);
Parameter:
• theta (real)
Output:
• x (real)
4.7.122 tanh
tanh function.
1 real x = tanh(theta);
Parameter:
• theta (real)
Output:
• x (real)
4.7.123 tgamma
Calculate the Γ function of 𝑥.
1 real tg = tgamma(x);
Parameter:
• x (real)
Output:
• tg (real)
4.7.124 time
Return the current time (C++ function).
1 real t = time();
Parameter:
• None
Output:
• t (real)
4.7.125 trace
Matrix trace
Parameters:
• Matrix
Output:
• Trace of the matrix (real)
4.7.126 trunc
Split triangle of a mesh.
Parameters:
• Th0 (mesh)
• R (bool or int) Split triangles where R is true or different from 0
• split= (int) [Optional]
Level of splitting Default: 1
• label= (int) [Optional]
Label number of new boundary item Default: 1
Output:
• Th (mesh)
4.7.127 y0
Bessel function of second kind, order 0.
1 real B = y0(x);
Parameters:
• x (real)
Output:
• b (real)
4.7.128 y1
Bessel function of second kind, order 1.
1 real B = y1(x);
Parameters:
• x (real)
Output:
• b (real)
4.7.129 yn
Bessel function of second kind, order n.
Todo
todo
4.8.2 BEC
BECtrap
Todo
todo
GPvortex
Todo
todo
dxGPVortex
Todo
todo
dyGPVortex
Todo
todo
Todo
todo
LoadFlag
Todo
todo
SaveVec
Todo
todo
flag
Todo
todo
4.8.4 buildlayer
buildlayers
Todo
todo
4.8.5 ClosePoints
radiusSearch
Todo
todo
Voisinage
Todo
todo
neighborhood
Todo
todo
ClosePoints2
Todo
todo
ClosePoint
Todo
todo
ClosePoints1
Todo
todo
4.8.6 Curvature
extractborder
Extract a border of a mesh.
Parameters:
• Th (mesh or mesh3)
• Label (int) Label of the border to extract
• Points (real[int, int]) Extracted points Must be allocated as real[int, int] Points(3, 1);
Output:
• Res (real) Length of the extracted border
curvature
Todo
todo
raxicurvature
Todo
todo
curves
Todo
todo
setecurveabcisse
Todo
todo
equiparameter
Todo
todo
Tresca
Todo
todo
VonMises
Todo
todo
4.8.7 dfft
Refer to the FFTW documentation for more informations.
plandfft
Todo
todo
execute
Todo
todo
delete
Todo
todo
dfft
Todo
todo
map
Todo
todo
4.8.8 distance
Need
1 load "distance"
distance
Parameters:
• Th (mesh)
• d
• dist (real[int])
Output:
•
Todo
todo
checkdist
Todo
todo
4.8.9 DxWriter
Dxaddmesh
Todo
todo
Dxaddtimeseries
Todo
todo
Dxaddsol2ts
Todo
todo
4.8.10 Element_P1bl
expert
Todo
todo
4.8.11 exactpartition
exactpartition
Todo
todo
4.8.12 ff-AiryBiry
airy
Todo
todo
biry
Todo
todo
4.8.13 ff-cmaes
cmaes
Todo
todo
4.8.14 ff_gsl_awk
Refer to the GSL documentation for more informations
gslcdfugaussianP
Link to:
1 gsl_cdf_ugaussian_P(a)
gslcdfugaussianQ
Link to:
1 gsl_cdf_ugaussian_Q(a)
gslcdfugaussianPinv
Link to:
1 gsl_cdf_ugaussian_Pinv(a)
gslcdfugaussianQinv
Link to:
1 gsl_cdf_ugaussian_Qinv(a)
gslcdfgaussianP
Link to:
1 gsl_cdf_gaussian_P(a, b)
gslcdfgaussianQ
Link to:
1 gsl_cdf_gaussian_Q(a, b)
gslcdfgaussianPinv
Link to:
1 gsl_cdf_gaussian_Pinv(a, b)
gslcdfgaussianQinv
Link to:
1 gsl_cdf_gaussian_Qinv(a, b)
gslcdfgammaP
Link to:
1 gsl_cdf_gamma_P(a, b, c)
gslcdfgammaQ
Link to:
1 gsl_cdf_gamma_Q(a, b, c)
gslcdfgammaPinv
Link to:
1 gsl_cdf_gamma_Pinv(a, b, c)
gslcdfgammaQinv
Link to:
1 gsl_cdf_gamma_Pinv(a, b, c)
gslcdfcauchyP
Link to:
1 gsl_cdf_cauchy_P(a, b)
gslcdfcauchyQ
Link to:
1 gsl_cdf_cauchy_Q(a, b)
gslcdfcauchyPinv
Link to:
1 gsl_cdf_cauchy_Pinv(a, b)
gslcdfcauchyQinv
Link to:
1 gsl_cdf_cauchy_Qinv(a, b)
gslcdflaplaceP
Link to:
1 gsl_cdf_lapalce_P(a, b)
gslcdflaplaceQ
Link to:
1 gsl_cdf_lapalce_Q(a, b)
gslcdflaplacePinv
Link to:
1 gsl_cdf_lapalce_Pinv(a, b)
gslcdflaplaceQinv
Link to:
1 gsl_cdf_lapalce_Qinv(a, b)
gslcdfrayleighP
Link to:
1 gsl_cdf_rayleigh_P(a, b)
gslcdfrayleighQ
Link to:
1 gsl_cdf_rayleigh_Q(a, b)
gslcdfrayleighPinv
Link to:
1 gsl_cdf_rayleigh_Pinv(a, b)
gslcdfrayleighQinv
Link to:
1 gsl_cdf_rayleigh_Qinv(a, b)
gslcdfchisqP
Link to:
1 gsl_cdf_chisq_P(a, b)
gslcdfchisqQ
Link to:
1 gsl_cdf_chisq_Q(a, b)
gslcdfchisqPinv
Link to:
1 gsl_cdf_chisq_Pinv(a, b)
gslcdfchisqQinv
Link to:
1 gsl_cdf_chisq_Qinv(a, b)
gslcdfexponentialP
Link to:
1 gsl_cdf_exponential_P(a, b)
gslcdfexponentialQ
Link to:
1 gsl_cdf_exponential_Q(a, b)
gslcdfexponentialPinv
Link to:
1 gsl_cdf_exponential_Pinv(a, b)
gslcdfexponentialQinv
Link to:
1 gsl_cdf_exponential_Qinv(a, b)
gslcdfexppowP
Link to:
1 gsl_cdf_exppow_P(a, b, c)
gslcdfexppowQ
Link to:
1 gsl_cdf_exppow_Q(a, b, c)
gslcdftdistP
Link to:
1 gsl_cdf_t_dist_P(a, b)
gslcdftdistQ
Link to:
1 gsl_cdf_t_dist_Q(a, b)
gslcdftdistPinv
Link to:
1 gsl_cdf_t_dist_Pinv(a, b)
gslcdftdistQinv
Link to:
1 gsl_cdf_t_dist_Qinv(a, b)
gslcdffdistP
Link to:
1 gsl_cdf_fdist_P(a, b, c)
gslcdffdistQ
Link to:
1 gsl_cdf_fdist_Q(a, b, c)
gslcdffdistPinv
Link to:
1 gsl_cdf_fdist_Pinv(a, b, c)
gslcdffdistQinv
Link to:
1 gsl_cdf_fdist_Qinv(a, b, c)
gslcdfbetaP
Link to:
1 gsl_cdf_beta_P(a, b, c)
gslcdfbetaQ
Link to:
1 gsl_cdf_beta_Q(a, b, c)
gslcdfbetaPinv
Link to:
1 gsl_cdf_beta_Pinv(a, b, c)
gslcdfbetaQinv
Link to:
1 gsl_cdf_beta_Qinv(a, b, c)
gslcdfflatP
Link to:
1 gsl_cdf_flat_P(a, b, c)
gslcdfflatQ
Link to:
1 gsl_cdf_flat_Q(a, b, c)
gslcdfflatPinv
Link to:
1 gsl_cdf_flat_Pinv(a, b, c)
gslcdfflatQinv
Link to:
1 gsl_cdf_flat_Qinv(a, b, c)
gslcdflognormalP
Link to:
1 gsl_cdf_lognormal_P(a, b, c)
gslcdflognormalQ
Link to:
1 gsl_cdf_lognormal_Q(a, b, c)
gslcdflognormalPinv
Link to:
1 gsl_cdf_lognormal_Pinv(a, b, c)
gslcdflognormalQinv
Link to:
1 gsl_cdf_lognormal_Qinv(a, b, c)
gslcdfgumbel1P
Link to:
1 gsl_cdf_gumbel1_P(a, b, c)
gslcdfgumbel1Q
Link to:
1 gsl_cdf_gumbel1_Q(a, b, c)
gslcdfgumbel1Pinv
Link to:
1 gsl_cdf_gumbel1_Pinv(a, b, c)
gslcdfgumbel1Qinv
Link to:
1 gsl_cdf_gumbel1_Qinv(a, b, c)
gslcdfgumbel2P
Link to:
1 gsl_cdf_gumbel2_P(a, b, c)
gslcdfgumbel2Q
Link to:
1 gsl_cdf_gumbel2_Q(a, b, c)
gslcdfgumbel2Pinv
Link to:
1 gsl_cdf_gumbel2_Pinv(a, b, c)
gslcdfgumbel2Qinv
Link to:
1 gsl_cdf_gumbel2_Qinv(a, b, c)
gslcdfweibullP
Link to:
1 gsl_cdf_weibull_P(a, b, c)
gslcdfweibullQ
Link to:
1 gsl_cdf_weibull_Q(a, b, c)
gslcdfweibullPinv
Link to:
1 gsl_cdf_weibull_Pinv(a, b, c)
gslcdfweibullQinv
Link to:
1 gsl_cdf_weibull_Qinv(a, b, c)
gslcdfparetoP
Link to:
1 gsl_cdf_pareto_P(a, b, c)
gslcdfparetoQ
Link to:
1 gsl_cdf_pareto_Q(a, b, c)
gslcdfparetoPinv
Link to:
1 gsl_cdf_pareto_Pinv(a, b, c)
gslcdfparetoQinv
Link to:
1 gsl_cdf_pareto_Qinv(a, b, c)
gslcdflogisticP
Link to:
1 gsl_cdf_logistic_P(a, b)
gslcdflogisticQ
Link to:
1 gsl_cdf_logistic_Q(a, b)
gslcdflogisticPinv
Link to:
1 gsl_cdf_logistic_Pinv(a, b)
gslcdflogisticQinv
Link to:
1 gsl_cdf_logistic_Qinv(a, b)
gslcdfbinomialP
Link to:
1 gsl_cdf_binomial_P(a, b, c)
gslcdfbinomialQ
Link to:
1 gsl_cdf_binomial_Q(a, b, c)
gslcdfpoissonP
Link to:
1 gsl_cdf_poisson_P(a, b)
gslcdfpoissonQ
Link to:
1 gsl_cdf_poisson_Q(a, b)
gslcdfgeometricP
Link to:
1 gsl_cdf_geometric_P(a, b)
gslcdfgeometricQ
Link to:
1 gsl_cdf_geometric_Q(a, b)
gslcdfnegativebinomialP
Link to:
1 gsl_cdf_negative_binomial_P(a, b, c)
gslcdfnegativebinomialQ
Link to:
1 gsl_cdf_negative_binomial_Q(a, b, c)
gslcdfpascalP
Link to:
1 gsl_cdf_pascal_P(a, b, c)
gslcdfpascalQ
Link to:
1 gsl_cdf_pascal_Q(a, b, c)
gslranbernoullipdf
Link to:
1 gsl_ran_bernoulli_pdf(a, b)
gslranbeta
Link to:
1 gsl_ran_beta(a, b, c)
gslranbetapdf
Link to:
1 gsl_ran_beta_pdf(a, b, c)
gslranbinomialpdf
Link to:
1 gsl_ran_binomial_pdf(a, b, c)
gslranexponential
Link to:
1 gsl_ran_exponential(a, b)
gslranexponentialpdf
Link to:
1 gsl_ran_exponential_pdf(a, b)
gslranexppow
Link to:
1 gsl_ran_exppow(a, b, c)
gslranexppowpdf
Link to:
1 gsl_ran_exppow_pdf(a, b, c)
gslrancauchy
Link to:
1 gsl_ran_cauchy(a, b)
gslrancauchypdf
Link to:
1 gsl_ran_cauchy_pdf(a, b)
gslranchisq
Link to:
1 gsl_ran_chisq(a, b)
gslranchisqpdf
Link to:
1 gsl_ran_chisq_pdf(a, b)
gslranerlang
Link to:
1 gsl_ran_erlang(a, b, c)
gslranerlangpdf
Link to:
1 gsl_ran_erlang_pdf(a, b, c)
gslranfdist
Link to:
1 gsl_ran_fdist(a, b, c)
gslranfdistpdf
Link to:
1 gsl_ran_fdist_pdf(a, b, c)
gslranflat
Link to:
1 gsl_ran_flat(a, b, c)
gslranflatpdf
Link to:
1 gsl_ran_flat_pdf(a, b, c)
gslrangamma
Link to:
1 gsl_ran_gamma(a, b, c)
gslrangammaint
Link to:
1 gsl_ran_gamma_int(a, b, c)
gslrangammapdf
Link to:
1 gsl_ran_gamma_pdf(a, b, c)
gslrangammamt
Link to:
1 gsl_ran_gamma_mt(a, b, c)
gslrangammaknuth
Link to:
1 gsl_ran_gamma_knuth(a, b, c)
gslrangaussian
Link to:
1 gsl_ran_gaussian(a, b)
gslrangaussianratiomethod
Link to:
1 gsl_ran_gaussian_ratio_method(a, b)
gslrangaussianziggurat
Link to:
1 gsl_ran_gaussian_ziggurat(a, b)
gslrangaussianpdf
Link to:
1 gsl_ran_gaussian_pdf(a, b)
gslranugaussian
Link to:
1 gsl_ran_ugaussian(a)
gslranugaussianratiomethod
Link to:
1 gsl_ran_ugaussian_ratio_method(a)
gslranugaussianpdf
Link to:
1 gsl_ran_ugaussian_pdf(a)
gslrangaussiantail
Link to:
1 gsl_ran_gaussian_tail(a, b, c)
gslrangaussiantailpdf
Link to:
1 gsl_ran_gaussian_tail_pdf(a, b, c)
gslranugaussiantail
Link to:
1 gsl_ran_ugaussian_tail(a, b)
gslranugaussiantailpdf
Link to:
1 gsl_ran_ugaussian_tail_pdf(a, b)
gslranlandau
Link to:
1 gsl_ran_landau(a)
gslranlandaupdf
Link to:
1 gsl_ran_landau_pdf(a)
gslrangeometricpdf
Link to:
1 gsl_ran_geometric_pdf(a, b)
gslrangumbel1
Link to:
1 gsl_ran_gumbel1(a, b, c)
gslrangumbel1pdf
Link to:
1 gsl_ran_gumbel1_pdf(a, b, c)
gslrangumbel2
Link to:
1 gsl_ran_gumbel2(a, b, c)
gslrangumbel2pdf
Link to:
1 gsl_ran_gumbel2_pdf(a, b, c)
gslranlogistic
Link to:
1 gsl_ran_logistic(a, b)
gslranlogisticpdf
Link to:
1 gsl_ran_logistic_pdf(a, b)
gslranlognormal
Link to:
1 gsl_ran_lognormal(a, b, c)
gslranlognormalpdf
Link to:
1 gsl_ran_lognormal_pdf(a, b, c)
gslranlogarithmicpdf
Link to:
1 gsl_ran_logarithmic_pdf(a, b)
gslrannegativebinomialpdf
Link to:
1 gsl_ran_negative_binomial_pdf(a, b, c)
gslranpascalpdf
Link to:
1 gsl_ran_pascal_pdf(a, b, c)
gslranpareto
Link to:
1 gsl_ran_pareto(a, b, c)
gslranparetopdf
Link to:
1 gsl_ran_pareto_pdf(a, b, c)
gslranpoissonpdf
Link to:
1 gsl_ran_poisson_pdf(a, b)
gslranrayleigh
Link to:
1 gsl_ran_rayleigh(a, b)
gslranrayleighpdf
Link to:
1 gsl_ran_rayleigh_pdf(a, b)
gslranrayleightail
Link to:
1 gsl_ran_rayleigh_tail(a, b, c)
gslranrayleightailpdf
Link to:
1 gsl_ran_rayleigh_tail_pdf(a, b, c)
gslrantdist
Link to:
1 gsl_ran_tdsit(a, b)
gslrantdistpdf
Link to:
1 gsl_ran_tdsit_pdf(a, b)
gslranlaplace
Link to:
1 gsl_ran_laplace(a, b)
gslranlaplacepdf
Link to:
1 gsl_ran_laplace_pdf(a, b)
gslranlevy
Link to:
1 gsl_ran_levy(a, b, c)
gslranweibull
Link to:
1 gsl_ran_weibull(a, b, c)
gslranweibullpdf
Link to:
1 gsl_ran_weibull_pdf(a, b, c)
gslsfairyAi
Link to:
1 gsl_sf_airy_Ai(a, b)
gslsfairyBi
Link to:
1 gsl_sf_airy_Bi(a, b)
gslsfairyAiscaled
Link to:
1 gsl_sf_airy_Ai_scaled(a, b)
gslsfairyBiscaled
Link to:
1 gsl_sf_airy_Bi_scaled(a, b)
gslsfairyAideriv
Link to:
1 gsl_sf_airy_Ai_deriv(a, b)
gslsfairyBideriv
Link to:
1 gsl_sf_airy_Bi_deriv(a, b)
gslsfairyAiderivscaled
Link to:
1 gsl_sf_airy_Ai_deriv_scaled(a, b)
gslsfairyBiderivscaled
Link to:
1 gsl_sf_airy_Bi_deriv_scaled(a, b)
gslsfairyzeroAi
Link to:
1 gsl_sf_airy_Ai(a, b)
gslsfairyzeroBi
Link to:
1 gsl_sf_airy_aero_Bi(a)
gslsfairyzeroAideriv
Link to:
1 gsl_sf_airy_aero_Ai_deriv(a)
gslsfairyzeroBideriv
Link to:
1 gsl_sf_airy_aero_Bi_deriv(a)
gslsfbesselJ0
Link to:
1 gsl_sf_bessel_J0(a)
gslsfbesselJ1
Link to:
1 gsl_sf_bessel_J1(a)
gslsfbesselJn
Link to:
1 gsl_sf_bessel_Jn(a, b)
gslsfbesselY0
Link to:
1 gsl_sf_bessel_Y0(a)
gslsfbesselY1
Link to:
1 gsl_sf_bessel_Y1(a)
gslsfbesselYn
Link to:
1 gsl_sf_bessel_Yn(a, b)
gslsfbesselI0
Link to:
1 gsl_sf_bessel_I0(a)
gslsfbesselI1
Link to:
1 gsl_sf_bessel_I1(a)
gslsfbesselIn
Link to:
1 gsl_sf_bessel_In(a, b)
gslsfbesselI0scaled
Link to:
1 gsl_sf_bessel_I0_scaled(a)
gslsfbesselI1scaled
Link to:
1 gsl_sf_bessel_I1_scaled(a)
gslsfbesselInscaled
Link to:
1 gsl_sf_bessel_In_scaled(a, b)
gslsfbesselK0
Link to:
1 gsl_sf_bessel_K0(a)
gslsfbesselK1
Link to:
1 gsl_sf_bessel_K1(a)
gslsfbesselKn
Link to:
1 gsl_sf_bessel_Kn(a, b)
gslsfbesselK0scaled
Link to:
1 gsl_sf_bessel_K0_scaled(a)
gslsfbesselK1scaled
Link to:
1 gsl_sf_bessel_K1_scaled(a)
gslsfbesselKnscaled
Link to:
1 gsl_sf_bessel_Kn_scaled(a, b)
gslsfbesselj0
Link to:
1 gsl_sf_bessel_j0(a)
gslsfbesselj1
Link to:
1 gsl_sf_bessel_j1(a)
gslsfbesselj2
Link to:
1 gsl_sf_bessel_j2(a)
gslsfbesseljl
Link to:
1 gsl_sf_bessel_jl(a, b)
gslsfbessely0
Link to:
1 gsl_sf_bessel_y0(a)
gslsfbessely1
Link to:
1 gsl_sf_bessel_y0(a)
gslsfbessely2
Link to:
1 gsl_sf_bessel_y0(a)
gslsfbesselyl
Link to:
1 gsl_sf_bessel_jl(a, b)
gslsfbesseli0scaled
Link to:
1 gsl_sf_bessel_i0_scaled(a)
gslsfbesseli1scaled
Link to:
1 gsl_sf_bessel_i1_scaled(a)
gslsfbesseli2scaled
Link to:
1 gsl_sf_bessel_i2_scaled(a)
gslsfbesselilscaled
Link to:
1 gsl_sf_bessel_il_scaled(a, b)
gslsfbesselk0scaled
Link to:
1 gsl_sf_bessel_k0_scaled(a)
gslsfbesselk1scaled
Link to:
1 gsl_sf_bessel_k1_scaled(a)
gslsfbesselk2scaled
Link to:
1 gsl_sf_bessel_k2_scaled(a)
gslsfbesselklscaled
Link to:
1 gsl_sf_bessel_kl_scaled(a, b)
gslsfbesselJnu
Link to:
1 gsl_sf_bessel_Jnu(a, b)
gslsfbesselYnu
Link to:
1 gsl_sf_bessel_Ynu(a, b)
gslsfbesselInuscaled
Link to:
1 gsl_sf_bessel_Inu_scaled(a, b)
gslsfbesselInu
Link to:
1 gsl_sf_bessel_Inu(a, b)
gslsfbesselKnuscaled
Link to:
1 gsl_sf_bessel_Knu_scaled(a, b)
gslsfbesselKnu
Link to:
1 gsl_sf_bessel_Knu(a, b)
gslsfbessellnKnu
Link to:
1 gsl_sf_bessel_lnKnu(a, b)
gslsfbesselzeroJ0
Link to:
1 gsl_sf_bessel_zero_J0(a)
gslsfbesselzeroJ1
Link to:
1 gsl_sf_bessel_zero_J1(a)
gslsfbesselzeroJnu
Link to:
1 gsl_sf_bessel_zero_Jnu(a, b)
gslsfclausen
Link to:
1 gsl_sf_clausen(a)
gslsfhydrogenicR1
Link to:
1 gsl_sf_hydrogenicR_1(a, b)
gslsfdawson
Link to:
1 gsl_sf_dawnson(a)
gslsfdebye1
Link to:
1 gsl_sf_debye_1(a)
gslsfdebye2
Link to:
1 gsl_sf_debye_2(a)
gslsfdebye3
Link to:
1 gsl_sf_debye_3(a)
gslsfdebye4
Link to:
1 gsl_sf_debye_4(a)
gslsfdebye5
Link to:
1 gsl_sf_debye_5(a)
gslsfdebye6
Link to:
1 gsl_sf_debye_6(a)
gslsfdilog
Link to:
1 gsl_sf_dilog(a)
gslsfmultiply
Link to:
1 gsl_sf_multiply(a, b)
gslsfellintKcomp
Link to:
1 gsl_sf_ellint_Kcomp(a, b)
gslsfellintEcomp
Link to:
1 gsl_sf_ellint_Ecomp(a, b)
gslsfellintPcomp
Link to:
1 gsl_sf_ellint_Pcomp(a, b, c)
gslsfellintDcomp
Link to:
1 gsl_sf_ellint_Dcomp(a, b)
gslsfellintF
Link to:
1 gsl_sf_ellint_F(a, b, c)
gslsfellintE
Link to:
1 gsl_sf_ellint_E(a, b, c)
gslsfellintRC
Link to:
1 gsl_sf_ellint_RC(a, b, c)
gslsferfc
Link to:
1 gsl_sf_erfc(a)
gslsflogerfc
Link to:
1 gsl_sf_log_erfc(a)
gslsferf
Link to:
1 gsl_sf_erf(a)
gslsferfZ
Link to:
1 gsl_sf_erf_Z(a)
gslsferfQ
Link to:
1 gsl_sf_erf_Q(a)
gslsfhazard
Link to:
1 gsl_sf_hazard(a)
gslsfexp
Link to:
1 gsl_sf_exp(a)
gslsfexpmult
Link to:
1 gsl_sf_exp_mult(a, b)
gslsfexpm1
Link to:
1 gsl_sf_expm1(a)
gslsfexprel
Link to:
1 gsl_sf_exprel(a)
gslsfexprel2
Link to:
1 gsl_sf_exprel_2(a)
gslsfexpreln
Link to:
1 gsl_sf_exprel_n(a, b)
gslsfexpintE1
Link to:
1 gsl_sf_expint_E1(a)
gslsfexpintE2
Link to:
1 gsl_sf_expint_E2(a)
gslsfexpintEn
Link to:
1 gsl_sf_expint_En(a, b)
gslsfexpintE1scaled
Link to:
1 gsl_sf_expint_E1_scaled(a)
gslsfexpintE2scaled
Link to:
1 gsl_sf_expint_E1_scaled(a)
gslsfexpintEnscaled
Link to:
1 gsl_sf_expint_En_scaled(a, b)
gslsfexpintEi
Link to:
1 gsl_sf_expint_Ei(a)
gslsfexpintEiscaled
Link to:
1 gsl_sf_expint_Ei_scaled(a)
gslsfShi
Link to:
1 gsl_sf_Shi(a)
gslsfChi
Link to:
1 gsl_sf_Chi(a)
gslsfexpint3
Link to:
1 gsl_sf_expint_3(a)
gslsfSi
Link to:
1 gsl_sf_Si(a)
gslsfCi
Link to:
1 gsl_sf_Ci(a)
gslsfatanint
Link to:
1 gsl_sf_atanint(a)
gslsffermidiracm1
Link to:
1 gsl_sf_fermi_dirac_m1(a)
gslsffermidirac0
Link to:
1 gsl_sf_fermi_dirac_0(a)
gslsffermidirac1
Link to:
1 gsl_sf_fermi_dirac_1(a)
gslsffermidirac2
Link to:
1 gsl_sf_fermi_dirac_2(a)
gslsffermidiracint
Link to:
1 gsl_sf_fermi_dirac_int(a, b)
gslsffermidiracmhalf
Link to:
1 gsl_sf_fermi_dirac_mhalf(a)
gslsffermidirachalf
Link to:
1 gsl_sf_fermi_dirac_half(a)
gslsffermidirac3half
Link to:
1 gsl_sf_fermi_dirac_3half(a)
gslsffermidiracinc0
Link to:
1 gsl_sf_fermi_dirac_inc_0(a, b)
gslsflngamma
Link to:
1 gsl_sf_lngamma(a)
gslsfgamma
Link to:
1 gsl_sf_gamma(a)
gslsfgammastar
Link to:
1 gsl_sf_gammastar(a)
gslsfgammainv
Link to:
1 gsl_sf_gammainv(a)
gslsftaylorcoeff
Link to:
1 gsl_sf_taylorcoeff(a, b)
gslsffact
Link to:
1 gsl_sf_fact(a)
gslsfdoublefact
Link to:
1 gsl_sf_doublefact(a)
gslsflnfact
Link to:
1 gsl_sf_lnfact(a)
gslsflndoublefact
Link to:
1 gsl_sf_lndoublefact(a)
gslsflnchoose
Link to:
1 gsl_sf_lnchoose(a, b)
gslsfchoose
Link to:
1 gsl_sf_choose(a, b)
gslsflnpoch
Link to:
1 gsl_sf_lnpoch(a, b)
gslsfpoch
Link to:
1 gsl_sf_poch(a, b)
gslsfpochrel
Link to:
1 gsl_sf_pochrel(a, b)
gslsfgammaincQ
Link to:
1 gsl_sf_gamma_inc_Q(a, b)
gslsfgammaincP
Link to:
1 gsl_sf_gamma_inc_P(a, b)
gslsfgammainc
Link to:
1 gsl_sf_gamma_inc(a, b)
gslsflnbeta
Link to:
1 gsl_sf_lnbeta(a, b)
gslsfbeta
Link to:
1 gsl_sf_beta(a, b)
gslsfbetainc
Link to:
1 gsl_sf_betaçinc(a, b, c)
gslsfgegenpoly1
Link to:
1 gsl_sf_gegenpoly_1(a, b)
gslsfgegenpoly2
Link to:
1 gsl_sf_gegenpoly_2(a, b)
gslsfgegenpoly3
Link to:
1 gsl_sf_gegenpoly_3(a, b)
gslsfgegenpolyn
Link to:
1 gsl_sf_gegenpoly_n(a, b, c)
gslsfhyperg0F1
Link to:
1 gsl_sf_hyperg_0F1(a, b)
gslsfhyperg1F1int
Link to:
1 gsl_sf_hyperg_1F1_inc(a, b, c)
gslsfhyperg1F1
Link to:
1 gsl_sf_hyperg_1F1(a, b, c)
gslsfhypergUint
Link to:
1 gsl_sf_hyperg_U_inc(a, b, c)
gslsfhypergU
Link to:
1 gsl_sf_hyperg_U(a, b, c)
gslsfhyperg2F0
Link to:
1 gsl_sf_hyperg_U_2F0(a, b, c)
gslsflaguerre1
Link to:
1 gsl_sf_laguerre_1(a, b)
gslsflaguerre2
Link to:
1 gsl_sf_laguerre_2(a, b)
gslsflaguerre3
Link to:
1 gsl_sf_laguerre_3(a, b)
gslsflaguerren
Link to:
1 gsl_sf_laguerre_n(a, b, c)
gslsflambertW0
Link to:
1 gsl_sf_lambert_W0(a)
gslsflambertWm1
Link to:
1 gsl_sf_lambert_Wm1(a)
gslsflegendrePl
Link to:
1 gsl_sf_legendre_Pl(a, b)
gslsflegendreP1
Link to:
1 gsl_sf_legendre_P1(a)
gslsflegendreP2
Link to:
1 gsl_sf_legendre_P2(a)
gslsflegendreP3
Link to:
1 gsl_sf_legendre_P3(a)
gslsflegendreQ0
Link to:
1 gsl_sf_legendre_Q0(a)
gslsflegendreQ1
Link to:
1 gsl_sf_legendre_Q1(a)
gslsflegendreQl
Link to:
1 gsl_sf_legendre_Ql(a, b)
gslsflegendrePlm
Link to:
1 gsl_sf_legendre_Plm(a, b, c)
gslsflegendresphPlm
Link to:
1 gsl_sf_legendre_sphP1m(a, b, c)
gslsflegendrearraysize
Link to:
1 gsl_sf_legendre_array_size(a, b)
gslsfconicalPhalf
Link to:
1 gsl_sf_conicalP_half(a, b)
gslsfconicalPmhalf
Link to:
1 gsl_sf_conicalP_mhalf(a, b)
gslsfconicalP0
Link to:
1 gsl_sf_conicalP_0(a, b)
gslsfconicalP1
Link to:
1 gsl_sf_conicalP_1(a, b)
gslsfconicalPsphreg
Link to:
1 gsl_sf_conicalP_sph_reg(a, b, c)
gslsfconicalPcylreg
Link to:
1 gsl_sf_conicalP_cyl_reg(a, b, c)
gslsflegendreH3d0
Link to:
1 gsl_sf_legendre_H3d_0(a, b)
gslsflegendreH3d1
Link to:
1 gsl_sf_legendre_H3d_1(a, b)
gslsflegendreH3d
Link to:
1 gsl_sf_legendre_H3d(a, b, c)
gslsflog
Link to:
1 gsl_sf_log(a)
gslsflogabs
Link to:
1 gsl_sf_log_abs(a)
gslsflog1plusx
Link to:
1 gsl_sf_log_1plusx(a)
gslsflog1plusxmx
Link to:
1 gsl_sf_log_1plusx_mx(a)
gslsfpowint
Link to:
1 gsl_sf_pow_int(a, b)
gslsfpsiint
Link to:
1 gsl_sf_psi_int(a)
gslsfpsi
Link to:
1 gsl_sf_psi(a)
gslsfpsi1piy
Link to:
1 gsl_sf_psi_1piy(a)
gslsfpsi1int
Link to:
1 gsl_sf_psi_1_int(a)
gslsfpsi1
Link to:
1 gsl_sf_psi_1(a)
gslsfpsin
Link to:
1 gsl_sf_psi_n(a, b)
gslsfsynchrotron1
Link to:
1 gsl_sf_synchrotron_1(a)
gslsfsynchrotron2
Link to:
1 gsl_sf_synchrotron_2(a)
gslsftransport2
Link to:
1 gsl_sf_transport_2(a)
gslsftransport3
Link to:
1 gsl_sf_transport_3(a)
gslsftransport4
Link to:
1 gsl_sf_transport_4(a)
gslsftransport5
Link to:
1 gsl_sf_transport_5(a)
gslsfsin
Link to:
1 gsl_sf_sin(a)
gslsfcos
Link to:
1 gsl_sf_cos(a)
gslsfhypot
Link to:
1 gsl_sf_hypot(a, b)
gslsfsinc
Link to:
1 gsl_sf_sinc(a)
gslsflnsinh
Link to:
1 gsl_sf_lnsinh(a)
gslsflncosh
Link to:
1 gsl_sf_lncosh(a)
gslsfanglerestrictsymm
Link to:
1 gsl_sf_andle_restrict_symm(a)
gslsfanglerestrictpos
Link to:
1 gsl_sf_angle_restrict_pos(a)
gslsfzetaint
Link to:
1 gsl_sf_zeta_int(a)
gslsfzeta
Link to:
1 gsl_sf_zeta(a)
gslsfzetam1
Link to:
1 gsl_sf_zetam1(a)
gslsfzetam1int
Link to:
1 gsl_sf_zetam1_int(a)
gslsfhzeta
Link to:
1 gsl_sf_hzeta(a, b)
gslsfetaint
Link to:
1 gsl_sf_eta_int(a)
gslsfeta
Link to:
1 gsl_sf_eta(a)
4.8.15 ff-Ipopt
Refer to the Ipopt documentation for more informations.
IPOPT
Todo
todo
4.8.16 fflapack
Refer to the LAPACK documentation for more informations.
inv
Todo
todo
dgeev
Todo
todo
zgeev
Todo
todo
geev
Todo
todo
geev
Todo
todo
dggev
Todo
todo
zggev
Todo
todo
dsygvd
Todo
todo
dgesdd
Todo
todo
zhegv
Todo
todo
dsyev
Todo
todo
zheev
Todo
todo
4.8.17 ff-mmap-semaphore
Wait
Todo
todo
trywait
Todo
todo
Post
Todo
todo
msync
Todo
todo
Read
Todo
todo
Write
Todo
todo
4.8.18 ffnewuoa
newuoa
Todo
todo
4.8.19 ff-NLopt
Refer to the NLOPT documentation for more informations.
nloptDIRECT
Todo
todo
nloptDIRECTL
Todo
todo
nloptDIRECTLRand
Todo
todo
nloptDIRECTScal
Todo
todo
nloptDIRECTNoScal
Todo
todo
nloptDIRECTLNoScal
Todo
todo
nloptDIRECTLRandNoScal
Todo
todo
nloptOrigDIRECT
Todo
todo
nloptOrigDIRECTL
Todo
todo
nloptStoGO
Todo
todo
nloptStoGORand
Todo
todo
nloptLBFGS
Todo
todo
nloptPRAXIS
Todo
todo
nloptVar1
Todo
todo
nloptVar2
Todo
todo
nloptTNewton
Todo
todo
nloptTNewtonRestart
Todo
todo
nloptTNewtonPrecond
Todo
todo
nloptTNewtonPrecondRestart
Todo
todo
nloptCRS2
Todo
todo
nloptMMA
Todo
todo
nloptCOBYLA
Todo
todo
nloptNEWUOA
Todo
todo
nloptNEWUOABound
Todo
todo
nloptNelderMead
Todo
todo
nloptSbplx
Todo
todo
nloptBOBYQA
Todo
todo
nloptISRES
Todo
todo
nloptSLSQP
Todo
todo
nloptMLSL
Todo
todo
nloptMLSLLDS
Todo
todo
nloptAUGLAG
Todo
todo
nloptAUGLAGEQ
Todo
todo
4.8.20 ffrandom
srandomdev
Todo
todo
srandom
Todo
todo
random
Todo
todo
4.8.21 FreeFemQA
MeshGenQA
Todo
todo
4.8.22 freeyams
freeyams
Todo
todo
4.8.23 gmsh
Need
1 load "gmsh"
gmshload
Load a 2D mesh build with Gmsh.
Parameters:
• MeshFile (string) Mesh file name
• reftri= (.. todo:: todo)
• renum= (.. todo:: todo)
Output:
• Th (mesh)
gmshload3
Load a 3D mesh build with Gmsh.
Parameters:
• MeshFile (string) Mesh file name
• reftet= (.. todo:: todo)
• renum= (.. todo:: todo)
Output:
• Th (mesh3)
savegmsh
Todo
todo
4.8.24 gsl
gslpolysolvequadratic
Todo
todo
gslpolysolvecubic
Todo
todo
gslpolycomplexsolve
Todo
todo
gslrnguniform
Todo
todo
gslrnguniformpos
Todo
todo
gslname
Todo
todo
gslrngget
Todo
todo
gslrngmin
Todo
todo
gslrngmax
Todo
todo
gslrngset
Todo
todo
gslrngtype
Todo
todo
4.8.25 ilut
applyIlutPrecond
Todo
todo
makeIlutPrecond
Todo
todo
4.8.26 iohdf5
savehdf5sol
Todo
todo
4.8.27 iovtk
savevtk
Save mesh or solution in vtk/vtu format.
Parameters:
• FileName (string) File name: *.vtk or *.vtu
• Th (mesh or mesh3)
• [Ux, Uy, Uz], p (fespace function of vector of fespace functions) Solutions to save, as much as wanted
• dataname= (string) Name of solutions, seprated by a space
• withsurfacemesh= (bool) .. todo:: todo
• order= (int[int]) Order of solutions.
Available: 0 or 1
• floatmesh= (bool) .. todo:: todo
vtkload
Todo
todo
vtkload3
Todo
todo
4.8.28 isoline
Need
1 load "isoline"
isoline
Todo
todo
Curve
Todo
todo
Area
Todo
todo
findallocalmin
Todo
todo
4.8.29 lapack
inv
Todo
todo
dgeev
Todo
todo
zgeev
Todo
todo
geev
Todo
todo
dggev
Todo
todo
zggev
Todo
todo
dsygvd
Todo
todo
dgesdd
Todo
todo
zhegv
Todo
todo
dsyev
Todo
todo
zheev
Todo
todo
dgelsy
Todo
todo
4.8.30 lgbmo
bmo
Todo
todo
4.8.31 mat_dervieux
MatUpWind1
Todo
todo
4.8.32 mat_psi
MatUpWind0
Todo
todo
4.8.33 medit
medit
Todo
todo
savesol
Todo
todo
readsol
Todo
todo
4.8.34 metis
metisnodal
Todo
todo
metisdual
Todo
todo
4.8.35 MetricKuate
MetricKuate
Todo
todo
4.8.36 MetricPk
MetricPk
Todo
todo
4.8.37 mmg3d
mmg3d
Todo
todo
4.8.38 mmg3d-v4.0
mmg3d
Todo
todo
4.8.39 msh3
change
Todo
todo
movemesh23
Todo
todo
movemesh2D3Dsurf
Todo
todo
movemesh3
Todo
todo
movemesh
Todo
todo
movemesh3D
Todo
todo
deplacement
Todo
todo
checkbemesh
Todo
todo
buildlayers
Todo
todo
bcube
Todo
todo
cube
Construct a cubic mesh.
Parameters:
• nnX (int) Number of discretization point along 𝑥
• nnY (int) Number of discretization point along 𝑦
• nnZ (int) Number of discretization point along 𝑧
• X(x) (func) [Optional] Affine function of 𝑥 to define the length Default: x
• Y(y) (func) [Optional] Affine function of 𝑦 to define the width Default: y
• Z(z) (func) [Optional] Affine function of 𝑧 to define the height Default: z
• label= (int[int]) [Optional]
List of surface labels Default: [1, 2, 3, 4, 5, 6]
• flags= (int) [Optional]
Refer to square
• region= (int) [Optional]
Region number of the cube volume Default: 0
Output:
• Th (mesh3) Cube mesh
trunc
Todo
todo
gluemesh
Todo
todo
extract
Todo
todo
showborder
Todo
todo
getborder
Todo
todo
AddLayers
Todo
todo
4.8.40 mshmet
mshmet
Todo
todo
4.8.41 MUMPS
defaulttoMUMPSseq
Todo
todo
4.8.42 MUMPS_seq
defaulttoMUMPSseq
Todo
todo
4.8.43 netgen
netg
Todo
todo
netgstl
Todo
todo
netgload
Todo
todo
4.8.44 NewSolver
defaulttoUMFPACK
Todo
todo
4.8.45 PARDISO
defaulttoPARDISO
Todo
todo
ompsetnumthreads
Todo
todo
ompgetnumthreads
Todo
todo
ompgetmaxthreads
Todo
todo
4.8.46 pcm2rnm
readpcm
Todo
todo
4.8.47 pipe
flush
Todo
todo
sleep
Todo
todo
usleep
Todo
todo
4.8.48 qf11to25
QF1d
Todo
todo
QF2d
Todo
todo
QF3d
Todo
todo
tripleQF
4.8.49 scotch
scotch
Todo
todo
4.8.50 shell
readdir
Todo
todo
unlink
Todo
todo
rmdir
Todo
todo
cddir
Todo
todo
chdir
Todo
todo
basename
Todo
todo
dirname
Todo
todo
mkdir
Todo
todo
chmod
Todo
todo
cpfile
Todo
todo
stat
Todo
todo
isdir
Todo
todo
getenv
Todo
todo
setenv
Todo
todo
unsetenv
Todo
todo
4.8.51 splitedges
SplitedgeMesh
Todo
todo
4.8.52 splitmesh12
splitmesh12
Todo
todo
4.8.53 splitmesh3
splitmesh3
Todo
todo
4.8.54 splitmesh4
splimesh4
Todo
todo
4.8.55 splitmesh6
splitmesh6
Todo
todo
4.8.56 SuperLu
defaulttoSuperLu
Todo
todo
4.8.57 symmetrizeCSR
symmetrizeCSR
Todo
todo
4.8.58 tetgen
Refer to the Tetgen documentation for more informations.
tetgconvexhull
Todo
todo
tetgtransfo
Todo
todo
tetg
Build a 3D mesh from a surface.
Todo
todo
tetgreconstruction
Todo
todo
4.8.59 UMFPACK64
defaulttoUMFPACK64
Todo
todo
4.8.60 VTK_writer_3d
Vtkaddmesh
Todo
todo
Vtkaddscalar
Todo
todo
4.8.61 VTK_writer
Vtkaddmesh
Todo
todo
Vtkaddscalar
FIVE
MATHEMATICAL MODELS
Summary:
This chapter goes deeper into a number of problems that FreeFEM can solve. It is a complement to the Tutorial part
which was only an introduction.
Users are invited to contribute to make this models database grow.
We assume the shape of the film is described by the graph (𝑥, 𝑦, 𝑢(𝑥, 𝑦)) of the vertical displacement 𝑢(𝑥, 𝑦) (𝑥2 +𝑦 2 <
1) under a vertical pressure 𝑝 in terms of force per unit area and an initial tension 𝜇 in terms of force per unit length.
Consider the “small plane” ABCD, A:(𝑥, 𝑦, 𝑢(𝑥, 𝑦)), B:(𝑥, 𝑦, 𝑢(𝑥 + 𝛿𝑥, 𝑦)), C:(𝑥, 𝑦, 𝑢(𝑥 + 𝛿𝑥, 𝑦 + 𝛿𝑦)) and
D:(𝑥, 𝑦, 𝑢(𝑥, 𝑦 + 𝛿𝑦)).
Denote by ⃗𝑛(𝑥, 𝑦) = (𝑛𝑥 (𝑥, 𝑦), 𝑛𝑦 (𝑥, 𝑦), 𝑛𝑧 (𝑥, 𝑦)) the normal vector of the surface 𝑧 = 𝑢(𝑥, 𝑦). We see that the
vertical force due to the tension 𝜇 acting along the edge AD is −𝜇𝑛𝑥 (𝑥, 𝑦)𝛿𝑦 and the the vertical force acting along
the edge AD is:
(︂ )︂
𝜕𝑛𝑥
𝜇𝑛𝑥 (𝑥 + 𝛿𝑥, 𝑦)𝛿𝑦 ≃ 𝜇 𝑛𝑥 (𝑥, 𝑦) + 𝛿𝑥 (𝑥, 𝑦)𝛿𝑦
𝜕𝑥
527
FreeFEM Documentation, Release 4.13
The force in the vertical direction on the surface ABCD due to the tension 𝜇 is given by:
Letting 𝛿𝑥 → 𝑑𝑥, 𝛿𝑦 → 𝑑𝑦, we have the equilibrium of the vertical displacement of soap film on ABCD by 𝑝:
Using the Laplace operator ∆ = 𝜕 2 /𝜕𝑥2 + 𝜕 2 /𝜕𝑦 2 , we can find the virtual displacement write the following:
−∆𝑢 = 𝑓 in Ω
𝑢 = 0 on 𝜕Ω
1 // Parameters
2 int nn = 50;
3 func f = -1;
4 func ue = (x^2+y^2-1)/4; //ue: exact solution
5
6 // Mesh
7 border a(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
8 mesh disk = buildmesh(a(nn));
9 plot(disk);
10
11 // Fespace
12 fespace femp1(disk, P1);
13 femp1 u, v;
14
15 // Problem
16 problem laplace (u, v)
17 = int2d(disk)( //bilinear form
18 dx(u)*dx(v)
19 + dy(u)*dy(v)
20 )
21 - int2d(disk)( //linear form
22 f*v
23 )
24 + on(1, u=0) //boundary condition
25 ;
26
27 // Solve
28 laplace;
(continues on next page)
30 // Plot
31 plot (u, value=true, wait=true);
32
33 // Error
34 femp1 err = u - ue;
35 plot(err, value=true, wait=true);
36
39
42 // Mesh adaptation
43 disk = adaptmesh(disk, u, err=0.01);
44 plot(disk, wait=true);
45
46 // Solve
47 laplace;
48 plot (u, value=true, wait=true);
49
50 // Error
51 err = u - ue; //become FE-function on adapted mesh
52 plot(err, value=true, wait=true);
53
In the 37th line, the 𝐿2 -error estimation between the exact solution 𝑢𝑒 ,
(︂∫︁ )︂1/2
‖𝑢ℎ − 𝑢𝑒 ‖0,Ω = |𝑢ℎ − 𝑢𝑒 | d𝑥d𝑦
2
Ω
are done on the initial mesh. The results are ‖𝑢ℎ − 𝑢𝑒 ‖0,Ω = 0.000384045, |𝑢ℎ − 𝑢𝑒 |1,Ω = 0.0375506.
After the adaptation, we have ‖𝑢ℎ − 𝑢𝑒 ‖0,Ω = 0.000109043, |𝑢ℎ − 𝑢𝑒 |1,Ω = 0.0188411. So the numerical solution is
improved by adaptation of mesh.
5.1.2 Electrostatics
We assume that there is no current and a time independent charge distribution. Then the electric field E satisfies:
divE = 𝜌/𝜖
(5.1)
curlE = 0
where 𝜌 is the charge density and 𝜖 is called the permittivity of free space.
From the equation (5.1) We can introduce the electrostatic potential such that E = −∇𝜑. Then we have Poisson’s
equation −∆𝜑 = 𝑓 , 𝑓 = −𝜌/𝜖.
We now obtain the equipotential line which is the level curve of 𝜑, when there are no charges except conductors
{𝐶𝑖 }1,··· ,𝐾 . Let us assume 𝐾 conductors 𝐶1 , · · · , 𝐶𝐾 within an enclosure 𝐶0 .
Each one is held at an electrostatic potential 𝜙𝑖 . We assume that the enclosure 𝐶0 is held at potential 0. In order to
know 𝜙(𝑥) at any point 𝑥 of the domain Ω, we must solve:
−∆𝜙 = 0 in Ω
∑︀𝑁
where Ω is the interior of 𝐶0 minus the conductors 𝐶𝑖 , and Γ is the boundary of Ω, that is 𝑖=0 𝐶𝑖 .
Here 𝑔 is any function of 𝑥 equal to 𝜙𝑖 on 𝐶𝑖 and to 0 on 𝐶0 . The boundary equation is a reduced form for:
𝜙 = 𝜙𝑖 on 𝐶𝑖 , 𝑖 = 1...𝑁, 𝜙 = 0 on 𝐶0 .
1 // Mesh
2 border C0(t=0, 2*pi){x=5*cos(t); y=5*sin(t);}
3 border C1(t=0, 2*pi){x=2+0.3*cos(t); y=3*sin(t);}
4 border C2(t=0, 2*pi){x=-2+0.3*cos(t); y=3*sin(t);}
5
9 // Fespace
10 fespace Vh(Th, P1);
11 Vh uh, vh;
12
13 // Problem
14 problem Electro (uh, vh)
15 = int2d(Th)( //bilinear
16 dx(uh)*dx(vh)
17 + dy(uh)*dy(vh)
18 )
19 + on(C0, uh=0) //boundary condition on C_0
20 + on(C1, uh=1) //+1 volt on C_1
21 + on(C2, uh=-1) //-1 volt on C_2
22 ;
23
24 // Solve
25 Electro;
26 plot(uh);
(a) Disk with two elliptical holes (b) Equipotential lines where 𝐶1 is located in right hand side
5.1.3 Aerodynamics
Let us consider a wing profile 𝑆 in a uniform flow. Infinity will be represented by a large circle Γ∞ . As previously, we
must solve:
where Ω is the area occupied by the fluid, 𝑢∞ is the air speed at infinity, 𝑐 is a constant to be determined so that 𝜕𝑛 𝜙
is continuous at the trailing edge 𝑃 of 𝑆 (so-called Kutta-Joukowski condition). Lift is proportional to 𝑐.
To find 𝑐 we use a superposition method. As all equations in (5.2) are linear, the solution 𝜙𝑐 is a linear function of 𝑐
𝜙𝑐 = 𝜙0 + 𝑐𝜙1
where 𝜙0 is a solution of (5.2) with 𝑐 = 0 and 𝜙1 is a solution with 𝑐 = 1 and zero speed at infinity.
With these two fields computed, we shall determine 𝑐 by requiring the continuity of 𝜕𝜙/𝜕𝑛 at the trailing edge. An
equation for the upper surface of a NACA0012 (this is a classical wing profile in aerodynamics; the rear of the wing is
called the trailing edge) is:
√
𝑦 = 0.17735 𝑥 − 0.075597𝑥 − 0.212836𝑥2 + 0.17363𝑥3 − 0.06254𝑥4
where Γ2 is the wing profile and Γ1 is an approximation of infinity. One finds 𝑐 by solving:
The solution 𝜙 = 𝜙0 + 𝑐𝜙1 allows us to find 𝑐 by writing that 𝜕𝑛 𝜙 has no jump at the trailing edge 𝑃 = (1, 0).
We have 𝜕𝑛𝜙 − (𝜙(𝑃 + ) − 𝜙(𝑃 ))/𝛿 where 𝑃 + is the point just above 𝑃 in the direction normal to the profile at a
distance 𝛿. Thus the jump of 𝜕𝑛 𝜙 is (𝜙0 |𝑃 + + 𝑐(𝜙1 |𝑃 + − 1)) + (𝜙0 |𝑃 − + 𝑐(𝜙1 |𝑃 − − 1)) divided by 𝛿 because the
normal changes sign between the lower and upper surfaces. Thus
𝜙0 |𝑃 + + 𝜙0 |𝑃 −
𝑐=− ,
(𝜙1 |𝑃 + + 𝜙1 |𝑃 − − 2)
which can be programmed as:
𝜙0 (0.99, 0.01) + 𝜙0 (0.99, −0.01)
𝑐=− .
(𝜙1 (0.99, 0.01) + 𝜙1 (0.99, −0.01) − 2)
1 // Mesh
2 border a(t=0, 2*pi){x=5*cos(t); y=5*sin(t);}
3 border upper(t=0, 1) {
4 x=t;
5 y=0.17735*sqrt(t)-0.075597*t - 0.212836*(t^2) + 0.17363*(t^3) - 0.06254*(t^4);
6 }
7 border lower(t=1, 0) {
8 x=t;
9 y=-(0.17735*sqrt(t) - 0.075597*t - 0.212836*(t^2) + 0.17363*(t^3) - 0.06254*(t^4));
10 }
11 border c(t=0, 2*pi){x=0.8*cos(t)+0.5; y=0.8*sin(t);}
12
16 // Fespace
17 fespace Vh(Th, P2);
18 Vh psi0, psi1, vh;
19
32 plot(psi0);
33
34 solve Joukowski1(psi1,vh)
35 = int2d(Th)(
36 dx(psi1)*dx(vh)
37 + dy(psi1)*dy(vh)
38 )
39 + on(a, psi1=0)
40 + on(upper, lower, psi1=1);
41
42 plot(psi1);
43
|∇𝑢 − ∇𝑢ℎ |2 d𝑥d𝑦 = ∫︀Ω ∇𝑢 · ∇(𝑢 − 2𝑢ℎ ) d𝑥d𝑦 ∫︀ + Ω ∇𝑢ℎ · ∇𝑢ℎ d𝑥d𝑦
∫︀ ∫︀ ∫︀
Ω
= Ω 𝑓 (𝑢 − 2𝑢ℎ ) d𝑥d𝑦 + Ω 𝑓 𝑢ℎ d𝑥d𝑦
1 // Parameters
2 func f = x*y;
3
4 //Mesh
5 mesh Th0 = square(100, 100);
6
7 // Fespace
8 fespace V0h(Th0, P2);
9 V0h u0, v0;
10
11 // Problem
12 solve Poisson0 (u0, v0)
13 = int2d(Th0)(
14 dx(u0)*dx(v0)
15 + dy(u0)*dy(v0)
16 )
17 - int2d(Th0)(
18 f*v0
19 )
20 + on(1, 2, 3, 4, u0=0)
21 ;
22 plot(u0);
23
24 // Error loop
25 real[int] errL2(10), errH1(10);
26 for (int i = 1; i <= 10; i++){
27 // Mesh
28 mesh Th = square(5+i*3,5+i*3);
29
30 // Fespace
31 fespace Vh(Th, P1);
32 Vh u, v;
33 fespace Ph(Th, P0);
34 Ph h = hTriangle; //get the size of all triangles
(continues on next page)
36 // Problem
37 solve Poisson (u, v)
38 = int2d(Th)(
39 dx(u)*dx(v)
40 + dy(u)*dy(v)
41 )
42 - int2d(Th)(
43 f*v
44 )
45 + on(1, 2, 3, 4, u=0)
46 ;
47
48 // Error
49 V0h uu = u; //interpolate solution on first mesh
50 errL2[i-1] = sqrt( int2d(Th0)((uu - u0)^2) )/h[].max^2;
51 errH1[i-1] = sqrt( int2d(Th0)(f*(u0 - 2*uu + uu)) )/h[].max;
52 }
53
54 // Display
55 cout << "C1 = " << errL2.max << "("<<errL2.min<<")" << endl;
56 cout << "C2 = " << errH1.max << "("<<errH1.min<<")" << endl;
We can guess that 𝐶1 = 0.0179253(0.0173266) and 𝐶2 = 0.0729566(0.0707543), where the numbers inside the
parentheses are minimum in calculation.
on the square ]0, 2𝜋[2 under bi-periodic boundary condition 𝑢(0, 𝑦) = 𝑢(2𝜋, 𝑦) for all 𝑦 and 𝑢(𝑥, 0) = 𝑢(𝑥, 2𝜋) for all
𝑥.
These boundary conditions are achieved from the definition of the periodic finite element space.
1 // Parameters
2 func f = sin(x+pi/4.)*cos(y+pi/4.); //right hand side
3
4 // Mesh
5 mesh Th = square(10, 10, [2*x*pi, 2*y*pi]);
6
7 // Fespace
8 //defined the fespace with periodic condition
9 //label: 2 and 4 are left and right side with y the curve abscissa
10 // 1 and 2 are bottom and upper side with x the curve abscissa
11 fespace Vh(Th, P2, periodic=[[2, y], [4, y], [1, x], [3, x]]);
12 Vh uh, vh;
13
14 // Problem
15 problem laplace (uh, vh)
16 = int2d(Th)(
(continues on next page)
26 // Solve
27 laplace;
28
29 // Plot
30 plot(uh, value=true);
The periodic condition does not necessarily require parallel boundaries. The following example give such example.
Tip
7 // Mesh
8 border a(t=0, 1){x=-t+1; y=t; label=1;};
9 border b(t=0, 1){x=-t; y=1-t; label=2;};
16 // Fespace
17 //warning for periodic condition:
18 //side a and c
19 //on side a (label 1) $ x \in [0,1] $ or $ x-y\in [-1,1] $
20 //on side c (label 3) $ x \in [-1,0]$ or $ x-y\in[-1,1] $
21 //so the common abscissa can be respectively $x$ and $x+1$
22 //or you can can try curviline abscissa $x-y$ and $x-y$
23 //1 first way
24 //fespace Vh(Th, P2, periodic=[[2, 1+x], [4, x], [1, x], [3, 1+x]]);
25 //2 second way
26 fespace Vh(Th, P2, periodic=[[2, x+y], [4, x+y], [1, x-y], [3, x-y]]);
27 Vh uh, vh;
28
29 // Problem
30 real intf = int2d(Th)(f);
31 real mTh = int2d(Th)(1);
32 real k = intf / mTh;
33 problem laplace (uh, vh)
34 = int2d(Th)(
35 uh*vh*1.0e-10 // to fix the constant
36 + dx(uh)*dx(vh)
37 + dy(uh)*dy(vh)
38 )
39 + int2d(Th)(
40 (k-f)*vh
41 )
42 ;
43
44 // Solve
45 laplace;
46
47 // Plot
48 plot(uh, wait=true);
Fig. 5.6: The isovalue of solution 𝑢 for ∆𝑢 = ((𝑦 + 𝑥)2 + 1)((𝑦 − 𝑥)2 + 1) − 𝑘, in Ω and 𝜕𝑛 𝑢 = 0 on hole, and
with two periodic boundary condition on external border
An other example with no equal border, just to see if the code works.
Tip
13 // Parameters
14 int n = 10;
15 real Ax = 0.9, Ay = 1;
16 real Bx = 2, By = 1;
17 real Cx = 2.5, Cy = 2.5;
18 real Dx = 1, Dy = 2;
19 real gx = (Ax+Bx+Cx+Dx)/4.;
20 real gy = (Ay+By+Cy+Dy)/4.;
21
22 // Mesh
23 LINEBORDER(A,B,1)
24 LINEBORDER(B,C,2)
25 LINEBORDER(C,D,3)
26 LINEBORDER(D,A,4)
27 mesh Th=buildmesh(AB(n)+BC(n)+CD(n)+DA(n),fixedborder=1);
28
29 // Fespace
30 real l1 = dist(Ax,Ay,Bx,By);
31 real l2 = dist(Bx,By,Cx,Cy);
32 real l3 = dist(Cx,Cy,Dx,Dy);
33 real l4 = dist(Dx,Dy,Ax,Ay);
34 func s1 = dist(Ax,Ay,x,y)/l1; //absisse on AB = ||AX||/||AB||
35 func s2 = dist(Bx,By,x,y)/l2; //absisse on BC = ||BX||/||BC||
36 func s3 = dist(Cx,Cy,x,y)/l3; //absisse on CD = ||CX||/||CD||
37 func s4 = dist(Dx,Dy,x,y)/l4; //absisse on DA = ||DX||/||DA||
38 verbosity = 6; //to see the abscisse value of the periodic condition
39 fespace Vh(Th, P1, periodic=[[1, s1], [3, s3], [2, s2], [4, s4]]);
40 verbosity = 1; //reset verbosity
41 Vh u, v;
42
43 real cc = 0;
44 cc = int2d(Th)((x-gx)*(y-gy)-cc)/Th.area;
45 cout << "compatibility = " << int2d(Th)((x-gx)*(y-gy)-cc) <<endl;
46
47 // Problem
48 solve Poisson (u, v)
49 = int2d(Th)(
50 Grad(u)'*Grad(v)
51 + 1e-10*u*v
52 )
53 -int2d(Th)(
54 10*v*((x-gx)*(y-gy)-cc)
55 )
56 ;
57
58 // Plot
59 plot(u, value=true);
Tip
3 // Parameters
4 real hs = 0.1; //mesh size on sphere
5 int[int] N = [20, 20, 20];
6 real [int,int] B = [[-1, 1], [-1, 1], [-1, 1]];
7 int [int,int] L = [[1, 2], [3, 4], [5, 6]];
8
12 // Mesh
13 bool buildTh = 0;
14 mesh3 Th;
15 try { //a way to build one time the mesh or read it if the file exist
16 Th = readmesh3("Th-hex-sph.mesh");
17 }
18 catch (...){
19 buildTh = 1;
20 }
21
22 if (buildTh){
23 include "MeshSurface.idp"
24
25 // Surface Mesh
26 mesh3 ThH = SurfaceHex(N, B, L, 1);
27 mesh3 ThS = Sphere(0.5, hs, 7, 1);
28
35 savemesh(Th, "Th-hex-sph.mesh");
36 }
37
38 // Fespace
39 fespace Ph(Th, P0);
40 Ph reg = region;
41 cout << " centre = " << reg(0,0,0) << endl;
42 cout << " exterieur = " << reg(0,0,0.7) << endl;
43
44 verbosity = 50;
45 fespace Vh(Th, P1, periodic=[[3, x, z], [4, x, z], [1, y, z], [2, y, z], [5, x, y],␣
˓→[6, x, y]]);
46 verbosity = 1;
47 Vh uh,vh;
48
49 // Macro
50 macro Grad(u) [dx(u),dy(u),dz(u)] // EOM
51
52 // Problem
53 problem Poisson (uh, vh)
54 = int3d(Th, 1)(
55 Grad(uh)'*Grad(vh)*100
56 )
57 + int3d(Th, 2)(
58 Grad(uh)'*Grad(vh)*2
59 )
60 + int3d(Th)(
61 vh*f
62 )
63 ;
64
65 // Solve
66 Poisson;
67
68 // Plot
69 plot(uh, wait=true, nbiso=6);
70 medit("uh", Th, uh);
−∆𝑢 = 𝑓 in Ω
𝑢 = 𝑔 on Γ𝐷
𝜕𝑢/𝜕𝑛 = 0 on Γ𝑁
𝑢 = 𝐾𝑖 𝑢𝑆 + 𝑢𝑅 , 𝑢𝑅 ∈ 𝐻 2 (near 𝛾𝑖 ), 𝑖 = 1, 2
with a constants 𝐾𝑖 .
1/2
Here 𝑢𝑆 = 𝑟𝑗 sin(𝜃𝑗 /2) by the local polar coordinate (𝑟𝑗 , 𝜃𝑗 at 𝛾𝑗 such that (𝑟1 , 𝜃1 ) = (𝑟, 𝜃).
Instead of polar coordinate system (𝑟, 𝜃), we use that 𝑟 = sqrt (𝑥2 + 𝑦 2 ) and 𝜃 = atan2 (𝑦, 𝑥) in FreeFEM.
Assume that 𝑓 = −2 × 30(𝑥2 + 𝑦 2 ) and 𝑔 = 𝑢𝑒 = 10(𝑥2 + 𝑦 2 )1/4 sin [tan−1 (𝑦/𝑥)]/2 + 30(𝑥2 𝑦 2 ), where 𝑢𝑒 S is
(︀ )︀
1 // Parameters
2 func f = -2*30*(x^2+y^2); //given function
3 //the singular term of the solution is K*us (K: constant)
(continues on next page)
8 // Mesh
9 border N(t=0, 1){x=-1+t; y=0; label=1;};
10 border D1(t=0, 1){x=t; y=0; label=2;};
11 border D2(t=0, 1){x=1; y=t; label=2;};
12 border D3(t=0, 2){x=1-t; y=1; label=2;};
13 border D4(t=0, 1){x=-1; y=1-t; label=2;};
14
18 // Fespace
19 fespace V0h(T0h, P1);
20 V0h u0, v0;
21
22 //Problem
23 solve Poisson0 (u0, v0)
24 = int2d(T0h)(
25 dx(u0)*dx(v0)
26 + dy(u0)*dy(v0)
27 )
28 - int2d(T0h)(
29 f*v0
30 )
31 + on(2, u0=ue)
32 ;
33
39 // Fespace
40 fespace Vh(Th, P1);
41 Vh u, v;
42
43 // Problem
44 solve Poisson (u, v)
45 = int2d(Th)(
46 dx(u)*dx(v)
47 + dy(u)*dy(v)
48 )
49 - int2d(Th)(
50 f*v
51 )
52 + on(2, u=ue)
53 ;
54
55 // Plot
(continues on next page)
59 // Error in H1 norm
60 Vh uue = ue;
61 real H1e = sqrt( int2d(Th)(dx(uue)^2 + dy(uue)^2 + uue^2) );
62 Vh err0 = u0 - ue;
63 Vh err = u - ue;
64 Vh H1err0 = int2d(Th)(dx(err0)^2 + dy(err0)^2 + err0^2);
65 Vh H1err = int2d(Th)(dx(err)^2 + dy(err)^2 + err^2);
66 cout << "Relative error in first mesh = "<< int2d(Th)(H1err0)/H1e << endl;
67 cout << "Relative error in adaptive mesh = "<< int2d(Th)(H1err)/H1e << endl;
From line 35 to 37, mesh adaptations are done using the base of singular term.
In line 61, H1e = |𝑢𝑒 |1,Ω is calculated.
In lines 64 and 65, the relative errors are calculated, that is:
where 𝑢0ℎ is the numerical solution in T0h and 𝑢𝑎ℎ is u in this program.
−∆𝑝 = 1 in Ω
𝑝 = 𝑔𝑑 on Γ𝐷
𝜕𝑝/𝜕𝑛 = 𝑔𝑛 on Γ𝑁
∇𝑝 + u = 0 in Ω
∇.u = 𝑓 in Ω
𝑝 = 𝑔𝑑 on Γ𝐷
𝜕𝑢.𝑛 = g𝑛 .𝑛 on Γ𝑁
and:
V0 = {v ∈ V; v.𝑛 = 0 on Γ𝑁 }
To write the FreeFEM example, we have just to choose the finites elements spaces.
Here V space is discretize with Raviart-Thomas finite element RT0 and P is discretize by constant finite element P0.
Example 9.10 LaplaceRT.edp
1 // Parameters
2 func gd = 1.;
3 func g1n = 1.;
4 func g2n = 1.;
5
6 // Mesh
7 mesh Th = square(10, 10);
8
9 // Fespace
10 fespace Vh(Th, RT0);
11 Vh [u1, u2];
12 Vh [v1, v2];
13
17 // Problem
18 problem laplaceMixte ([u1, u2, p], [v1, v2, q], solver=GMRES, eps=1.0e-10, tgv=1e30,␣
˓→dimKrylov=150)
19 = int2d(Th)(
20 p*q*1e-15 //this term is here to be sure
21 // that all sub matrix are inversible (LU requirement)
22 + u1*v1
23 + u2*v2
24 + p*(dx(v1)+dy(v2))
25 + (dx(u1)+dy(u2))*q
26 )
27 + int2d(Th) (
28 q
29 )
30 - int1d(Th, 1, 2, 3)(
31 gd*(v1*N.x +v2*N.y)
32 )
33 + on(4, u1=g1n, u2=g2n)
34 ;
35
36 // Solve
37 laplaceMixte;
38
39 // Plot
40 plot([u1, u2], coef=0.1, wait=true, value=true);
41 plot(p, fill=1, wait=true, value=true);
1 // Parameters
2 real[int] viso(21);
3 for (int i = 0; i < viso.n; i++)
4 viso[i] = 10.^(+(i-16.)/2.);
5 real error = 0.01;
6 func f = (x-y);
7
8 // Mesh
9 border ba(t=0, 1.0){x=t; y=0; label=1;}
10 border bb(t=0, 0.5){x=1; y=t; label=2;}
11 border bc(t=0, 0.5){x=1-t; y=0.5; label=3;}
12 border bd(t=0.5, 1){x=0.5; y=t; label=4;}
13 border be(t=0.5, 1){x=1-t; y=1; label=5;}
14 border bf(t=0.0, 1){x=0; y=1-t; label=6;}
15 mesh Th = buildmesh(ba(6) + bb(4) + bc(4) + bd(4) + be(4) + bf(6));
16
17 // Fespace
18 fespace Vh(Th, P2);
19 Vh u, v;
20
24 // Problem
25 problem Problem1 (u, v, solver=CG, eps=1.0e-6)
26 = int2d(Th, qforder=5)(
27 u*v*1.0e-10
28 + dx(u)*dx(v)
29 + dy(u)*dy(v)
30 )
31 + int2d(Th, qforder=5)(
32 - f*v
33 )
34 ;
where ℎ𝑇 is the longest edge of 𝑇 , ℰ𝑇 is the set of 𝑇 edge not on Γ = 𝜕Ω, 𝑛𝑇 is the outside unit normal to 𝐾, ℎ𝑒 is
the length of edge 𝑒, [𝑔] is the jump of the function 𝑔 across edge (left value minus right value).
Of course, we can use a variational form to compute 𝜂𝑇2 , with test function constant function in each triangle.
1 // Error
2 varf indicator2 (uu, chiK)
3 = intalledges(Th)(
4 chiK*lenEdge*square(jump(N.x*dx(u) + N.y*dy(u)))
5 )
6 + int2d(Th)(
7 chiK*square(hTriangle*(f + dxx(u) + dyy(u)))
8 )
(continues on next page)
18 // Error
19 rho[] = indicator2(0, Nh);
20 rho = sqrt(rho);
21 cout << "rho = min " << rho[].min << " max=" << rho[].max << endl;
22 plot(rho, fill=true, wait=true, cmm="indicator density", value=true, viso=viso,␣
˓→nbiso=viso.n);
23
24 // Mesh adaptation
25 plot(Th, wait=true, cmm="Mesh (before adaptation)");
26 Th = adaptmesh(Th, [dx(u), dy(u)], err=error, anisomax=1);
27 plot(Th, wait=true, cmm="Mesh (after adaptation)");
28 u = u;
29 rho = rho;
30 error = error/2;
31 }
If the method is correct, we expect to look the graphics by an almost constant function 𝜂 on your computer as in Fig.
5.8a and Fig. 5.8b.
(a) Density of the error indicator with isotropic 𝑃2 metric (b) Density of the error indicator with isotropic 𝑃2 metric
ℎ𝑛 (𝑥)
ℎ𝑛+1 (𝑥) =
𝑓𝑛 (𝜂𝐾 (𝑥))
where 𝜂𝑛 (𝑥) is the level of error at point 𝑥 given by the local error indicator, ℎ𝑛 is the previous “mesh size” field, and
𝑓𝑛 is a user function define by 𝑓𝑛 = 𝑚𝑖𝑛(3, 𝑚𝑎𝑥(1/3, 𝜂𝑛 /𝜂𝑛* )) where 𝜂𝑛* = 𝑚𝑒𝑎𝑛(𝜂𝑛 )𝑐, and 𝑐 is an user coefficient
generally close to one.
First a macro MeshSizecomputation is defined to get a 𝑃1 mesh size as the average of edge length.
19 /*etaK is discontinous*/
20 /*we use P1 L2 projection with mass lumping*/
21 Vh fn, sigma;
22 varf veta(unused, v) = int2d(Th)(etak*v);
23 varf vun(unused, v) = int2d(Th)(1*v);
24 fn[] = veta(0, Vh);
25 sigma[] = vun(0, Vh);
26 fn[] = fn[]./ sigma[];
27 fn = max(min(fn/etastar,3.),0.3333);
28
1 // Parameters
2 real hinit = 0.2; //initial mesh size
3 func f=(x-y);
4
5 // Mesh
6 border ba(t=0, 1.0){x=t; y=0; label=1;}
7 border bb(t=0, 0.5){x=1; y=t; label=2;}
8 border bc(t=0, 0.5){x=1-t; y=0.5; label=3;}
9 border bd(t=0.5, 1){x=0.5; y=t; label=4;}
10 border be(t=0.5, 1){x=1-t; y=1; label=5;}
11 border bf(t=0.0, 1){x=0; y=1-t; label=6;}
12 mesh Th = buildmesh(ba(6) + bb(4) + bc(4) + bd(4) + be(4) + bf(6));
13
14 // Fespace
15 fespace Vh(Th, P1); //for the mesh size and solution
16 Vh h = hinit; //the FE function for the mesh size
17 Vh u, v;
18
25 // Problem
26 problem Poisson (u, v)
27 = int2d(Th, qforder=5)(
28 u*v*1.0e-10
29 + dx(u)*dx(v)
30 + dy(u)*dy(v)
31 )
32 - int2d(Th, qforder=5)(
33 f*v
34 )
35 ;
36
50 // Solve
51 Poisson;
52 plot(Th, u, wait=true);
53
54 real cc = 0.8;
55 if (i > 5) cc=1;
56 ReMeshIndicator(Th, Ph, Vh, indicator2, cc);
57 plot(Th, wait=true);
58 }
(a) The error indicator with isotropic 𝑃1 (b) The mesh and isovalue of the solution
5.2 Elasticity
Consider an elastic plate with undeformed shape Ω×] − ℎ, ℎ[ in R3 , Ω ⊂ R2 .
By the deformation of the plate, we assume that a point 𝑃 (𝑥1 , 𝑥2 , 𝑥3 ) moves to 𝒫(𝜉1 , 𝜉2 , 𝜉3 ). The vector u =
(𝑢1 , 𝑢2 , 𝑢3 ) = (𝜉1 − 𝑥1 , 𝜉2 − 𝑥2 , 𝜉3 − 𝑥3 ) is called the displacement vector.
By the deformation, the line segment x, x + 𝜏 ∆x moves approximately to x + u(x), x + 𝜏 ∆x + u(x + 𝜏 ∆x) for
small 𝜏 , where x = (𝑥1 , 𝑥2 , 𝑥3 ), ∆x = (∆𝑥1 , ∆𝑥2 , ∆𝑥3 ).
We now calculate the ratio between two segments:
where 𝜈𝑖 = ∆𝑥𝑖 |∆x|−1 . If the deformation is small, then we may consider that:
where 𝜎𝑖𝑗 (x) is called stress tensor at x. Hooke’s law is the assumption of a linear relation between 𝜎𝑖𝑗 and 𝜀𝑖𝑗 such
as:
𝜎3𝑖 = 0, 𝑥3 = ±ℎ, 𝑖 1, 2, 3
The assumption leads that 𝜎3𝑖 = 0 in Ω×] − ℎ, ℎ[ and u(𝑥1 , 𝑥2 , 𝑥3 ) = 𝑢(𝑥1 , 𝑥2 ) for all −ℎ < 𝑥3 < ℎ.
• Generalized plain stress:
The cylinder is subjected to no load at 𝑥3 = ±ℎ. Introducing the mean values with respect to thickness,
∫︁ ℎ
1
𝑢𝑖 (𝑥1 , 𝑥2 ) = 𝑢(𝑥1 , 𝑥2 , 𝑥3 )𝑑𝑥3
2ℎ −ℎ
and we derive 𝑢3 ≡ 0. Similarly we define the mean values 𝑓 , 𝑔 of the body force and surface force as well as
the mean values 𝜀𝑖𝑗 and 𝜎 𝑖𝑗 of the components of stress and strain, respectively.
In what follows we omit the overlines of 𝑢, 𝑓 , 𝑔, 𝜀𝑖𝑗 and 𝜀𝑖𝑗 . Then we obtain similar equation of equilibrium given in
(5.4) replacing Ω×] − ℎ, ℎ[ with Ω and changing 𝑖 = 1, 2. In the case of plane stress, 𝜎𝑖𝑗 = 𝜆* 𝛿𝑖𝑗 div𝑢 + 2𝜇𝜀𝑖𝑗 , 𝜆* =
(2𝜆𝜇)/(𝜆 + 𝜇).
The equations of elasticity are naturally written in variational form for the displacement vector u(x) ∈ 𝑉 as:
∫︁ ∫︁ ∫︁
[2𝜇𝜖𝑖𝑗 (u)𝜖𝑖𝑗 (v) + 𝜆𝜖𝑖𝑖 (u)𝜖𝑗𝑗 (v)] = f · v + g · v, ∀v ∈ 𝑉
Ω Ω Γ
Tip
Beam
Consider an elastic plate with the undeformed rectangle shape ]0, 10[×]0, 2[. The body force is the gravity force f
and the boundary force g is zero on lower and upper side. On the two vertical sides of the beam are fixed.
1 // Parameters
2 real E = 21.5;
3 real sigma = 0.29;
4 real gravity = -0.05;
5
6 // Mesh
7 border a(t=2, 0){x=0; y=t; label=1;}
8 border b(t=0, 10){x=t; y=0; label=2;}
9 border c(t=0, 2){ x=10; y=t; label=1;}
10 border d(t=0, 10){ x=10-t; y=2; label=3;}
11 mesh th = buildmesh(b(20) + c(5) + d(20) + a(5));
12
13 // Fespace
14 fespace Vh(th, [P1, P1]);
15 Vh [uu, vv];
16 Vh [w, s];
17
18 // Macro
19 real sqrt2 = sqrt(2.);
20 macro epsilon(u1, u2) [dx(u1), dy(u2), (dy(u1)+dx(u2))/sqrt2] //
21 macro div(u,v) (dx(u) + dy(v)) //
22
23 // Problem
24 real mu = E/(2*(1+sigma));
25 real lambda = E*sigma/((1+sigma)*(1-2*sigma));
26 solve Elasticity ([uu, vv], [w, s])
27 = int2d(th)(
28 lambda*div(w,s)*div(uu,vv)
29 + 2.*mu*( epsilon(w,s)'*epsilon(uu,vv) )
30 )
31 + int2d(th)(
32 - gravity*s
33 )
34 + on(1, uu=0, vv=0)
35 ;
36
37 // Plot
38 plot([uu, vv], wait=true);
41 // Movemesh
42 mesh th1 = movemesh(th, [x+uu, y+vv]);
43 plot(th1, wait=true);
Tip
Beam 3D
Consider elastic box with the undeformed parallelepiped shape ]0, 5[×]0, 1[×]0, 1[. The body force is the gravity
force f and the boundary force g is zero on all face except one the one vertical left face where the beam is fixed.
1 include "cube.idp"
2
3 // Parameters
4 int[int] Nxyz = [20, 5, 5];
5 real [int, int] Bxyz = [[0., 5.], [0., 1.], [0., 1.]];
6 int [int, int] Lxyz = [[1, 2], [2, 2], [2, 2]];
7
8 real E = 21.5e4;
9 real sigma = 0.29;
10 real gravity = -0.05;
11
12 // Mesh
13 mesh3 Th = Cube(Nxyz, Bxyz, Lxyz);
14
15 // Fespace
16 fespace Vh(Th, [P1, P1, P1]);
17 Vh [u1, u2, u3], [v1, v2, v3];
18
19 // Macro
20 real sqrt2 = sqrt(2.);
21 macro epsilon(u1, u2, u3) [
22 dx(u1), dy(u2), dz(u3),
23 (dz(u2) + dy(u3))/sqrt2,
24 (dz(u1) + dx(u3))/sqrt2,
25 (dy(u1) + dx(u2))/sqrt2] //
26 macro div(u1, u2, u3) (dx(u1) + dy(u2) + dz(u3)) //
27
28 // Problem
29 real mu = E/(2*(1+sigma));
30 real lambda = E*sigma/((1+sigma)*(1-2*sigma));
31
41 ;
42
43 // Display
44 real dmax = u1[].max;
45 cout << "max displacement = " << dmax << endl;
46
47 // Movemesh
48 real coef = 0.1/dmax;
49 int[int] ref2 = [1, 0, 2, 0];
50 mesh3 Thm = movemesh3(Th, transfo=[x+u1*coef, y+u2*coef, z+u3*coef], label=ref2);
51 Thm = change(Thm, label=ref2);
52
53 // Plot
54 plot(Th, Thm, wait=true, cmm="coef amplification = "+coef);
with u𝑘,𝑅 ∈ 𝐻 2 (ΩΣ ∩𝑈𝑘 )2 , where 𝑈𝑘 , 𝑘 = 1, 2 are open neighborhoods of 𝛾𝑘 such that 𝜕𝐿1 ∩𝑈1 = 𝛾1 , 𝜕𝐿𝑚 ∩𝑈2 =
𝛾2 , and
[︂ ]︂
𝐶 1 1 [2𝜅 − 1] cos(𝜃𝑘 /2) − cos(3𝜃𝑘 /2)
𝑆𝑘1 (𝜃𝑘 ) = 4𝜇 (2𝜋)1/2 ,
[︂ −[2𝜅 + 1] sin(𝜃𝑘 /2) + sin(3𝜃𝑘 /2) ]︂
𝐶 1 1 −[2𝜅 − 1] sin(𝜃𝑘 /2) + 3 sin(3𝜃𝑘 /2)
𝑆𝑘2 (𝜃𝑘 ) = 4𝜇 .
(2𝜋)1/2 −[2𝜅 + 1] cos(𝜃𝑘 /2) + cos(3𝜃𝑘 /2)
where 𝜇 is the shear modulus of elasticity, 𝜅 = 3 − 4𝜈 (𝜈 is the Poisson’s ratio) for plane strain and 𝜅 = 3−𝜈
1+𝜈 for plane
stress.
The coefficients 𝐾1 (𝛾𝑖 ) and 𝐾2 (𝛾𝑖 ), which are important parameters in fracture mechanics, are called stress intensity
factors of the opening mode (mode I) and the sliding mode (mode II), respectively.
For simplicity, we consider the following simple crack
with only one crack tip 𝛾 = (0, 0). Unfortunately, FreeFEM cannot treat crack, so we use the modification of the
domain with U-shape channel (see U-shape example, Fig. 3.19) with 𝑑 = 0.0001. The undeformed crack Σ is approx-
imated by
• Adaptivity is an important technique here, because a large singularity occurs at 𝛾 as shown in (5.5).
The first example creates mode I deformation by the opposed surface force on B and T in the vertical direction of Σ,
and the displacement is fixed on R.
In a laboratory, fracture engineers use photoelasticity to make stress field visible, which shows the principal stress
difference
√︁
𝜎1 − 𝜎2 = (𝜎11 − 𝜎22 )2 + 4𝜎12 2
Tip
4 real E = 21.5;
5 real sigma = 0.29;
6
7 // Mesh
8 border L1(t=0, ca-d){x=-cb; y=-d-t;}
9 border L2(t=0, ca-d){x=-cb; y=ca-t;}
10 border B(t=0, 2){x=cb*(t-1); y=-ca;}
11 border C1(t=0, 1){x=-ca*(1-t)+(tip-10*d)*t; y=d;}
12 border C21(t=0, 1){x=(tip-10*d)*(1-t)+tip*t; y=d*(1-t);}
13 border C22(t=0, 1){x=(tip-10*d)*t+tip*(1-t); y=-d*t;}
14 border C3(t=0, 1){x=(tip-10*d)*(1-t)-ca*t; y=-d;}
15 border C4(t=0, 2*d){x=-ca; y=-d+t;}
16 border R(t=0, 2){x=cb; y=cb*(t-1);}
17 border T(t=0, 2){x=cb*(1-t); y=ca;}
18 mesh Th = buildmesh(L1(n/2) + L2(n/2) + B(n)
19 + C1(n) + C21(3) + C22(3) + C3(n) + R(n) + T(n));
20 plot(Th, wait=true);
21
22 cb=0.1; ca=0.1;
23 mesh Zoom = buildmesh(L1(n/2) + L2(n/2) + B(n) + C1(n)
24 + C21(3) + C22(3) + C3(n) + R(n) + T(n));
25 plot(Zoom, wait=true);
26
27 // Fespace
28 fespace Vh(Th, [P2, P2]);
29 Vh [u, v];
30 Vh [w, s];
31
35 // Problem
36 real mu = E/(2*(1+sigma));
37 real lambda = E*sigma/((1+sigma)*(1-2*sigma));
38 solve Problem ([u, v], [w, s])
39 = int2d(Th)(
40 2*mu*(dx(u)*dx(w) + ((dx(v)+dy(u))*(dx(s)+dy(w)))/4)
41 + lambda*(dx(u) + dy(v))*(dx(w) + dy(s))/2
42 )
43 -int1d(Th, T)(
44 0.1*(1-x)*s
45 )
46 +int1d(Th, B)(
47 0.1*(1-x)*s
48 )
49 +on(R, u=0, v=0)
50 ;
51
52 // Loop
53 for (int i = 1; i <= 5; i++){
54 mesh Plate = movemesh(Zoom, [x+u, y+v]); //deformation near gamma
55 Sx = lambda*(dx(u) + dy(v)) + 2*mu*dx(u);
56 Sy = lambda*(dx(u) + dy(v)) + 2*mu*dy(v);
57 Sxy = mu*(dy(u) + dx(v));
58 N = 0.1*1*sqrt((Sx-Sy)^2 + 4*Sxy^2); //principal stress difference
59 if (i == 1){
60 plot(Plate, bw=1);
61 plot(N, bw=1);
62 }
63 else if (i == 5){
64 plot(Plate, bw=1);
65 plot(N, bw=1);
66 break;
67 }
68
69 // Adaptmesh
70 Th = adaptmesh(Th, [u, v]);
71
72 // Solve
73 Problem;
74 }
(a) Crack open displacement (COD) on the first mesh (b) Principal stress difference on the first mesh
(c) COD on the last adaptive mesh (d) Principal stress difference on the last adaptive mesh
It is difficult to create mode II deformation by the opposed shear force on B and T that is observed in a laboratory. So
we use the body shear force along Σ, that is, the 𝑥-component 𝑓1 of the body force f is given by
Tip
4 real E = 21.5;
5 real sigma = 0.29;
6
7 // Mesh
8 border L1(t=0, ca-d){x=-cb; y=-d-t;}
9 border L2(t=0, ca-d){x=-cb; y=ca-t;}
10 border B(t=0, 2){x=cb*(t-1); y=-ca;}
11 border C1(t=0, 1){x=-ca*(1-t)+(tip-10*d)*t; y=d;}
12 border C21(t=0, 1){x=(tip-10*d)*(1-t)+tip*t; y=d*(1-t);}
13 border C22(t=0, 1){x=(tip-10*d)*t+tip*(1-t); y=-d*t;}
14 border C3(t=0, 1){x=(tip-10*d)*(1-t)-ca*t; y=-d;}
15 border C4(t=0, 2*d){x=-ca; y=-d+t;}
16 border R(t=0, 2){x=cb; y=cb*(t-1);}
17 border T(t=0, 2){x=cb*(1-t); y=ca;}
18 mesh Th = buildmesh(L1(n/2) + L2(n/2) + B(n)
19 + C1(n) + C21(3) + C22(3) + C3(n) + R(n) + T(n));
20 plot(Th, wait=true);
21
22 cb=0.1; ca=0.1;
23 mesh Zoom = buildmesh(L1(n/2) + L2(n/2) + B(n) + C1(n)
24 + C21(3) + C22(3) + C3(n) + R(n) + T(n));
25 plot(Zoom, wait=true);
26
27 // Fespace
28 fespace Vh(Th, [P2, P2]);
29 Vh [u, v];
30 Vh [w, s];
31
35 fespace Vh1(Th,P1);
36 Vh1 fx = ((y>0.001)*(y<0.1))-((y<-0.001)*(y>-0.1));
37
38 // Problem
39 real mu = E/(2*(1+sigma));
40 real lambda = E*sigma/((1+sigma)*(1-2*sigma));
41 solve Problem ([u, v], [w, s])
42 = int2d(Th)(
43 2*mu*(dx(u)*dx(w) + ((dx(v) + dy(u))*(dx(s)+ dy(w)))/4)
44 + lambda*(dx(u) + dy(v))*(dx(w) + dy(s))/2
45 )
46 -int2d(Th)(
47 fx*w
48 )
49 +on(R, u=0, v=0)
50 ;
51
52 // Loop
53 for (int i = 1; i <= 3; i++){
54 mesh Plate = movemesh(Zoom, [x+u, y+v]); //deformation near gamma
55 Sx = lambda*(dx(u) + dy(v)) + 2*mu*dx(u);
56 Sy = lambda*(dx(u) + dy(v)) + 2*mu*dy(v);
57 Sxy = mu*(dy(u) + dx(v));
58 N = 0.1*1*sqrt((Sx-Sy)^2 + 4*Sxy^2); //principal stress difference
59 if (i == 1){
60 plot(Plate, bw=1);
61 plot(N, bw=1);
62 }
63 else if (i == 3) {
64 plot(Plate, bw=1);
65 plot(N, bw=1);
66 break;
67 }
68
69 // Adaptmesh
70 Th=adaptmesh(Th, [u, v]);
71
72 // Solve
73 Problem;
74 }
(a) COD on the first mesh (b) Principal stress difference in the first mesh
(c) COD on the last adaptive mesh (d) Principal stress difference on the last adaptive mesh
1 // Parameters
2 real a = 0.001;
3 func b = 1.;
4
5 // Mesh
6 mesh Th = square(10, 10);
7 Th = adaptmesh(Th, 0.05, IsMetric=1, splitpbedge=1);
8 plot(Th, wait=true);
9
10 // Fespace
11 fespace Vh(Th, P1);
12 Vh u=0;
13 Vh v, w;
14
19 // Function
20 func real f (real u){
21 return u*a + u - log(1.+u);
22 }
23 func real df (real u){
24 return a +u/(1.+u);
25 }
26 func real ddf (real u){
27 return 1. / ((1.+u)*(1.+u));
28 }
29
30 // Problem
31 //the variational form of evaluate dJ = nabla J
32 //dJ = f'()*(dx(u)*dx(vh) + dy(u)*dy(vh))
33 varf vdJ (uh, vh)
34 = int2d(Th)(
35 alpha*(dx(u)*dx(vh) + dy(u)*dy(vh))
(continues on next page)
52 // Newton algorithm
53 for (int i = 0; i < 100; i++){
54 // Compute f' and f''
55 alpha = df(dx(u)*dx(u) + dy(u)*dy(u));
56 dalpha = 2*ddf(dx(u)*dx(u) + dy(u)*dy(u));
57
58 // nabla J
59 v[]= vdJ(0, Vh);
60
61 // Residual
62 real res = v[]'*v[];
63 cout << i << " residu^2 = " << res << endl;
64 if( res < 1e-12) break;
65
66 // HJ
67 matrix H = vhJ(Vh, Vh, factorize=1, solver=LU);
68
69 // Newton
70 w[] = H^-1*v[];
71 u[] -= w[];
72 }
73
74 // Plot
75 plot (u, wait=true, cmm="Solution with Newton-Raphson");
This tool is based on arpack++, the object-oriented version of ARPACK eigenvalue package [LEHOUCQ1998].
The function EigenValue computes the generalized eigenvalue of 𝐴𝑢 = 𝜆𝐵𝑢. The Shift-invert method is used by
default, with sigma =𝜎 the shift of the method.
The matrix 𝑂𝑃 is defined with 𝐴 − 𝜎𝐵.
The return value is the number of converged eigenvalues (can be greater than the number of requested eigenvalues
nev=)
where the matrix 𝑂𝑃 = 𝐴 − 𝜎𝐵 with a solver and boundary condition, and the matrix 𝐵.
There is also a functional interface:
where 𝑛 is the size of the problem, and the operators are now defined through functions, defining respectively the matrix
product of 𝑂𝑃 −1 and 𝐵, as in
1 int n = OP1.n;
2 func real[int] FOP1(real[int] & u){ real[int] Au = OP^-1*u; return Au; }
3 func real[int] FB(real[int] & u){ real[int] Au = B*u; return Au; }
If you want finer control over the method employed in ARPACK, you can specify which mode ARPACK will work with
(mode= , see ARPACK documentation [LEHOUCQ1998]). The operators necessary for the chosen mode can be passed
through the optional parameters A=, A1=, B=, B1=, (see below).
• mode=1: Regular mode for solving 𝐴𝑢 = 𝜆𝑢
where the functions FOP, FB and FB1 define respectively the matrix product of 𝐴, 𝐵 and 𝐵 −1
• mode=3: Shift-invert mode for solving 𝐴𝑢 = 𝜆𝐵𝑢
where the functions FOP1 and FB define respectively the matrix product of 𝑂𝑃 −1 = (𝐴 − 𝜎𝐵)−1 and 𝐵
You can also specify which subset of eigenvalues you want to compute (which=). The default value is which="LM",
for eigenvalues with largest magnitude. "SM" is for smallest magnitude, "LA" for largest algebraic value, "SA" for
smallest algebraic value, and "BE" for both ends of the spectrum.
Remark: For complex problems, you need to use the keyword complexEigenValue instead of EigenValue when
passing operators through functions.
Note
If you put locking (Dirichlet) boundary condition on 𝐵 matrix (with key work on) you get small spurious modes
(10−30 ), due to boundary condition, but if you forget the locking boundary condition on 𝐵 matrix (no keywork on)
you get huge spurious (1030 ) modes associated to these boundary conditons. We compute only small mode, so we
get the good one in this case.
• sym= The problem is symmetric (all the eigen value are real)
• nev= The number desired eigenvalues (nev) close to the shift.
• value= The array to store the real part of the eigenvalues
• ivalue= The array to store the imaginary part of the eigenvalues
• vector= The FE function array to store the eigenvectors
• rawvector= An array of type real[int,int] to store eigenvectors by column.
For real non symmetric problems, complex eigenvectors are given as two consecutive vectors, so if eigenvalue 𝑘
and 𝑘 + 1 are complex conjugate eigenvalues, the 𝑘th vector will contain the real part and the 𝑘 + 1th vector the
imaginary part of the corresponding complex conjugate eigenvectors.
• tol= The relative accuracy to which eigenvalues are to be determined;
• sigma= The shift value;
• maxit= The maximum number of iterations allowed;
• ncv= The number of Arnoldi vectors generated at each iteration of ARPACK;
• mode= The computational mode used by ARPACK (see above);
• which= The requested subset of eigenvalues (see above).
Tip
Laplace eigenvalue
In the first example, we compute the eigenvalues and the eigenvectors of the Dirichlet problem on square Ω =]0, 𝜋[2 .
The problem is to find: 𝜆, and ∇𝑢𝜆 in R×𝐻01 (Ω)
∫︁ ∫︁
∇𝑢𝜆 ∇𝑣 = 𝜆 𝑢𝑣 ∀𝑣 ∈ 𝐻01 (Ω)
Ω Ω
The exact eigenvalues are 𝜆𝑛,𝑚 = (𝑛2 +𝑚2 ), (𝑛, 𝑚) ∈ N* 2 with the associated eigenvectors are 𝑢𝑚,𝑛 = sin(𝑛𝑥)*
sin(𝑚𝑦).
We use the generalized inverse shift mode of the arpack++ library, to find 20 eigenvalues and eigenvectors close
to the shift value 𝜎 = 20.
1 // Parameters
2 verbosity=0;
3 real sigma = 20; //value of the shift
4 int nev = 20; //number of computed eigen value close to sigma
5
6 // Mesh
7 mesh Th = square(20, 20, [pi*x, pi*y]);
8
9 // Fespace
10 fespace Vh(Th, P2);
11 Vh u1, u2;
12
13 // Problem
14 // OP = A - sigma B ; // the shifted matrix
15 varf op (u1, u2)
16 = int2d(Th)(
17 dx(u1)*dx(u2)
18 + dy(u1)*dy(u2)
19 - sigma* u1*u2
20 )
21 + on(1, 2, 3, 4, u1=0)
22 ;
23
26 matrix OP = op(Vh, Vh, solver=Crout, factorize=1); //crout solver because the matrix␣
˓→in not positive
29 // important remark:
30 // the boundary condition is make with exact penalization:
31 // we put 1e30=tgv on the diagonal term of the lock degree of freedom.
32 // So take Dirichlet boundary condition just on $a$ variational form
33 // and not on $b$ variational form.
34 // because we solve $ w=OP^-1*B*v $
35
36 // Solve
37 real[int] ev(nev); //to store the nev eigenvalue
38 Vh[int] eV(nev); //to store the nev eigenvector
39
(a) Isovalue of 11th eigenvector 𝑢4,3 − 𝑢3,4 (b) Isovalue of 12th eigenvector 𝑢4,3 + 𝑢3,4
We use the definition of the partial derivative of the solution in the time derivative:
𝜕𝑢 𝑢(𝑥, 𝑦, 𝑡) − 𝑢(𝑥, 𝑦, 𝑡 − 𝜏 )
(𝑥, 𝑦, 𝑡) = lim
𝜕𝑡 𝜏 →0 𝜏
which indicates that 𝑢𝑚 (𝑥, 𝑦) = 𝑢(𝑥, 𝑦, 𝑚𝜏 ) will satisfy approximatively:
Using the identity just above, we can calculate the finite element approximation 𝑢𝑚
ℎ of 𝑢
𝑚
in a step-by-step manner
with respect to 𝑡.
Tip
Example
We now solve the following example with the exact solution 𝑢(𝑥, 𝑦, 𝑡) = 𝑡𝑥4 , Ω =]0, 1[2 .
𝜕𝑢
𝜕𝑡 − 𝜇∆𝑢 = 𝑥4 − 𝜇12𝑡𝑥2 in Ω×]0, 3[
𝑢(𝑥, 𝑦, 0) = 0 on Ω
𝑢|𝜕Ω = 𝑡 * 𝑥4
1 // Parameters
2 real dt = 0.1;
3 real mu = 0.01;
4
5 // Mesh
6 mesh Th = square(16, 16);
7
8 // Fespace
9 fespace Vh(Th, P1);
10 Vh u, v, uu, f, g;
11
12 // Problem
25 // Time loop
26 real t = 0;
27 uu = 0;
28 for (int m = 0; m <= 3/dt; m++){
29 // Update
30 t = t+dt;
31 f = x^4 - mu*t*12*x^2;
32 g = t*x^4;
33 uu = u;
34
35 // Solve
36 dHeat;
37
38 // Plot
39 plot(u, wait=true);
40 cout << "t=" << t << " - L^2-Error=" << sqrt(int2d(Th)((u-t*x^4)^2)) << endl;
41 }
(︁∫︀ ⃒ ⃒2 )︁1/2
In the last statement, the 𝐿2 -error Ω ⃒𝑢 − 𝑡𝑥4 ⃒ is calculated at 𝑡 = 𝑚𝜏, 𝜏 = 0.1. At 𝑡 = 0.1, the error is
0.000213269. The errors increase with 𝑚 and 0.00628589 at 𝑡 = 3.
The iteration of the backward Euler (5.7) is made by for loop.
Note
The stiffness matrix in the loop is used over and over again. FreeFEM support reuses of stiffness matrix.
Let us denote the time step by 𝜏 > 0, 𝑁𝑇 = [𝑇 /𝜏 ]. For the discretization, we put 𝑢𝑛 = 𝑢(𝑛𝜏 ) and consider the time
difference for each 𝜃 ∈ [0, 1]
1
(︀ 𝑛+1
− 𝑢𝑛ℎ , 𝜑𝑖 + 𝑎 𝑢ℎ𝑛+𝜃 , 𝜑𝑖
)︀ (︀ )︀
𝜏 𝑢ℎ = ⟨𝑓 𝑛+𝜃 , 𝜑𝑖 ⟩
𝑖 = 1, · · · , 𝑚, 𝑛 = 0, · · · , 𝑁𝑇
𝑢𝑛+𝜃
ℎ = 𝜃𝑢𝑛+1
ℎ + (1 − 𝜃)𝑢𝑛ℎ ,
𝑛+𝜃 𝑛+1
𝑓 = 𝜃𝑓 + (1 − 𝜃)𝑓 𝑛
Formula (5.8) is the forward Euler scheme if 𝜃 = 0, Crank-Nicolson scheme if 𝜃 = 1/2, the backward Euler scheme if
𝜃 = 1.
Unknown vectors 𝑢𝑛 = (𝑢1ℎ , · · · , 𝑢𝑀
ℎ ) in
𝑇
Refer [TABATA1994], pp.70–75 for solvability of (5.8). The stability of (5.8) is in [TABATA1994], Theorem 2.13:
Let {𝒯ℎ }ℎ↓0 be regular triangulations (see Regular Triangulation). Then there is a number 𝑐0 > 0 independent of ℎ
such that,
{︃ {︁ ∑︀𝑛−1 𝑘+𝜃 2 }︁
1 0 2
|𝑢ℎ | + 𝜏 𝑘=0 ‖𝑓 ‖𝑉 ′ 𝜃 ∈ [0, 1/2)
|𝑢𝑛ℎ |2 ≤ 𝛿
0 2
∑︀𝑛−1 𝑘+𝜃 2 ℎ
2(1 − 𝛿) 2
𝜏< ℎ
(1 − 2𝜃)𝑐20
Tip
Example
1 // Parameters
2 real tau = 0.1; real
3 theta = 0.;
4
5 // Mesh
6 mesh Th = square(12, 12);
7
8 // Fespace
9 fespace Vh(Th, P1);
10 Vh u, v, oldU;
11 Vh f1, f0;
12
16 // Function
17 func real f (real t){
18 return x^2*(x-1)^2 + t*(-2 + 12*x - 11*x^2 - 2*x^3 + x^4);
19 }
20
21 // File
22 ofstream out("err02.csv"); //file to store calculations
23 out << "mesh size = " << h[].max << ", time step = " << tau << endl;
24 for (int n = 0; n < 5/tau; n++)
25 out << n*tau << ",";
26 out << endl;
27
28 // Problem
29 problem aTau (u, v)
30 = int2d(Th)(
31 u*v
32 + theta*tau*(dx(u)*dx(v) + dy(u)*dy(v) + u*v)
33 )
34 - int2d(Th)(
35 oldU*v
36 - (1-theta)*tau*(dx(oldU)*dx(v) + dy(oldU)*dy(v) + oldU*v)
37 )
38 - int2d(Th)(
39 tau*(theta*f1 + (1-theta)*f0)*v
40 )
41 ;
42
43 // Theta loop
44 while (theta <= 1.0){
45 real t = 0;
46 real T = 3;
47 oldU = 0;
48 out << theta << ",";
49 for (int n = 0; n < T/tau; n++){
50 // Update
51 t = t + tau;
52 f0 = f(n*tau);
53 f1 = f((n+1)*tau);
54
55 // Solve
56 aTau;
57 oldU = u;
58
59 // Plot
60 plot(u);
61
62 // Error
63 Vh uex = t*x^2*(1-x)^2; //exact solution = tx^2(1-x)^2
64 Vh err = u - uex; // err = FE-sol - exact
65 out << abs(err[].max)/abs(uex[].max) << ",";
66 }
Fig. 5.14: max𝑥∈Ω |𝑢𝑛ℎ (𝜃) − 𝑢𝑒𝑥 (𝑛𝜏 )| max𝑥∈Ω |𝑢𝑒𝑥 (𝑛𝜏 )|𝑎𝑡𝑛 = 0, 1, · · · , 29
We can see in Fig. 5.14 that 𝑢𝑛ℎ (𝜃) become unstable at 𝜃 = 0.4, and figures are omitted in the case 𝜃 < 0.4.
5.5.2 Convection
The hyperbolic equation
appears frequently in scientific problems, for example in the Navier-Stokes equations, in the Convection-Diffusion
equation, etc.
In the case of 1-dimensional space, we can easily find the general solution (𝑥, 𝑡) ↦→ 𝑢(𝑥, 𝑡) = 𝑢0 (𝑥 − 𝛼𝑡) of the
following equation, if 𝛼 is constant,
𝜕𝑡 𝑢 + 𝛼𝜕𝑥 𝑢 = 0
(5.9)
𝑢(𝑥, 0) = 𝑢0 (𝑥),
Even if 𝛼 is not constant, the construction works on similar principles. One begins with the ordinary differential
equation (with the convention that 𝛼 is prolonged by zero apart from (0, 𝐿) × (0, 𝑇 )):
In this equation 𝜏 is the variable and 𝑥, 𝑡 are parameters, and we denote the solution by 𝑋𝑥,𝑡 (𝜏 ). Then it is noticed that
(𝑥, 𝑡) → 𝑣(𝑋(𝜏 ), 𝜏 ) in 𝜏 = 𝑡 satisfies the equation
𝜕𝑡 𝑣 + 𝛼𝜕𝑥 𝑣 = 𝜕𝑡 𝑋 𝑣˙ + 𝑎𝜕𝑥 𝑋 𝑣˙ = 0
𝜕𝑡 𝑢 + 𝛼 · ∇𝑢 = 0 in Ω × (0, 𝑇 )
where a(𝑥, 𝑡) ∈ R𝑑 .
FreeFEM implements the Characteristic-Galerkin method for convection operators. Recall that the equation (5.8) can
be discretized as
𝐷𝑢 𝑑𝑢 𝑑𝑋
= 𝑓 i.e. (𝑋(𝑡), 𝑡) = 𝑓 (𝑋(𝑡), 𝑡) where (𝑡) = 𝛼(𝑋(𝑡), 𝑡)
𝐷𝑡 𝑑𝑡 𝑑𝑡
where 𝐷 is the total derivative operator. So a good scheme is one step of backward convection by the method of
Characteristics-Galerkin
1 (︀ 𝑚+1
(𝑥) − 𝑢𝑚 (𝑋 𝑚 (𝑥)) = 𝑓 𝑚+1 (𝑥) (5.10)
)︀
𝑢
𝜏
where 𝑋 𝑚 (𝑥) is an approximation of the solution at 𝑡 = 𝑚𝜏 of the ordinary differential equation
𝑑X
(𝑡) = 𝛼𝑚 (X(𝑡)), X((𝑚 + 1)𝜏 ) = 𝑥.
𝑑𝑡
where 𝛼𝑚 (𝑥) = (𝛼1 (𝑥, 𝑚𝜏 ), 𝛼2 (𝑥, 𝑚𝜏 )). Because, by Taylor’s expansion, we have
∑︀𝑑 𝑚
𝜕𝑋𝑖
𝑢𝑚 (X(𝑚𝜏 )) = 𝑢𝑚 (X((𝑚 + 1)𝜏 )) − 𝜏 𝑖=1 𝜕𝑢
𝜕𝑥𝑖 (X((𝑚 + 1)𝜏 )) 𝜕𝑡 ((𝑚 + 1)𝜏 ) + 𝑜(𝜏 )
𝑚 𝑚 𝑚
= 𝑢 (𝑥) − 𝜏 𝛼 (𝑥) · ∇𝑢 (𝑥) + 𝑜(𝜏 )
where 𝑋𝑖 (𝑡) are the i-th component of X(𝑡), 𝑢𝑚 (𝑥) = 𝑢(𝑥, 𝑚𝜏 ) and we used the chain rule and 𝑥 = X((𝑚 + 1)𝜏 ).
From (5.11), it follows that
Putting
convect(𝛼, −𝜏, 𝑢𝑚 ) ≈ 𝑢𝑚 (𝑥 − 𝛼𝑚 𝜏 )
we can get the approximation
1 , 𝑎2 ], −𝜏, 𝑢 ) by 𝑋
𝑢𝑚 (𝑋 𝑚 (𝑥)) ≈ convect ([𝑎𝑚 𝑚 𝑚 𝑚
≈ 𝑥 ↦→ 𝑥 − 𝜏 [𝑎𝑚 𝑚
1 (𝑥), 𝑎2 (𝑥)]
A classical convection problem is that of the “rotating bell” (quoted from [LUCQUIN1998], p.16).
Let Ω be the unit disk centered at 0, with its center rotating with speed 𝛼1 = 𝑦, 𝛼2 = −𝑥. We consider the problem
(5.8) with 𝑓 = 0 and the initial condition 𝑢(𝑥, 0) = 𝑢0 (𝑥), that is, from (5.10)
𝑢𝑚+1 (𝑥) = 𝑢𝑚 (𝑋 𝑚 (𝑥)) ≈ convect(𝛼, −𝜏, 𝑢𝑚 )
The exact solution is 𝑢(𝑥, 𝑡) = 𝑢(X(𝑡)) where X equals 𝑥 rotated around the origin by an angle 𝜃 = −𝑡 (rotate in
clockwise). So, if 𝑢0 in a 3D perspective looks like a bell, then 𝑢 will have exactly the same shape, but rotated by the
same amount. The program 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.
Tip
Convect
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(70));
7
8 // Fespace
9 fespace Vh(Th, P1);
10 Vh u0;
11 Vh a1 = -y, a2 = x; //rotation velocity
12 Vh u;
13
14 // Initialization
15 u = exp(-10*((x-0.3)^2 +(y-0.3)^2));
16
17 // Time loop
18 real t = 0.;
19 for (int m = 0; m < 2*pi/dt; m++){
20 // Update
21 t += dt;
22 u0 = u;
23
24 // Convect
25 u = convect([a1, a2], -dt, u0); //u^{m+1}=u^m(X^m(x))
26
27 // Plot
28 plot(u, cmm=" t="+t+", min="+u[].min+", max="+u[].max);
29 }
Note
The scheme convect is unconditionally stable, then the bell become lower and lower (the maximum of 𝑢37 is
0.406 as shown in Fig. 5.15a.
(a) 𝑢0 = 𝑒−10((𝑥−0.3)
2
+(𝑦−0.3)2 ) (b) The bell at 𝑡 = 6.29
Boundary conditions for this problem may not be so easy to device. As in the one dimensional case the PDE contains
boundary conditions on the axis 𝑥1 = 0 and on the axis 𝑥2 = 0, namely two one dimensional Black-Scholes equations
driven respectively by the data 𝑢 (0, +∞, 𝑇 ) and 𝑢 (+∞, 0, 𝑇 ). These will be automatically accounted for because they
are embedded in the PDE. So if we do nothing in the variational form (i.e. if we take a Neumann boundary condition
at these two axis in the strong form) there will be no disturbance to these. At infinity in one of the variable, as in 1D,
it makes sense to impose 𝑢 = 0. We take
An implicit Euler scheme is used and a mesh adaptation is done every 10 time steps. To have an unconditionally stable
scheme, the first order terms are treated by the Characteristic Galerkin method, which, roughly, approximates
𝜕𝑢 𝜕𝑢 𝜕𝑢 1 (︀ 𝑛+1
(𝑥) − 𝑢𝑛 (𝑥 − 𝛼𝜏 )
)︀
+ 𝑎1 + 𝑎2 ≈ 𝑢
𝜕𝑡 𝜕𝑥 𝜕𝑦 𝜏
Tip
Black-Scholes
1 // Parameters
2 int m = 30; int L = 80; int LL = 80; int j = 100; real sigx = 0.3; real sigy = 0.3;␣
˓→real rho = 0.3; real r = 0.05; real K = 40; real dt = 0.01;
4 // Mesh
5 mesh th = square(m, m, [L*x, LL*y]);
6
7 // Fespace
8 fespace Vh(th, P1);
9 Vh u = max(K-max(x,y),0.);
10 Vh xveloc, yveloc, v, uold;
11
12 // Time loop
13 for (int n = 0; n*dt <= 1.0; n++){
14 // Mesh adaptation
15 if (j > 20){
16 th = adaptmesh(th, u, verbosity=1, abserror=1, nbjacoby=2,
17 err=0.001, nbvx=5000, omega=1.8, ratio=1.8, nbsmooth=3,
18 splitpbedge=1, maxsubdiv=5, rescaling=1);
19 j = 0;
20 xveloc = -x*r + x*sigx^2 + x*rho*sigx*sigy/2;
21 yveloc = -y*r + y*sigy^2 + y*rho*sigx*sigy/2;
22 u = u;
23 }
24
25 // Update
26 uold = u;
27
28 // Solve
29 solve eq1(u, v, init=j, solver=LU)
30 = int2d(th)(
31 u*v*(r+1/dt)
32 + dx(u)*dx(v)*(x*sigx)^2/2
33 + dy(u)*dy(v)*(y*sigy)^2/2
34 + (dy(u)*dx(v) + dx(u)*dy(v))*rho*sigx*sigy*x*y/2
35 )
36 - int2d(th)(
37 v*convect([xveloc, yveloc], dt, uold)/dt
38 )
39 + on(2, 3, u=0)
40 ;
41
42 // Update
43 j = j+1;
44 };
45
46 // Plot
47 plot(u, wait=true, value=true);
(a) The adapted triangulation (b) The level line of the European basquet put option
where u = (𝑢1 , 𝑢2 ) is the velocity vector and 𝑝 the pressure. For simplicity, let us choose Dirichlet boundary conditions
on the velocity, u = uΓ on Γ.
In [TEMAM1977], Theorem 2.2, there is a weak form of (5.11):
Find v = (𝑣1 , 𝑣2 ) ∈ V(Ω):
which satisfy:
2 ∫︁
∑︁ ∫︁
∇𝑢𝑖 · ∇𝑣𝑖 = f ·w for all 𝑣 ∈ 𝑉
𝑖=1 Ω Ω
By multiplying the first equation in (5.11) with 𝑣 ∈ 𝑉 and the second with 𝑞 ∈ 𝑊 , subsequent integration over Ω, and
an application of Green’s formula, we have:
∇u · ∇v − ∫︀Ω divv 𝑝 = Ω f · v
∫︀ ∫︀ ∫︀
Ω
Ω
divu 𝑞 = 0
Note
Assume that:
1. There is a constant 𝛼ℎ > 0 such that:
where:
𝑏(vℎ , 𝑞ℎ )
sup ≥ 𝛽ℎ ‖𝑞ℎ ‖0,Ω for all 𝑞ℎ ∈ 𝑊ℎ
vℎ ∈Vℎ ‖vℎ ‖1,Ω
where:
𝐵𝑥𝑇
(︂ )︂ {︂ }︂ {︂ }︂ {︂ ∫︀ }︂
𝐴 0 {𝑢1,ℎ } {∫︀Ω 𝑓1 𝜑𝑖 }
A= B* = Uℎ = Fℎ =
0 𝐴 𝐵𝑦 𝑇 {𝑢2,ℎ } { Ω 𝑓2 𝜑𝑖 }
Penalty method: This method consists of replacing (5.12) by a more regular problem:
Find (vℎ𝜖 , 𝑝𝜖ℎ ) ∈ Vℎ × 𝑊
˜ ℎ satisfying:
where 𝑊
˜ ℎ ⊂ 𝐿2 (Ω). Formally, we have:
divu𝜖ℎ = 𝜖𝑝𝜖ℎ
A 𝐵* U𝜖ℎ
(︂ )︂ (︂ )︂ (︂ )︂
Fℎ
=
𝐵 −𝜖𝐼 {𝑝𝜖ℎ } 0
Note
Since the matrix 𝐴 + (1/𝜖)𝐵 * 𝐵 is symmetric, positive-definite, and sparse, (5.14) can be solved by known tech-
nique. There is a constant 𝐶 > 0 independent of 𝜖 such that:
Tip
Cavity
The driven cavity flow problem is solved first at zero Reynolds number (Stokes flow) and then at Reynolds 100.
The velocity pressure formulation is used first and then the calculation is repeated with the stream function vorticity
formulation.
We solve the driven cavity problem by the penalty method (5.13) where uΓ · n = 0 and uΓ · s = 1 on the top
boundary and zero elsewhere (n is the unit normal to Γ, and s the unit tangent to Γ).
The mesh is constructed by:
1 mesh Th = square(8, 8);
𝑋ℎ = v ∈ 𝐻 1 (]0, 1[2 ) ⃒ ∀𝐾 ∈ 𝒯ℎ
{︀ ⃒ }︀
𝑣|𝐾 ∈ 𝑃2
and:
𝑀ℎ = 𝑣 ∈ 𝐻 1 (]0, 1[2 ) ⃒ ∀𝐾 ∈ 𝒯ℎ
{︀ ⃒ }︀
𝑣|𝐾 ∈ 𝑃1
The Stokes operator is implemented as a system-solve for the velocity (𝑢1, 𝑢2) and the pressure 𝑝. The test function
for the velocity is (𝑣1, 𝑣2) and 𝑞 for the pressure, so the variational form (5.12) in freefem language is:
1 solve Stokes (u1, u2, p, v1, v2, q, solver=Crout)
2 = int2d(Th)(
3 (
4 dx(u1)*dx(v1)
5 + dy(u1)*dy(v1)
6 + dx(u2)*dx(v2)
7 + dy(u2)*dy(v2)
8 )
9 - p*q*(0.000001)
10 - p*dx(v1) - p*dy(v2)
11 - dx(u1)*q - dy(u2)*q
12 )
13 + on(3, u1=1, u2=0)
14 + on(1, 2, 4, u1=0, u2=0)
15 ;
−∆𝜓 = ∇ × 𝑢
1 Xh psi, phi;
2
This is implemented by using the convection operator convect for the term 𝜕𝑢
𝜕𝑡 + 𝑢 · ∇𝑢, giving a discretization
in time
1 𝑛+1
𝜏 (𝑢 − 𝑢𝑛 ∘ 𝑋 𝑛 ) − 𝜈∆𝑢𝑛+1 + ∇𝑝𝑛+1 = 0,
∇ · 𝑢𝑛+1 =0
The term 𝑢𝑛 ∘ 𝑋 𝑛 (𝑥) ≈ 𝑢𝑛 (𝑥 − 𝑢𝑛 (𝑥)𝜏 ) will be computed by the operator convect, so we obtain:
1 int i=0;
2 real alpha=1/dt;
3 problem NS (u1, u2, p, v1, v2, q, solver=Crout, init=i)
4 = int2d(Th)(
5 alpha*(u1*v1 + u2*v2)
6 + nu * (
7 dx(u1)*dx(v1) + dy(u1)*dy(v1)
8 + dx(u2)*dx(v2) + dy(u2)*dy(v2)
9 )
10 - p*q*(0.000001)
11 - p*dx(v1) - p*dy(v2)
12 - dx(u1)*q - dy(u2)*q
13 )
14 + int2d(Th)(
15 - alpha*convect([up1,up2],-dt,up1)*v1
16 - alpha*convect([up1,up2],-dt,up2)*v2
17 )
18 + on(3, u1=1, u2=0)
19 + on(1, 2, 4,u1=0, u2=0)
20 ;
21
22 // Time loop
23 for (i = 0; i <= 10; i++){
24 // Update
25 up1 = u1;
26 up2 = u2;
27
28 // Solve
29 NS;
30
31 // Plot
32 if (!(i % 10))
33 plot(coef=0.2, cmm="[u1,u2] and p", p, [u1, u2]);
34 }
Notice that the stiffness matrices are reused (keyword init=i)
The complete script is available in cavity example.
𝑝𝑛+1
ℎ = 𝑝𝑛ℎ + 𝜌𝑛 Bu𝑛ℎ
There is a constant 𝛼 > 0 such that 𝛼 ≤ 𝜌𝑛 ≤ 2 for each 𝑛, then u𝑛ℎ converges to the solution uℎ , and then 𝐵vℎ𝑛 → 0
as 𝑛 → ∞ from the Advance 𝑝ℎ . This method in general converges quite slowly.
First we define mesh, and the Taylor-Hood approximation. So 𝑋ℎ is the velocity space, and 𝑀ℎ is the pressure space.
Tip
Stokes Uzawa
1 // Mesh
2 mesh Th = square(10, 10);
3
4 // Fespace
5 fespace Xh(Th, P2);
6 Xh u1, u2;
7 Xh bc1, bc2;
8 Xh b;
9
14 // Problem
15 varf bx (u1, q) = int2d(Th)(-(dx(u1)*q));
16 varf by (u1, q) = int2d(Th)(-(dy(u1)*q));
17 varf a (u1, u2)
18 = int2d(Th)(
19 dx(u1)*dx(u2)
20 + dy(u1)*dy(u2)
21 )
22 + on(3, u1=1)
23 + on(1, 2, 4, u1=0) ;
24 //remark: put the on(3,u1=1) before on(1,2,4,u1=0)
25 //because we want zero on intersection
26
37 //compute u1(pp)
38 b[] = Bx'*pp;
39 b[] *= -1;
40 b[] += bc1[];
41 u1[] = A^-1*b[];
42 //compute u2(pp)
43 b[] = By'*pp;
44 b[] *= -1;
45 b[] += bc2[];
46 u2[] = A^-1*b[];
47 //u^n = (A^-1 Bx^T p^n, By^T p^n)^T
48 ppp[] = Bx*u1[]; //ppp = Bx u_1
49 ppp[] += By*u2[]; //+ By u_2
50
51 return ppp[] ;
52 }
53
54 // Initialization
55 p=0; //p_h^0 = 0
56 LinearCG(divup, p[], eps=1.e-6, nbiter=50); //p_h^{n+1} = p_h^n + B u_h^n
57 // if n> 50 or |p_h^{n+1} - p_h^n| <= 10^-6, then the loop end
58 divup(p[]); //compute the final solution
59
5.6.2 NSUzawaCahouetChabart.edp
In this example we solve the Navier-Stokes equation past a cylinder with the Uzawa algorithm preconditioned by the
Cahouet-Chabart method (see [GLOWINSKI2003] for all the details).
The idea of the preconditioner is that in a periodic domain, all differential operators commute and the Uzawa algorithm
comes to solving the linear operator ∇.((𝛼𝐼𝑑 + 𝜈∆)−1 ∇, where 𝐼𝑑 is the identity operator. So the preconditioner
suggested is 𝛼∆−1 + 𝜈𝐼𝑑.
To implement this, we do:
Tip
13 //TODO
14 real Um = 1.5; //max velocity (Rey 100)
15 real nu = 1e-3;
16
24 // Variables
25 func Ub = Um*2./3.;
26 real alpha = 1/dt;
27 real Rey = Ub*D/nu;
28 real t = 0.;
29
30 // Mesh
31 border fr1(t=0, 2.2){x=t; y=0; label=1;}
32 border fr2(t=0, H){x=2.2; y=t; label=2;}
33 border fr3(t=2.2, 0){x=t; y=H; label=1;}
34 border fr4(t=H, 0){x=0; y=t; label=1;}
35 border fr5(t=2*pi, 0){x=cx0+D*sin(t)/2; y=cy0+D*cos(t)/2; label=3;}
36 mesh Th = buildmesh(fr1(5*nn) + fr2(nn) + fr3(5*nn) + fr4(nn) + fr5(-nn*3));
37
38 // Fespace
39 fespace Mh(Th, [P1]);
40 Mh p;
41
48 // Macro
49 macro grad(u) [dx(u), dy(u)] //
50 macro div(u1, u2) (dx(u1) + dy(u2)) //
51
52 // Problem
53 varf von1 ([u1, u2, p], [v1, v2, q])
54 = on(3, u1=0, u2=0)
55 + on(1, u1=U1, u2=U2)
56 ;
57
58 //remark : the value 100 in next varf is manualy fitted, because free outlet.
59 varf vA (p, q) =
60 int2d(Th)(
61 grad(p)' * grad(q)
62 )
63 + int1d(Th, 2)(
64 100*p*q
65 )
66 ;
67
68 varf vM (p, q)
69 = int2d(Th, qft=qf2pT)(
70 p*q
71 )
72 + on(2, p=0)
73 ;
74
121 // Initialization
122 p = 0;
123
131 // Solve
132 int res = LinearCG(JUzawa, p[], precon=Precon, nbiter=100, verbosity=10,␣
˓→veps=eps);
133 assert(res==1);
134 eps = -abs(eps);
135
136 // Vorticity
137 w = -dy(u1) + dx(u2);
138 plot(w, fill=true, wait=0, nbiso=40);
139
140 // Update
141 dt = min(dt, T-t);
142 t += dt;
143 if(dt < 1e-10*T) break;
144 }
145
146 // Plot
147 plot(w, fill=true, nbiso=40);
148
149 // Display
150 cout << "u1 max = " << u1[].linfty
151 << ", u2 max = " << u2[].linfty
152 << ", p max = " << p[].max << endl;
Warning
Fig. 5.17: The vorticity at Reynolds number 100 a time 2s with the Cahouet-Chabart method.
The projection on a convex satisfy clearly ∀𝑣 ∈ 𝒞, ((𝑢 − 𝑣, 𝑢 − 𝑓˜)) ≤ 0, and after expanding, we get the classical
inequality:
∫︁ ∫︁
∀𝑣 ∈ 𝒞, ∇(𝑢 − 𝑣)∇𝑢 ≤ (𝑢 − 𝑣)𝑓
Ω Ω
The primal-dual active set algorithm to solve the previous problem is:
1. k=0, and choose 𝜆0 belong 𝐻 −1 (Ω)
2. Loop on 𝑘 = 0, .....
• set ℐ𝑘 = {𝑥 ∈ Ω/𝜆𝑘 + 𝑐 * (𝑢𝑘+1 − 𝑔) ≤ 0}
• 𝑉𝑔,𝑘+1 = {𝑣 ∈ 𝐻01 (Ω)/𝑣 = 𝑔 on 𝐼𝑘 },
• 𝑉0,𝑘+1 = {𝑣 ∈ 𝐻01 (Ω)/𝑣 = 0 on 𝐼𝑘 },
• Find 𝑢𝑘+1 ∈ 𝑉𝑔,𝑘+1 and 𝜆𝑘+1 ∈ 𝐻 −1 (Ω) such that
⎧ ∫︁ ∫︁
⎪
⎨ ∇𝑢𝑘+1 .∇𝑣𝑘+1 𝑑𝜔 = 𝑓 𝑣𝑘+1 , ∀𝑣𝑘+1 ∈ 𝑉0,𝑘+1
Ω ∫︁ Ω
⎩ < 𝜆𝑘+1 , 𝑣 >=
⎪ ∇𝑢𝑘+1 .∇𝑣 − 𝑓 𝑣 𝑑𝜔
Ω
where <, > is the duality bracket between 𝐻01 (Ω) and 𝐻 −1 (Ω), and 𝑐 is a penalty constant (large enough).
You can find all the mathematics about this algorithm in [ITO2003] [HINTERMULLER2002].
Now how to do that in FreeFEM? The full example is:
Tip
Variational inequality
1 load "medit"
2
3 // Parameters
4 real eps = 1e-5;
5 real c = 1000; //penalty parameter of the algoritm
6 real tgv = 1e30; //a huge value for exact penalization
7 func f = 1; //right hand side function
8 func fd = 0; //Dirichlet boundary condition function
9
10 // Mesh
11 mesh Th = square(20, 20);
12
13 // Fespace
14 fespace Vh(Th, P1);
15 int n = Vh.ndof; //number of degree of freedom
16 Vh uh, uhp; //u^n+1 and u^n
17 Vh Ik; //to define the set where the containt is reached.
18 Vh g = 0.05; //discret function g
19 Vh lambda = 0;
20
21 // Problem
22 varf a (uh, vh)
23 = int2d(Th)(
24 dx(uh)*dx(vh)
25 + dy(uh)*dy(vh)
26 )
27 - int2d(Th)(
28 f*vh
29 )
30 + on(1, 2, 3, 4, uh=fd)
31 ;
32
42 real[int] Aiin(n);
43 real[int] Aii = A.diag; //get the diagonal of the matrix
44 real[int] rhs = a(0, Vh, tgv=tgv);
45
46 // Initialization
47 Ik = 0;
48 uhp = -tgv;
49
50 // Loop
51 for(int iter = 0; iter < 100; ++iter){
52 // Update
53 real[int] b = rhs; //get a copy of the Right hand side
54 real[int] Ak(n); //the complementary of Ik ( !Ik = (Ik-1))
55 Ak = 1.; Ak -= Ik[];
56 //adding new locking condition on b and on the diagonal if (Ik ==1 )
57 b = Ik[] .* g[]; b *= tgv; b -= Ak .* rhs;
58 Aiin = Ik[] * tgv; Aiin += Ak .* Aii; //set Aii= tgv i in Ik
59 A.diag = Aiin; //set the matrix diagonal
60 set(A, solver=CG); //important to change preconditioning for solving
61
62 // Solve
63 uh[] = A^-1* b; //solve the problem with more locking condition
64
65 // Residual
66 lambda[] = AA * uh[]; //compute the residual (fast with matrix)
67 lambda[] += rhs; //remark rhs = -\int f v
68
71 // Plot
72 plot(Ik, wait=true, cmm=" lock set ", value=true, fill=true);
73 plot(uh, wait=true, cmm="uh");
74
75 // Error
76 //trick to compute L^2 norm of the variation (fast method)
77 real[int] diff(n), Mdiff(n);
78 diff = uh[] - uhp[];
79 Mdiff = M*diff;
80 real err = sqrt(Mdiff'*diff);
81 cout << "|| u_{k=1} - u_{k} ||_2 = " << err << endl;
82
83 // Stop test
84 if(err < eps) break;
85
86 // Update
87 uhp[] = uh[];
88 }
89
90 // Plot
91 medit("uh", Th, uh);
Note
As you can see on this example, some vector, or matrix operator are not implemented so a way is to skip the
expression and we use operator +=, -= to merge the result.
−∆𝑢 = 𝑓, in Ω = Ω1 ∪ Ω2 𝑢|Γ = 0
−∆𝑢𝑛+1
1 = 𝑓 in Ω1 𝑢𝑛+1
1 |Γ1 = 𝑢𝑛2
−∆𝑢𝑛+1
2 = 𝑓 in Ω2 𝑛+1
𝑢2 |Γ2 = 𝑢𝑛1
where Γ𝑖 is the boundary of Ω𝑖 and on the condition that Ω1 ∩ Ω2 ̸= ∅ and that 𝑢𝑖 are zero at iteration 1.
Here we take Ω1 to be a quadrangle, Ω2 a disk and we apply the algorithm starting from zero.
Tip
Schwarz overlapping
1 // Parameters
2 int inside =2; //inside boundary
3 int outside = 1; //outside boundary
4 int n = 4;
5
6 // Mesh
7 border a(t=1, 2){x=t; y=0; label=outside;}
17 // Fespace
18 fespace vh(th, P1);
19 vh u=0, v;
20
24 // Problem
25 int i = 0;
26 problem PB (U, V, init=i, solver=Cholesky)
27 = int2d(TH)(
28 dx(U)*dx(V)
29 + dy(U)*dy(V)
30 )
31 + int2d(TH)(
32 - V
33 )
34 + on(inside, U=u)
35 + on(outside, U=0)
36 ;
37
50 // Calculation loop
51 for (i = 0 ; i < 10; i++){
52 // Solve
53 PB;
54 pb;
55
56 // Plot
57 plot(U, u, wait=true);
58 }
(a) Isovalues of the solution at iteration 0 (b) Isovalues of the solution at iteration 0
−∆𝑢 = 𝑓 in Ω = Ω1 ∪ Ω2 𝑢|Γ = 0
the Schwarz algorithm for domain decomposition without overlapping runs like this
The problem find 𝜆 such that (𝑢1 |Γ𝑖 = 𝑢2 |Γ𝑖 ) where 𝑢𝑖 is solution of the following Laplace problem:
(𝑢1 − 𝑢2 )
𝜆 = 𝜆±
2
where the sign + or − of ± is choose to have convergence.
Tip
Schwarz non-overlapping
1 // Parameters
2 int inside = 2; int outside = 1; int n = 4;
3
4 // Mesh
5 border a(t=1, 2){x=t; y=0; label=outside;};
6 border b(t=0, 1){x=2; y=t; label=outside;};
7 border c(t=2, 0){x=t; y=1; label=outside;};
8 border d(t=1, 0){x=1-t; y=t; label=inside;};
9 border e(t=0, 1){x=1-t; y=t; label=inside;};
10 border e1(t=pi/2, 2*pi){x=cos(t); y=sin(t); label=outside;};
11 mesh th = buildmesh(a(5*n) + b(5*n) + c(10*n) + d(5*n));
12 mesh TH = buildmesh(e(5*n) + e1(25*n));
13 plot(th, TH, wait=true);
14
15 // Fespace
16 fespace vh(th, P1);
17 vh u=0, v;
18 vh lambda=0;
19
23 // Problem
24 int i = 0;
25 problem PB (U, V, init=i, solver=Cholesky)
26 = int2d(TH)(
27 dx(U)*dx(V)
28 + dy(U)*dy(V)
29 )
30 + int2d(TH)(
31 - V
32 )
33 + int1d(TH, inside)(
34 lambda*V
35 )
36 + on(outside, U= 0 )
37 ;
38
41 dx(u)*dx(v)
42 + dy(u)*dy(v)
43 )
44 + int2d(th)(
45 - v
46 )
47 + int1d(th, inside)(
48 - lambda*v
49 )
50 + on(outside, u=0)
51 ;
52
59 // Plot
60 plot(U,u,wait=true);
61 }
62
63 // Plot
64 plot(U, u);
(a) Isovalues of the solution at iteration 0 without overlap- (b) Isovalues of the solution at iteration 9 without overlap-
ping ping
The problem find 𝜆 such that (𝑢1 |Γ𝑖 = 𝑢2 |Γ𝑖 ) where 𝑢𝑖 is solution of the following Laplace problem:
The version of this example uses the Shur complement. The problem on the border is solved by a conjugate gradient
method.
Tip
4 // Mesh
5 border Gamma1(t=1, 2){x=t; y=0; label=outside;}
6 border Gamma2(t=0, 1){x=2; y=t; label=outside;}
7 border Gamma3(t=2, 0){x=t; y=1; label=outside;}
8 border GammaInside(t=1, 0){x=1-t; y=t; label=inside;}
9 border GammaArc(t=pi/2, 2*pi){x=cos(t); y=sin(t); label=outside;}
10 mesh Th1 = buildmesh(Gamma1(5*n) + Gamma2(5*n) + GammaInside(5*n) + Gamma3(5*n));
11 mesh Th2 = buildmesh(GammaInside(-5*n) + GammaArc(25*n));
12 plot(Th1, Th2);
7 fespace Vh2(Th2,P1);
8 Vh2 u2, v2;
Note
It is impossible to define a function just on a part of boundary, so the 𝜆 function must be defined on the all
domain Ω1 such as:
1 Vh1 lambda;
And, we define a border matrix, because the 𝜆 function is none zero inside the domain Ω1 :
1 varf b(u2, v2, solver=CG) = int1d(Th1, inside)(u2*v2);
2 matrix B = b(Vh1, Vh1, solver=CG);
Note
The difference between the two notations v1 and v1[] is: v1 is the finite element function and v1[] is the
vector in the canonical basis of the finite element function v1.
1 // Solve
2 real cpu=clock();
3 LinearCG(BoundaryProblem, p[], eps=1.e-6, nbiter=100);
4 //compute the final solution, because CG works with increment
5 BoundaryProblem(p[]); //solve again to have right u1, u2
6
−∆u + ∇𝑝 = 0 in Ω
∇·u = 0 in Ω
u = uΓ on Γ = 𝜕Ω
where 𝑢Γ is the velocity of the boundaries. The force that the fluid applies to the boundaries is the normal stress
h = (∇u + ∇u𝑇 )n − 𝑝n
Elastic solids subject to forces deform: a point in the solid at (x,y) goes to (X,Y) after. When the displacement vector
v = (𝑣1 , 𝑣2 ) = (𝑋 − 𝑥, 𝑌 − 𝑦) is small, Hooke’s law relates the stress tensor 𝜎 inside the solid to the deformation
tensor 𝜖:
1 𝜕𝑣𝑖 𝜕𝑣𝑗
𝜎𝑖𝑗 = 𝜆𝛿𝑖𝑗 ∇.v + 2𝜇𝜖𝑖𝑗 , 𝜖𝑖𝑗 = ( + )
2 𝜕𝑥𝑗 𝜕𝑥𝑖
where 𝛿 is the Kronecker symbol and where 𝜆, 𝜇 are two constants describing the material mechanical properties in
terms of the modulus of elasticity, and Young’s modulus.
The equations of elasticity are naturally written in variational form for the displacement vector 𝑣(𝑥) ∈ 𝑉 as:
∫︁ ∫︁ ∫︁
[2𝜇𝜖𝑖𝑗 (v)𝜖𝑖𝑗 (w) + 𝜆𝜖𝑖𝑖 (𝑣)𝜖𝑗𝑗 (w)] = g · w + h · w, ∀w ∈ 𝑉
Ω Ω Γ
The data are the gravity force g and the boundary stress h.
Tip
Fluide-structure In our example, the Lamé system and the Stokes system are coupled by a common boundary on
which the fluid stress creates a displacement of the boundary and hence changes the shape of the domain where the
Stokes problem is integrated. The geometry is that of a vertical driven cavity with an elastic lid. The lid is a beam
with weight so it will be deformed by its own weight and by the normal stress due to the fluid reaction. The cavity
is the 10 × 10 square and the lid is a rectangle of height 𝑙 = 2.
A beam sits on a box full of fluid rotating because the left vertical side has velocity one. The beam is bent by its
own weight, but the pressure of the fluid modifies the bending.
The bending displacement of the beam is given by (𝑢𝑢, 𝑣𝑣) whose solution is given as follows.
1 // Parameters
2 int bottombeam = 2; //label of bottombeam
3 real E = 21.5;
4 real sigma = 0.29;
5 real gravity = -0.05;
6 real coef = 0.2;
7
8 // Mesh (solid)
9 border a(t=2, 0){x=0; y=t; label=1;}
10 border b(t=0, 10){x=t; y=0; label=bottombeam;}
11 border c(t=0, 2){x=10; y=t; label=1;}
12 border d(t=0, 10){x=10-t; y=2; label=3;}
13 mesh th = buildmesh(b(20) + c(5) + d(20) + a(5));
14
15 // Fespace (solid)
16 fespace Vh(th, P1);
17 Vh uu, w, vv, s;
18
19 // Macro
20 real sqrt2 = sqrt(2.);
21 macro epsilon(u1, u2) [dx(u1), dy(u2), (dy(u1)+dx(u2))/sqrt2] //
22 macro div(u1, u2) (dx(u1) + dy(u2)) //
23
24 // Problem (solid)
25 real mu = E/(2*(1+sigma));
26 real lambda = E*sigma/((1+sigma)*(1-2*sigma));
27 solve Elasticity([uu, vv], [w, s])
28 = int2d(th)(
29 lambda*div(w,s)*div(uu,vv)
30 + 2.*mu*(epsilon(w,s)'*epsilon(uu,vv))
31 )
32 + int2d(th)(
33 - gravity*s
34 )
35 + on(1, uu=0, vv=0)
36 ;
37
We use the Uzawa conjugate gradient to solve the Stokes problem like in Navier-Stokes equations.
1 // Fespace (fluid)
11 // Problem (fluid)
12 varf bx (u1, q) = int2d(sh)(-(dx(u1)*q));
13 varf by (u1, q) = int2d(sh)(-(dy(u1)*q));
14 varf Lap (u1, u2)
15 = int2d(sh)(
16 dx(u1)*dx(u2)
17 + dy(u1)*dy(u2)
18 )
19 + on(2, u1=1)
20 + on(1, 3, u1=0)
21 ;
22
29
1 // Coupling loop
2 for(int step = 0; step < 10; ++step){
3 // Solve (fluid)
4 LinearCG(divup, p[], eps=1.e-3, nbiter=50);
5 divup(p[]);
Now the beam will feel the stress constraint from the fluid:
1 // Forces
2 Vh sigma11, sigma22, sigma12;
3 Vh uu1=uu, vv1=vv;
4
1 // Solve (solid)
2 solve Elasticity2 ([uu, vv], [w, s], init=step)
3 = int2d(th)(
4 lambda*div(w,s)*div(uu,vv)
5 + 2.*mu*(epsilon(w,s)'*epsilon(uu,vv))
6 )
7 + int2d(th)(
8 - gravity*s
9 )
10 + int1d(th, bottombeam)(
11 - coef*(sigma11*N.x*w + sigma22*N.y*s + sigma12*(N.y*w+N.x*s))
12 )
13 + on(1, uu=0, vv=0)
14 ;
15
16 // Plot
17 plot([uu, vv], wait=1);
18
19 // Error
20 real err = sqrt(int2d(th)((uu-uu1)^2 + (vv-vv1)^2));
21 cout << "Erreur L2 = " << err << endl;
Notice that the matrix generated by Elasticity2 is reused (see init=i). Finally we deform the beam:
1 // Movemesh
2 th1 = movemesh(th, [x+0.2*uu, y+0.2*vv]);
3 plot(th1, wait=true);
Fluid velocity and pressure, displacement vector of the structure and displaced geometry in the fluid-structure
interaction of a soft side and a driven cavity are shown Fig. 5.22, Fig. 5.23a and Fig. 5.23b
−𝜇𝑖 ∆𝑢 = 𝑓 in Ω𝑖
(5.15)
𝜇𝑖 𝜕𝑛 𝑢|Γ𝑖 = −𝜇𝑗 𝜕𝑛 𝑢|Γ𝑗 on Ω𝑖 ∩ Ω𝑗 if 1 ≤ 𝑖 < 𝑗 ≤ 3
where 𝜕𝑛 𝑢|Γ𝑖 denotes the value of the normal derivative 𝜕𝑛 𝑢 on the boundary Γ𝑖 of the domain Ω𝑖 .
By introducing the characteristic function 𝜒𝑖 of Ω𝑖 , that is:
𝜒𝑖 (𝑥) = 1 if 𝑥 ∈ Ω𝑖 ; 𝜒𝑖 (𝑥) = 0 if 𝑥 ̸∈ Ω𝑖
we can easily rewrite (5.15) to the weak form. Here we assume that 𝑢 = 0 on Γ = 𝜕Ω.
Transmission problem: For a given function 𝑓 , find 𝑢 such that:
1 // Mesh
2 border a(t=0, 1){x=t; y=0;};
3 border b(t=0, 0.5){x=1; y=t;};
4 border c(t=0, 0.5){x=1-t; y=0.5;};
5 border d(t=0.5, 1){x=0.5; y=t;};
6 border e(t=0.5, 1){x=1-t; y=1;};
7 border f(t=0, 1){x=0; y=1-t;};
8 border i1(t=0, 0.5){x=t; y=1-t;};
9 border i2(t=0, 0.5){x=t; y=t;};
10 border i3(t=0, 0.5){x=1-t; y=t;};
11 mesh th = buildmesh(a(6) + b(4) + c(4) +d(4) + e(4)
12 + f(6) + i1(6) + i2(6) + i3(6));
13
14 // Fespace
15 fespace Ph(th, P0); //constant discontinuous functions / element
16 Ph reg=region; //defined the P0 function associated to region number
17
18 // Plot
19 plot(reg, fill=true, wait=true, value=true);
region is a keyword of FreeFEM which is in fact a variable depending of the current position (is not a function today,
use Ph reg=region; to set a function). This variable value returned is the number of the sub-domain of the current
position. This number is defined by buildmesh which scans while building the mesh all its connected component.
So to get the number of a region containing a particular point one does:
1 // Characteristic function
2 int nupper = reg(0.4, 0.9); //get the region number of point (0.4,0.9)
3 int nlower = reg(0.9, 0.1); //get the region number of point (0.4,0.1)
4 cout << "nlower = " << nlower << ", nupper = " << nupper<< endl;
5 Ph nu = 1 + 5*(region==nlower) + 10*(region==nupper);
6
7 // Plot
8 plot(nu, fill=true,wait=true);
This is particularly useful to define discontinuous functions such as might occur when one part of the domain is copper
and the other one is iron, for example.
We this in mind we proceed to solve a Laplace equation with discontinuous coefficients (𝜈 is 1, 6 and 11 below).
1 // Problem
2 solve lap (u, v)
3 = int2d(th)(
4 nu*(dx(u)*dx(v) + dy(u)*dy(v))
5 )
6 + int2d(th)(
7 - 1*v
8 )
9 + on(a, b, c, d, e, f, u=0)
10 ;
11
12 // Plot
13 plot(u);
1 // Parameters
2 real L = 10; //length
3 real hl = 2.1; //left height
4 real hr = 0.35; //right height
5 int n = 4;
6
7 // Mesh
8 border a(t=0, L){x=t; y=0;}; //bottom: Gamma_a
9 border b(t=0, hr){x=L; y=t;}; //right: Gamma_b
10 border f(t=L, 0){x=t; y=t*(hr-hl)/L+hl;}; //free surface: Gamma_f
11 border d(t=hl, 0){x=0; y=t;}; // left: Gamma_d
12 mesh Th = buildmesh(a(10*n) + b(6*n) + f(8*n) + d(3*n));
13 plot(Th);
in Ω𝑛
⎧
⎪ −∆𝑢 = 0
on Γ𝑛𝑏
⎪
⎨ 𝑢 = 𝑦
⎪ 𝜕𝑢
𝜕𝑛 = 0 on Γ𝑛𝑑 ∪ Γ𝑛𝑎
on Γ𝑛𝑓
⎪
𝑢 = 𝑦
⎩
And secondly to construct a domain deformation ℱ(𝑥, 𝑦) = [𝑥, 𝑦 − 𝑣(𝑥, 𝑦)] where 𝑣 is solution of the following
problem:
in Ω𝑛
⎧
⎪ −∆𝑣 = 0
on Γ𝑛𝑎
⎪
⎨ 𝑣 = 0
⎪ 𝜕𝑣
𝜕𝑛 = 0 on Γ𝑛𝑏 ∪ Γ𝑛𝑑
𝑞
on Γ𝑛𝑓
⎪ 𝜕𝑣 𝜕𝑢
= − 𝐾 𝑛𝑥
⎩
𝜕𝑛 𝜕𝑛
Tip
Free boundary
The FreeFEM implementation is:
1 // Parameters
2 real L = 10; //length
3 real hr = 2.1; //left height
4 real hl = 0.35; //right height
5 int n = 4;
6
10 // Mesh
18 // Fespace
19 fespace Vh(Th, P1);
20 Vh u, v;
21 Vh uu, vv;
22
23 // Problem
24 problem Pu (u, uu, solver=CG)
25 = int2d(Th)(
26 dx(u)*dx(uu)
27 + dy(u)*dy(uu)
28 )
29 + on(b, f, u=y)
30 ;
31
43 // Loop
44 int j = 0;
45 real errv = 1.;
46 real erradap = 0.001;
47 while (errv > 1e-6){
48 // Update
49 j++;
50
51 // Solve
52 Pu;
53 Pv;
54
55 // Plot
56 plot(Th, u, v);
57
58 // Error
59 errv = int1d(Th, f)(v*v);
60
61 // Movemesh
62 real coef = 1.;
63 real mintcc = checkmovemesh(Th, [x, y])/5.;
71 while (1){
72 real mint = checkmovemesh(Th, [x, y-v*coef]);
73
82 // Display
83 cout << endl << j << " - errv = " << errv << endl;
84 }
85
86 // Plot
87 plot(Th);
88 plot(u, wait=true);
(a) The final solution on the new domain Ω72 (b) The adapted mesh of the domain Ω72
where 𝐹 2(𝑢1 , 𝑢2 ) = 𝐴(𝐸[𝑢1 , 𝑢2 ], 𝐸[𝑢1 , 𝑢2 ]) and 𝐴(𝑋, 𝑌 ) is bilinear symmetric positive form with respect two matrix
𝑋, 𝑌 .
where 𝑓 is a given 𝒞 2 function, and 𝐸[𝑢1 , 𝑢2 ] = (𝐸𝑖𝑗 )𝑖=1,2, 𝑗=1,2 is the Green-Saint Venant deformation tensor defined
with:
(︀ ∑︁ )︀
𝐸𝑖𝑗 = 0.5 (𝜕𝑖 𝑢𝑗 + 𝜕𝑗 𝑢𝑖 ) + 𝜕𝑖 𝑢𝑘 ×𝜕𝑗 𝑢𝑘
𝑘
where:
3 macro ENL(u, v) [
4 (dx(u)*dx(u) + dx(v)*dx(v))*0.5,
5 (dx(u)*dy(u) + dx(v)*dy(v)),
6 (dy(u)*dy(u) + dy(v)*dy(v))*0.5
7 ] //
8
– 𝑢𝑛 = 𝑢𝑛 − 𝑑𝑢, 𝑣𝑛 = 𝑣𝑛 − 𝑑𝑣
– until (𝑑𝑢, 𝑑𝑣) small is enough
The way to implement this algorithm in FreeFEM is use a macro tool to implement 𝐴 and 𝐹 2, 𝑓 , 𝑓 ′ ,𝑓 ′′ .
A macro is like in ccp preprocessor of C++, but this begin by macro and the end of the macro definition is before the
comment //. In this case the macro is very useful because the type of parameter can be change. And it is easy to make
automatic differentiation.
1 // Macro
2 macro EL(u, v) [dx(u), (dx(v)+dy(u)), dy(v)] //is [epsilon_11, 2epsilon_12, epsilon_22]
3
4 macro ENL(u, v) [
5 (dx(u)*dx(u) + dx(v)*dx(v))*0.5,
6 (dx(u)*dy(u) + dx(v)*dy(v)),
7 (dy(u)*dy(u) + dy(v)*dy(v))*0.5
8 ] //
9
30 // Parameters
31 real mu = 0.012e5; //kg/cm^2
32 real lambda = 0.4e5; //kg/cm^2
33 real Pa = 1e2;
34
58 //the matrix A
59 func A = [[a11, a12, a13], [a21, a22, a23], [a31, a32, a33]];
60
61 // Mesh
62 int n = 30;
63 int m = 10;
64 mesh Th = square(n, m, [x, .3*y]); //label: 1 bottom, 2 right, 3 up, 4 left;
65 int bottom = 1, right = 2, upper = 3, left = 4;
66 plot(Th);
67
68 // Fespace
69 fespace Wh(Th, P1dc);
70 Wh e2, fe2, dfe2, ddfe2;
71
78 // Problem
79 varf vmass ([uu, vv], [w, s], solver=CG) = int2d(Th)(uu*w + vv*s);
80 matrix M = vmass(Vh, Vh);
81 problem NonLin([uu, vv], [w, s], solver=LU)
82 = int2d(Th, qforder=1)( //(D^2 J(un))
83 dF2(un, vn, uu, vv)*dF2(un, vn, w, s)*ddfe2
84 + ddF2(un, vn, uu, vv, w, s)*ddfe2
85 )
86 - int1d(Th, upper)(
(continues on next page)
95 // Newton's method
96 for (int i = 0; i < 10; i++){
97 cout << "Loop " << i << endl;
98
99 // Update
100 e2 = F2(un, vn);
101 dfe2 = df(e2) ;
102 ddfe2 = ddf(e2);
103 cout << "e2 max = " <<e2[].max << ", min = " << e2[].min << endl;
104 cout << "de2 max = "<< dfe2[].max << ", min = " << dfe2[].min << endl;
105 cout << "dde2 max = "<< ddfe2[].max << ", min = " << ddfe2[].min << endl;
106
107 // Solve
108 NonLin;
109 w[] = M*uu[];
110
111 // Residual
112 real res = sqrt(w[]' * uu[]); //L^2 norm of [uu, vv]
113 cout << " L^2 residual = " << res << endl;
114
115 // Update
116 v1 = vv;
117 u1 = uu;
118 cout << "u1 min = " <<u1[].min << ", u1 max = " << u1[].max << endl;
119 cout << "v1 min = " <<v1[].min << ", v2 max = " << v1[].max << endl;
120
121 // Plot
122 plot([uu, vv], wait=true, cmm="uu, vv");
123
124 // Update
125 un[] -= uu[];
126 plot([un, vn], wait=true, cmm="displacement");
127
131 // Plot
132 plot([un, vn], wait=true);
133
134 // Movemesh
135 mesh th1 = movemesh(Th, [x+un, y+vn]);
136
137 // Plot
138 plot(th1, wait=true);
5.13.1 Notation
In what follows, the symbols u, F, B, C, 𝜎 denote, respectively, the displacement field, the deformation gradient, the
left Cauchy-Green strain tensor B = FF𝑇 , the right Cauchy-Green strain tensor C = F𝑇 F, and the Cauchy stress
tensor.
We also introduce the symbols 𝐼1 := tr C and 𝐽 := det F. Use will be made of the identity:
𝜕𝐽
= 𝐽C−1
𝜕C
The symbol I denotes the identity tensor. The symbol Ω0 denotes the reference configuration of the body to be de-
formed. The unit volume in the reference (resp., deformed) configuration is denoted 𝑑𝑉 (resp., 𝑑𝑉0 ); these two are
related by:
𝑑𝑉 = 𝐽𝑑𝑉0 ,
which allows an integral over Ω involving the Cauchy stress T to be rewritten as an integral of the Kirchhoff stress
𝜅 = 𝐽T over Ω0 .
𝜕𝑊
S𝑛 := (F𝑛 ) = 𝜇(I − C−1 )
𝜕E
The Kirchhoff stress, then, is:
𝜅 = FSF𝑇 = 𝜇(B − I)
𝜕𝜅
(F𝑛 )𝛿F𝑛+1 = 𝜇 F𝑛 (𝛿F𝑛+1 )𝑇 + 𝛿F𝑛+1 (F𝑛 )𝑇
[︀ ]︀
𝜕F
The Weak Form of the BVP in the Absence of Body (External) Forces
The Ω0 we are considering is an elliptical annulus, whose boundary consists of two concentric ellipses (each allowed
to be a circle as a special case), with the major axes parallel. Let 𝑃 denote the dead stress load (traction) on a portion
𝜕Ω𝑡0 (= the inner ellipse) of the boundary 𝜕Ω0 . On the rest of the boundary, we prescribe zero displacement.
For brevity, in the rest of this section we assume 𝑃 = 0. The provided FreeFEM code, however, does not rely on this
assumption and allows for a general value and direction of 𝑃 .
Given a Newton approximation u𝑛 of the displacement field u satisfying the BVP, we seek the correction 𝛿u𝑛+1 to
obtain a better approximation:
u𝑛+1 = u𝑛 + 𝛿u𝑛+1
𝛿F𝑛+1 = ∇ ⊗ 𝛿u𝑛+1
Note
Contrary to standard notational use, the symbol 𝛿 here bears no variational context. By 𝛿 we mean simply an
increment in the sense of Newton’s Method. The role of a variational virtual displacement here is played by w.
D2 := F−𝑇
𝑛 𝛿F𝑛+1 ,
D3 := (∇ ⊗ w)F−2
𝑛 𝛿F𝑛+1 ,
and
D4 := (∇ ⊗ w)F−1
𝑛 ,
respectively.
In the above notation, the tangent Kirchhoff stress term becomes
𝜕𝜅
(F𝑛 ) 𝛿F𝑛+1 = 𝜇 D1
𝜕F
while the weak BVP formulation acquires the form:
∫︀ ⎫
0 = ∫︀Ω0 𝜅[F𝑛 ] : D4 ⎬
− Ω0 𝜅[F𝑛 ] : D3
∫︀ {︀ 𝜕𝜅 }︀ for all test functionsw
+ Ω0 𝜕F [F𝑛 ]𝛿F𝑛+1 : D4
⎭
1 // Macro
2 //Macros for the gradient of a vector field (u1, u2)
3 macro grad11(u1, u2) (dx(u1)) //
4 macro grad21(u1, u2) (dy(u1)) //
5 macro grad12(u1, u2) (dx(u2)) //
6 macro grad22(u1, u2) (dy(u2)) //
7
86 //The macros for the auxiliary tensors (D0, D1, D2, ...): Begin
87 //The tensor quantity D0 = F{n} (delta F{n+1})^T
(continues on next page)
237 // Parameters
238 real mu = 5.e2; //Elastic coefficients (kg/cm^2)
239 real D = 1.e3; //(1 / compressibility)
240 real Pa = -3.e2; //Stress loads
241
246
249 // Mesh
250 border InnerEdge(t=0, 2.*pi){x=(1.0 + InnerEllipseExtension)*InnerRadius*cos(t);␣
˓→y=InnerRadius*sin(t); label=1;}
256 // Fespace
257 fespace Wh(Th, P1dc);
258 fespace Vh(Th, [P1, P1]);
259 Vh [w1, w2], [u1n,u2n], [varu1, varu2];
260 Vh [ehat1x, ehat1y], [ehat2x, ehat2y];
261 Vh [auxVec1, auxVec2]; //The individual elements of the total 1st Piola-Kirchoff stress
262 Vh [ef1, ef2];
263
269 // Problem
270 varf vfMass1D(p, q) = int2d(Th)(p*q);
271 matrix Mass1D = vfMass1D(Sh, Sh, solver=CG);
272
273 p[] = 1;
274 ppp[] = Mass1D * p[];
275
330 //Initialization:
331 [varu1, varu2] = [0., 0.];
332 [u1n, u2n] = [0., 0.];
333 real res = 2. * tol;
334 real eforceres;
335 int loopcount = 0;
336 int loopmax = 45;
337
338 // Iterations
339 while (loopcount <= loopmax && res >= tol){
340 loopcount ++;
341 cout << "Loop " << loopcount << endl;
342
343 // Solve
344 neoHookeanInc;
(continues on next page)
346 // Update
347 u1 = varu1;
348 u2 = varu2;
349
350 // Residual
351 w1[] = Mass*varu1[];
352 res = sqrt(w1[]' * varu1[]); //L^2 norm of [varu1, varu2]
353 cout << " L^2 residual = " << res << endl;
354
355 // Newton
356 u1n[] += varu1[];
357
358 // Plot
359 plot([u1n,u2n], cmm="displacement");
360 }
361
362 // Plot
363 plot(Th, wait=true);
364
365 // Movemesh
366 mesh Th1 = movemesh(Th, [x+u1n, y+u2n]);
367
368 // Plot
369 plot(Th1, wait=true);
370 plot([u1n,u2n]);
Here 𝑘0 = 𝜔/𝑐 is the wavenumber, 𝛼 is the penalty term added to fight spurious FEM solutions. For anisotropic
single-axis medium with 𝜕ˆ
𝜖/𝜕𝜑 = 0 in cylindrical system of coordinates we have:
⎛ ⎞
𝜖𝜌 0 0
𝜖ˆ = ⎝ 0 𝜖𝜌 0 ⎠ .
0 0 𝜖𝑧
We now assume axial symmetry of our electromagnetic fields and insert an imaginary unity in front of the 𝐻𝜑 to
allow all field components to be real numbers and also to account for the phase shift of this component 𝐻(𝜌,
⃗ 𝜑, 𝑧) =
{𝐻𝜌 (𝜌, 𝑧), 𝑖𝐻𝜑 (𝜌, 𝑧), 𝐻𝑧 (𝜌, 𝑧)} × 𝑒 𝑖𝑚𝜑
.
We write the wave equation (5.16) explicitly in cylindrical coordinates, thus obtaining a set of three differential equa-
tions for the domain Ω given by the resonator’s cross section and some space outside:
The numerical solutions of these equations and boundary conditions can be found with FreeFEM if we write the system
in the weak, or integral form.
We can reduce the order of partial derivatives in this integral by using the Green’s formula for integration by parts. For
example:
𝜕 2 𝐻𝑧 𝜕𝐻𝑧𝑡 𝜕𝐻𝑧
∫︁ ∫︁ ∮︁
𝜕𝐻𝑧
𝐻𝑧𝑡 𝑑Ω = − 𝑑Ω + 𝐻𝑧𝑡 𝑛𝜌 𝑑Γ
𝜕𝜌2 𝜕𝜌 𝜕𝜌 𝜕𝜌
Ω Ω
Thus converting equations (5.17) we obtain a large expression for the weak form.
1 // Parameters
2 real radius = 36; //approximate radius of the cavity
3 real yb = -10, yt = -yb; //window yb=bottom and yt=top coordinates
4 real xl = radius-5, xr = radius+3; //window xl=left and xr=right coordinates
5 real angle = asin((yt)/radius); //angle of the sphere segment to model in radians
6 int Nm = 60; //number of mesh vertices per border
(continues on next page)
16 // Mesh
17 border W1l(t=0, 1){x=xl+(radius*cos(angle)-xl)*(1-t); y=yt; label=1;}
18 border W1r(t=0, 1){x=xr-(xr-radius*cos(angle))*(t); y=yt; label=1;}
19 border W2(t=0, 1){x=xr; y=yb+(yt-yb)*t; label=1;}
20 border W3l(t=0, 1){x=xl+(radius*cos(angle)-xl)*(t); y=yb; label=1;}
21 border W3r(t=0, 1){x=xr-(xr-radius*cos(angle))*(1-t); y=yb; label=1;}
22 border W4(t=0, 1){x=xl; y=yt-(yt-yb)*t; label=1;}
23 border S(t=0, 1){x=radius*cos((t-0.5)*2*angle); y=radius*sin((t-0.5)*2*angle); label=2;}
24 mesh Th = buildmesh(W1r(Nm/4) + W1l(Nm/4) + W4(Nm) + W3l(Nm/4) + W3r(Nm/4) + W2(Nm) +␣
˓→S(Nm));
25 plot(Th, WindowIndex=0);
26
27 // Fespace
28 fespace Ph(Th, P0);
29 Ph reg = region;
30
43
44 // Macro
45 //boundary condition macros
46 macro EWall(Hr, Hphi, Hz) (
47 dy(Hr) - dx(Hz) + Hr*N.x + Hz*N.y
48 - epara*(Hz*M - dy(Hphi)*x)*N.y
49 + eorto*(Hphi - Hr*M+dx(Hphi)*x)*N.x) //
50 macro MWall(Hr, Hphi, Hz) (
51 Hphi + Hz*N.x - Hr*N.y
(continues on next page)
55 // Problem
56 real sigma =(M/(ne*radius))^2+2; // value of the shift (k^2), where the modes will be␣
˓→found
69 )/(eorto*epara)
70 + alpha*(
71 (vHr*Hr - M*(vHphi*Hr + Hphi*vHr) + M^2*vHphi*Hphi)/x //D/r
72 + (dx(vHr) + dy(vHz))*(Hr - M*Hphi) + (vHr - M*vHphi)*(dx(Hr) + dy(Hz)) //E
73 + x*(dx(vHr) + dy(vHz))*(dx(Hr) + dy(Hz)) //F
74 )
75 -sigma*x*(vHr*Hr + vHphi*Hphi + vHz*Hz)
76 )
77 //electric wall boundary condition on the boundary of computation domain
78 +int1d(Th, 1)(
79 EWall(Hr, Hphi, Hz)*EWall(vHr, vHphi, vHz)
80 )
81 ;
82 //setting sparce matrices and assigning the solver UMFPACK to solve eigenvalue problem
83 matrix B = b(Vh, Vh, solver=UMFPACK);
84 matrix OP = op(Vh, Vh, solver=UMFPACK);
85
86 // Solve
87 real[int] ev(nev); //to store the nev eigenvalue
88 Vh[int] [eHr, eHphi, eHz](nev); //to store the nev eigenvector
89 //calling ARPACK on sparce matrices with the assigned solver UMFPACK:
90 int k = EigenValue(OP, B, sym=true, sigma=sigma, value=ev, vector=eHr, tol=1e-10,␣
˓→maxit=0, ncv=0);
91
105 f << "Mode "<< i << ", ka=" << sqrt(ev[i])*radius << endl;
106 }
SIX
EXAMPLES
6.1 Misc
6.1.1 Poisson’s Equation
1 // Parameters
2 int nn = 20;
3 real L = 1.;
4 real H = 1.;
5 real l = 0.5;
6 real h = 0.5;
7
8 func f = 1.;
9 func g = 0.;
10
13 // Mesh
14 border b1(t=0, L){x=t; y=0;};
15 border b2(t=0, h){x=L; y=t;};
16 border b3(t=L, l){x=t; y=h;};
17 border b4(t=h, H){x=l; y=t;};
18 border b5(t=l, 0){x=t; y=H;};
19 border b6(t=H, 0){x=0; y=t;};
20
22
23 // Fespace
24 fespace Vh(Th, P1); // Change P1 to P2 to test P2 finite element
25 Vh u, v;
26
27 // Macro
28 macro grad(u) [dx(u), dy(u)] //
29
30 // Problem
31 problem Poisson (u, v, solver=CG, eps=-1.e-6)
32 = int2d(Th)(
33 grad(u)' * grad(v)
34 )
35 + int2d(Th)(
(continues on next page)
623
FreeFEM Documentation, Release 4.13
48 // Plot
49 plot(Th, u);
50
51 // Adaptmesh
52 Th = adaptmesh(Th, u, inquire=1, err=error);
53 error = error * coef;
54 }
3 // Parameters
4 real hh = 0.1;
5 func ue = 2.*x*x + 3.*y*y + 4.*z*z + 5.*x*y + 6.*x*z + 1.;
6 func f= -18.;
7
8 // Mesh
9 mesh Th = square(10, 20, [x*pi-pi/2, 2*y*pi]); // ]-pi/2, pi/2[X]0,2pi[
(continues on next page)
35 // Fespace
36 fespace Vh(Th3, P23d);
37 Vh u, v;
38 Vh uhe = ue;
39 cout << "uhe min: " << uhe[].min << " - max: " << uhe[].max << endl;
40 cout << uhe(0.,0.,0.) << endl;
41
45 // Macro
46 macro Grad3(u) [dx(u), dy(u), dz(u)] //
47
48 // Problem
49 problem Lap3d (u, v, solver=CG)
50 = int3d(Th3)(
51 Grad3(v)' * Grad3(u)
52 )
53 - int3d(Th3)(
54 f * v
55 )
56 + on(0, 1, u=ue)
57 ;
58
59 // Solve
60 Lap3d;
61 cout << "u min: " << u[]. min << " - max: " << u[].max << endl;
(continues on next page)
63 // Error
64 real err = int3d(Th3)(square(u-ue));
65 cout << int3d(Th3)(1.) << " = " << Th3.measure << endl;
66 Vh d = ue - u;
67 cout << " err = " << err << " - diff l^intfy = " << d[].linfty << endl;
68
69 // Plot
70 u2 = u;
71 u2e = ue;
72 plot(u2, wait=true);
73 plot(u2, u2e,wait=true);
4 // Parameters
5 int nn = 8;
6
7 // Mesh
8 mesh Th0 = square(nn, nn);
9 int[int] rup = [0, 2];
10 int[int] rdown = [0, 1];
11 int[int] rmid = [1, 1, 2, 1, 3, 1, 4, 1];
12 real zmin = 0, zmax = 1;
13 mesh3 Th = buildlayers(Th0, nn, zbound=[zmin, zmax],
14 reffacemid=rmid, reffaceup=rup, reffacelow=rdown);
15
18 // Fespaces
19 fespace Vh2(Th0, P2);
(continues on next page)
26 // Macro
27 macro Grad(u) [dx(u), dy(u), dz(u)] //
28 macro div(u1,u2,u3) (dx(u1) + dy(u2) + dz(u3)) //
29
44 // Plot
45 plot(p, wait=1, nbiso=5); // 3D visualization of pressure isolines
46
6.1.4 Cavity
1 //Parameters
2 int m = 300;
3 real L = 1;
4 real rho = 500.;
5 real mu = 0.1;
6
7 real uin = 1;
8 func fx = 0;
9 func fy = 0;
10 int[int] noslip = [1, 2, 4];
11 int[int] inflow = [3];
12
13 real dt = 0.1;
14 real T = 50;
15
18 //Macros
19 macro div(u) (dx(u#x) + dy(u#y))//
20 macro grad(u) [dx(u), dy(u)]//
21 macro Grad(u) [grad(u#x), grad(u#y)]//
22
23 //Time
24 real cpu;
25 real tabcpu;
26
27 //mesh
28 border C1(t = 0, L){ x = t; y = 0; label = 1; }
29 border C2(t = 0, L){ x = L; y = t; label = 2; }
30 border C3(t = 0, L){ x = L-t; y = L; label = 3; }
(continues on next page)
39 //Solve
40 varf navierstokes([ux, uy, p], [uhx, uhy, ph])
41 = int2d(th)(
42 rho/dt* [ux, uy]'* [uhx, uhy]
43 + mu* (Grad(u):Grad(uh))
44 - p* div(uh)
45 - ph* div(u)
46 - 1e-10 *p*ph
47 )
48
49 + int2d(th) (
50 [fx, fy]' * [uhx, uhy]
51 + rho/dt* [convect([upx, upy], -dt, upx), convect([upx, upy], -dt, upy)]'* [uhx, uhy]
52 )
53
58 //Initialization
59 [ux, uy, p]=[0, 0, 0];
60
64 //Time loop
65 for(int j = 0; j < T/dt; j++){
66 [upx, upy, pp]=[ux, uy, p];
67
74 //CPU
75 cout << " CPU = " << clock()-cpu << endl ;
76 tabcpu = clock()-cpu;
5 real x0 = 1.2;
6 real x1 = 1.8;
7 real y0 = 0;
8 real y1 = 1;
9 int n = 5;
10 real m = 20;
11 mesh Th2 = square(n, m, [x0+(x1-x0)*x, y0+(y1-y0)*y]);
12
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
15 // Adaptmesh
16 for (int i = 0; i < 2; i++){
17 Th = adaptmesh(Th, fh);
18 fh = f; //old mesh is deleted
19 plot(Th, fh, wait=true);
20 }
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
31 // Adaptmesh loop
32 for (int i = 0; i < 4; i++){
33 Poisson;
34 Th = adaptmesh(Th, u, err=error);
(continues on next page)
38 // Plot
39 plot(u);
7 Th = adaptmesh(Th, 1./30., IsMetric=1, nbvx=10000); // More than one time due to the
8 Th = adaptmesh(Th, 1./30., IsMetric=1, nbvx=10000); // adaptation bound `maxsubdiv=`
9 plot(Th, wait=true);
6.2.5 Borders
1 {
2 int upper = 1;
3 int others = 2;
4 int inner = 3;
5
16 int n = 10;
17 plot(C01(-n) + C02(-n) + C03(-n) + C04(-n) + C05(-n)
18 + C06(-n) + C11(n) + C12(n) + C13(n), wait=true);
19
23 plot(Th, wait=true);
24
25 cout << "Part 1 has region number " << Th(0.75, -0.25).region << endl;
26 cout << "Part 2 has redion number " << Th(0.25, -0.25).region << endl;
27 }
28
29 {
30 border a(t=0, 2*pi){x=cos(t); y=sin(t); label=1;}
31 border b(t=0, 2*pi){x=0.3+0.3*cos(t); y=0.3*sin(t); label=2;}
32 plot(a(50) + b(30)); //to see a plot of the border mesh
33 mesh Thwithouthole = buildmesh(a(50) + b(30));
34 mesh Thwithhole = buildmesh(a(50) + b(-30));
(continues on next page)
39 {
40 real r=1;
41 border a(t=0, 2*pi){x=r*cos(t); y=r*sin(t); label=1;}
42 r=0.3;
43 border b(t=0, 2*pi){x=r*cos(t); y=r*sin(t); label=1;}
44 // mesh Thwithhole = buildmesh(a(50) + b(-30)); // do not do this because the two
45 // circles have the same radius = $0.3$
46 }
6.2.6 Change
1 verbosity=3;
2
3 // Mesh
4 mesh Th1 = square(10, 10);
5 mesh Th2 = square(20, 10, [x+1, y]);
(continues on next page)
27 solve P(u, v)
28 = int2d(Th)(
29 Grad(u)'*Grad(v)
30 )
31 -int2d(Th)(
32 v
33 )
34 + on(1, 3, u=0)
35 ;
36
37 plot(u, wait=1);
6.2.7 Cube
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);
6.2.9 3 points
1 // Square for Three-Point Bend Specimens fixed on Fix1, Fix2
2 // It will be loaded on Load
3 real a = 1, b = 5, c = 0.1;
4 int n = 5, m = b*n;
5 border Left(t=0, 2*a){x=-b; y=a-t;}
6 border Bot1(t=0, b/2-c){x=-b+t; y=-a;}
7 border Fix1(t=0, 2*c){x=-b/2-c+t; y=-a;}
8 border Bot2(t=0, b-2*c){x=-b/2+c+t; y=-a;}
9 border Fix2(t=0, 2*c){x=b/2-c+t; y=-a;}
10 border Bot3(t=0, b/2-c){x=b/2+c+t; y=-a;}
(continues on next page)
6.2.10 Bezier
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);
(continues on next page)
5 // Parameters
6 int C1 = 99;
7 int C2 = 98;
8
9 // 2D mesh
10 border C01(t=0, pi){x=t; y=0; label=1;}
11 border C02(t=0, 2*pi){ x=pi; y=t; label=1;}
12 border C03(t=0, pi){ x=pi-t; y=2*pi; label=1;}
13 border C04(t=0, 2*pi){ x=0; y=2*pi-t; label=1;}
14
37 func XX = x*cos(y);
38 func YY = x*sin(y);
39 func ZZ = z;
40
6.2.12 Sphere
1 // Parameter
2 real hh = 0.1;
3
4 // Mesh 2D
5 mesh Th = square(10, 20, [x*pi-pi/2, 2*y*pi]); // ]-pi/2, pi/2[X]0, 2pi[
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 // Partial 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;
21
22 // Periodic condition
23 func perio = [[4, y], [2, y], [1, x], [3, x]];
24
25 // Mesh adaptation
26 real vv = 1/square(hh);
27 Th = adaptmesh(Th, m11*vv, m21*vv, m22*vv, IsMetric=1, inquire=1, periodic=perio);
28 Th = adaptmesh(Th, m11*vv, m21*vv, m22*vv, IsMetric=1, periodic=perio);
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
32 // Sphere
33 mesh3 Th3 = movemesh23(Th, transfo=[f1, f2, f3]);
34 plot(Th3);
4 // Parameters
5 searchMethod=1; // More safe seach algo
6 real a = 1, d = 0.5, h = 0.5;
7 int nnb = 7, nni = 10;
8 int nz = 3;
9 func zmin = 0;
10 func zmax = h;
11
12 // Mesh 2D
13 border b1(t=0.5, -0.5){x=a*t; y=-a/2; label=1;}
14 border b2(t=0.5, -0.5){x=a/2; y=a*t; label=2;}
15 border b3(t=0.5, -0.5){x=a*t; y=a/2; label=3;}
16 border b4(t=0.5, -0.5){x=-a/2; y=a*t; label=4;}
17 border i1(t=0, 2.*pi){x=d/2*cos(t); y=-d/2*sin(t); label=7;}
18 mesh Th = buildmesh(b1(-nnb) + b3(nnb) + b2(-nnb) + b4(nnb) + i1(nni));
19
36 // Mesh 3D
37 int[int] rup = [0, 5], rlow = [0, 6], rmid = [1, 1, 2, 2, 3, 3, 4, 4, 7, 7], rtet = [0,␣
˓→41];
42
43 plot(Th3, wait=true);
44 medit("Th3", Th3);
45
46 fespace Vh(Th3, P2, periodic=[[1, x, z], [3, x, z], [2, y, z], [4, y, z], [5, x, y], [6,␣
˓→x, y]]);
4 // Mesh
5 mesh Th = square(10, 10);
6
7 // Fespace
8 fespace Vh(Th, P1);
9 int n = Vh.ndof;
10 int n1 = n+1;
11 Vh uh, vh;
12
13 // Problem
(continues on next page)
28 // Block matrix
29 matrix AA = [ [ A, B ], [ B', 0 ] ];
30 set(AA, solver=sparsesolver);
31
37 // Solve
38 xx = AA^-1 * bb;
39
40 // Set values
41 [uh[],l] = xx;
42
43 // Display
44 cout << " l = " << l(0) << " , b(u, 1) =" << B'*uh[] << endl;
45
46 // Plot
47 plot(uh);
6.4 Visualization
6.4.1 Plot
1 mesh Th = square(5,5);
2 fespace Vh(Th, P1);
3
8 // Zoom on box defined by the two corner points [0.1,0.2] and [0.5,0.6]
9 plot(uh, [uh, vh], bb=[[0.1, 0.2], [0.5, 0.6]],
10 wait=true, grey=true, fill=true, value=true);
11
12 // Compute a cut
13 int n = 10;
14 real[int] xx(10), yy(10);
15 for (int i = 0; i < n; i++){
16 x = i/real(n);
17 y = i/real(n);
18 xx[i] = i;
19 yy[i] = uh; // Value of uh at point (i/10., i/10.)
20 }
21 plot([xx, yy], wait=true);
22
29 // Calls the gnuplot command, waits 5 seconds and generates a postscript plot (UNIX ONLY)
30 exec("echo 'plot \"plot.gp\" w l \n pause 5 \n set term postscript \n set output \
˓→"gnuplot.eps\" \n replot \n quit' | gnuplot");
(c) Gnuplot
6.4.2 HSV
1 // From: 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
6.4.3 Medit
1 load "medit"
2
10 // Old way
11 savemesh(Th, "u", [x, y, u*.5]); // Saves u.points and u.faces file
12 // build a u.bb file for medit
13 {
14 ofstream file("u.bb");
15 file << "2 1 1 " << u[].n << " 2 \n";
16 for (int j = 0; j < u[].n; j++)
17 file << u[][j] << endl;
18 }
19 // Calls medit command
20 exec("ffmedit u");
21 // Cleans files on unix-like OS
22 exec("rm u.bb u.faces u.points");
6.4.4 Paraview
1 load "iovtk"
2
7 // Algorithms tests
8 {
9 func bool stop (int iter, real[int] u, real[int] g){
10 cout << " stop = " << iter << " " << u.linfty << " " << g.linfty << endl;
11 return g.linfty < 1e-5 || iter > 15;
12 }
13 // minimization of J(u) = 1./2 * sum (i+1) u_i^2 - b_i
14 real[int] b(10), u(10);
15
16 //J
17 func real J (real[int] & u){
18 real s = 0;
19 for (int i = 0; i < u.n; i++)
20 s += (i+1)*u[i]*u[i]*0.5 - b[i]*u[i];
21 if (debugJ)
22 cout << "J = " << s << ", u = " << u[0] << " " << u[1] << endl;
23 return s;
24 }
25
47 //erro calculation
48 func real error (real[int] & u, real[int] & b){
49 real s = 0;
50 for (int i = 0; i < u.n; i++)
51 s += abs((i+1)*u[i] - b[i]);
52 return s;
53 }
54
66 b = 1;
67 u = 0;
68 LinearCG(DJ, u, eps=1.e-15, nbiter=20, precon=matId, verbosity=verb, stop=stop);
69 cout << "LinearGC (Affine with stop) : J(u) = " << J(u) << ", err = " << error(u, b)
˓→<< endl;
73 b = 1;
74 u = 0;
75 LinearCG(DJ0, u, b, eps=1.e-6, nbiter=20, precon=matId, verbosity=verb);
76 cout << "LinearGC (Linear) : J(u) = " << J(u) << ", err = " << error(u, b) << endl;
77 nerr += !(error(u,b) < 1e-5);
78 if(nerr) cout << "sol: u = " << u[0] << " " << u[1] << " " << u[2] << endl;
79
80
81 b = 1;
82 u = 0;
83 AffineGMRES(DJ, u, eps=1.e-6, nbiter=20, precon=matId, verbosity=verb);
84 cout << "AffineGMRES (Affine) : J(u) = " << J(u) << ", err = " << error(u, b) <<␣
˓→endl;
88 b=1;
89 u=0;
90 LinearGMRES(DJ0, u, b, eps=1.e-6, nbiter=20, precon=matId, verbosity=verb);
91 cout << "LinearGMRES (Linear) : J(u) = " << J(u) << ", err = " << error(u, b) <<␣
˓→endl;
95
96 b=1;
97 u=0;
98 NLCG(DJ, u, eps=1.e-6, nbiter=20, precon=matId, verbosity=verb);
99 cout << "NLCG: J(u) = " << J(u) << ", err = " << error(u, b) << endl;
100 nerr += !(error(u,b) < 1e-5);
101 if(nerr) cout << "sol: u =" << u[0] << " " << u[1] << " " << u[2] << endl;
102
103
112 assert(nerr==0);
113 }
114
124 // Mesh
125 mesh Th = square(20, 20);
126
127 // Fespace
128 fespace Vh(Th, P1);
129 Vh b = 1;
130 Vh u = 0;
131
162 // Problem
163 alpha = df(dx(u)*dx(u) + dy(u)*dy(u));
164 varf alap (uh, vh)
165 = int2d(Th)(
166 alpha*(dx(uh)*dx(vh) + dy(uh)*dy(vh))
167 )
168 + on(1, 2, 3, 4, uh=0)
169 ;
170
181 // Preconditionner
182 func real[int] C(real[int] & u){
183 real[int] w = u;
184 u = Alap^-1*w;
185 return u; //warning: no return of local array variable
186 }
187
188 // Solve
189 int conv=0;
190 for(int i = 0; i < 20; i++){
191 conv = NLCG(dJ, u[], nbiter=10, precon=C, veps=eps, verbosity=5);
192 if (conv) break;
193
199 // Plot
200 plot (u, wait=true, cmm="solution with NLCG");
201 umax = u[].max;
202
207 assert(nerr==0);
3 // Parameters
4 int NN = 7;
5 func f1 = 1.;
6 func f2 = -1.;
7 func g1 = 0.;
8 func g2 = 0.1;
9 int iter = 0;
10 int nadapt = 1;
11 real starttol = 1e-10;
12 real bctol = 6.e-12;
13 real pena = 1000.;
14
15 // Mesh
16 mesh Th = square(NN, NN);
17
18 // Fespace
19 fespace Vh(Th, P1);
20 Vh ou1, ou2;
21
40 Vh In, Bord;
41 Bord[] = Vbord(0, Vh, tgv=1);
42 In[] = Bord[] ? 0:1;
43 Vh gh1 = Bord*g1;
44 Vh gh2 = Bord*g2;
45
98 return val ;
99 }
100
101 // Solve
102 real[int] start(2*In[].sum);
103
5 // Parameters
6 int nadapt = 3;
7 real alpha = 0.9;
8 int np = 30;
9 real regtest;
10 int shapeswitch = 1;
11 real sigma = 2*pi/40.;
12 real treshold = 0.1;
13 real e = 0.1;
(continues on next page)
19 // Mesh
20 mesh Th = square(2*np, np, [2*pi*x, pi*y]);
21
22 // Fespace
23 fespace Vh(Th, P1, periodic=[[2, y], [4, y]]);
24 //Initial shape definition
25 //outside of the mesh adaptation loop to initialize with the previous optimial shape␣
˓→found on further iterations
26 Vh startshape = 5;
27 Vh uz = 1., lz = 1.;
28
46 if(ffplot)
47 plot(Sphere);
48 else
49 medit(cmm, Sphere);
50 }
51 catch(...){
52 cout << "PLOT ERROR" << endl;
53 }
54 return 1;
55 }
56
57 // Surface computation
58 //Maybe is it possible to use movemesh23 to have the surface function less␣
˓→complicated
66 ++iter;
67 if(1)
68 plot(rho, value=true, fill=true, cmm="rho(theta,phi) on [0,2pi]x[0,pi] - S=
˓→"+res, dim=3);
69 else
70 Plot3D(rho[], "shape_evolution", 1);
71 return res;
72 }
73
94 matrix hessianA;
95 func matrix HessianArea (real[int] &X){
96 Vh rho, rho2;
97 rho[] = X;
98 rho2 = square(rho);
99 Vh sqrtPsi, sqrtPsi3, C00, C01, C02, C11, C12, C22, A;
100 {
101 Vh C0, C1, C2;
102 Vh dxrho2 = dx(rho)*dx(rho), dyrho2 = dy(rho)*dy(rho);
103 sqrtPsi = sqrt( rho2*rho2*sin2 + rho2*dxrho2 + rho2*dyrho2*sin2);
104 sqrtPsi3 = (rho2*rho2*sin2 + rho2*dxrho2 + rho2*dyrho2*sin2)*sqrtPsi;
105 C0 = 2*rho2*rho*sin2 + rho*dxrho2 + rho*dyrho2*sin2;
106 C1 = rho2*dx(rho);
107 C2 = rho2*sin2*dy(rho);
108 C00 = square(C0);
109 C01 = C0*C1;
110 C02 = C0*C2;
111 C11 = square(C1);
112 C12 = C1*C2;
113 C22 = square(C2);
114 A = 6.*rho2*sin2 + dxrho2 + dyrho2*sin2;
115 }
116 varf d2Area (w, v)
117 =int2d(Th)(
118 1./sqrtPsi * (
119 A*w*v
120 + 2*rho*dx(rho)*dx(w)*v
121 + 2*rho*dx(rho)*w*dx(v)
122 + 2*rho*dy(rho)*sin2*dy(w)*v
123 + 2*rho*dy(rho)*sin2*w*dy(v)
124 + rho2*dx(w)*dx(v)
125 + rho2*sin2*dy(w)*dy(v)
126 )
127 + 1./sqrtPsi3 * (
128 C00*w*v
129 + C01*dx(w)*v
130 + C01*w*dx(v)
131 + C02*dy(w)*v
132 + C02*w*dy(v)
133 + C11*dx(w)*dx(v)
134 + C12*dx(w)*dy(v)
135 + C12*dy(w)*dx(v)
136 + C22*dy(w)*dy(v)
137 )
138 )
139 ;
140 hessianA = d2Area(Vh, Vh);
141 return hessianA;
(continues on next page)
190 func real Gaussian (real X, real Y, real theta, real phi){
191 real deltax2 = square((X-theta)*sin(Y)), deltay2 = square(Y-phi);
192 return exp(-0.5 * (deltax2 + deltay2) / (sigma*sigma));
(continues on next page)
198 if(1){
199 lb = r0;
200 for (int q = 0; q < 5; ++q){
201 func f = rr*Gaussian(x, y, 2*q*pi/5., pi/3.);
202 func g = rr*Gaussian(x, y, 2*q*pi/5.+pi/5., 2.*pi/3.);
203 lb = max(max(lb, f), g);
204 }
205 lb = max(lb, rr*Gaussian(x, y, 2*pi, pi/3));
206 }
207 lb = max(lb, max(disc1, disc2));
208 real Vobj = Volume(lb[]);
209 real Vnvc = 4./3.*pi*pow(lb[].linfty,3);
210
211 if(1)
212 Plot3D(lb[], "object_inside", 1);
213 real[int] clb = 0., cub = [(1-alpha)*Vobj + alpha*Vnvc];
214
221 // Plot
222 Plot3D(rc[], "Shape_at_"+kkk, 1);
223 Plot3D(GradArea(rc[]), "ShapeGradient", 1);
224
1 load "mpi-cmaes"
2
3 // Parameters
4 int NN = 10;
5 func f1 = 1.;
6 func f2 = -1.;
7 func g1 = 0.;
8 func g2 = 0.1;
9 int iter = 0;
10 int nadapt = 1;
11 real starttol = 1e-10;
12 real bctol = 6.e-12;
13 real pena = 1000;
14
15 // Mesh
16 mesh Th = square(NN, NN);
17
18 // Fespace
19 fespace Vh(Th, P1);
20 Vh ou1, ou2;
21
39 Vh In, Bord;
40 Bord[] = Vbord(0, Vh, tgv=1);
41 In[] = Bord[] ? 0:1;
42 Vh gh1 = Bord*g1, gh2 = Bord*g2;
43
95 return val ;
96 }
97
98 // Solve
99 real[int] start(2*In[].sum);
100
101 if (al==0){
102 start(0:In[].sum-1) = 0.;
103 start(In[].sum:2*In[].sum-1) = 0.1;
104 }
105 else
106 FEFToSSP(ou1[], ou2[], start);
107
6.6 Parallelization
6.6.1 MPI-GMRES 2D
To launch this script, use for example:
1 //usage :
2 //ff-mpirun [mpi parameter] MPIGMRES2d.edp [-glut ffglut] [-n N] [-k K] [-d D] [-ns] [-
˓→gmres [0|1]
3 //arguments:
4 //-glut ffglut : to see graphicaly the process
(continues on next page)
15 load "MPICG"
16 load "medit"
17 load "metis"
18 include "getARGV.idp"
19 include "MPIplot.idp"
20 include "MPIGMRESmacro.idp"
21
22 searchMethod = 0; //more safe seach algo (warning can be very expensive in case of lot␣
˓→of ouside point)
28 // Arguments
29 verbosity = getARGV("-vv", 0);
30 int vdebug = getARGV("-d", 1);
31 int ksplit = getARGV("-k", 3);
32 int nloc = getARGV("-n", 10);
33 string sff = getARGV("-p", "");
(continues on next page)
47
48 // Parameters
49 int withplot = 0;
50 bool withmetis = 1;
51 bool RAS = 1;
52 string sPk = "P2-2gd";
53 func Pk = P2;
54 int sizeoverlaps = 1; //size of overlap
55 int[int] l111 = [1, 1, 1, 1]; //mesh labels
56
57 // MPI function
58 func bool plotMPIall(mesh &Th, real[int] &u, string cm){
59 if(vdebug)
60 PLOTMPIALL(mesh, Pk, Th, u, {cmm=cm, nbiso=20, fill=1, dim=3, value=1});
61 return 1;
62 }
63
64 // MPI
65 mpiComm comm(mpiCommWorld,0,0); //trick : make a no split mpiWorld
66
70 int njpart = 0; //Number of part with intersection (a jpart) with ipart without ipart
71 int[int] jpart(npart); //list of jpart
72 if(ipart==0)
73 cout << " Final N = " << ksplit*nloc << ", nloc = " << nloc << ", split = " <<␣
˓→ksplit << endl;
74 settt
75
76 // Mesh
77 mesh Thg = square(nloc, nloc, label=l111);
78 mesh ThC = square(nC, nC, label=l111);// Coarse mesh
79
82 // Fespace
83 fespace Phg(Thg, P0);
(continues on next page)
91 // Partitioning
92 {
93 int[int] nupart(Thg.nt);
94 nupart = 0;
95 if (npart > 1 && ipart == 0)
96 metisdual(nupart, Thg, npart);
97
109 Thin = trunc(Thg, suppi>0, label=10); // non-overlapping mesh, interfaces have label 10
110 int nnn = sizeoverlaps*2;// to be sure
111 AddLayers(Thg, suppi[], nnn, unssd[]); //see above! suppi and unssd are modified
112 unssd[] *= nnn; //to put value nnn a 0
113 real nnn0 = nnn - sizeoverlaps + 0.001;
114 Thi = trunc(Thg, unssd>nnn0, label=10); //overlapping mesh, interfaces have label 10
115
116 settt
117
118 // Fespace
119 fespace Vhi(Thi,P1);
120 int npij = npart;
121 Vhi[int] pij(npij); //local partition of unit + pii
122 Vhi pii;
123
139
140 {
141 //find all j mes (supp(p_j) cap supp(p_i)) >0
142 //compute all phi_j on Thii
143 //remark: supp p_i include in Thi
144
145 // Fespace
146 fespace Phii(Thii, P0);
147 fespace Vhii(Thii, P1);
148 Vhi sumphi = 0;
149 Vhii phii = 0;
150
151 jpart = 0;
152 njpart = 0;
153 int nlayer = RAS ? 1 : sizeoverlaps;
154 if (ipart == 0)
155 cout << "nlayer = " << nlayer << endl;
156 pii = max(unssd-nnn+nlayer, 0.)/nlayer;
157 if(dplot)
158 plot(pii, wait=1, cmm=" 0000");
159 sumphi[] += pii[];
160 if(dplot)
161 plot(sumphi, wait=1, cmm=" summ 0000");
162
228 if(vdebug)
229 plotMPIall(Thi, pii[], "pi_i");
230
248 settt
249
250 if (ipart == 0)
251 cout << "End build mesh intersection" << endl;
252
265 {Whi uuu=1; Whij vvv=-1; vvv[]+=rMj[jp]'*uuu[]; cout << jp << " ### " <<␣
˓→vvv[].linfty << endl; assert(vvv[].linfty < 1e-6);}
266 }
267 }
268 }
269 if (ipart == 0)
270 cout << "End build transfert matrix" << endl;
271
304 if (mpiRank(comm) == 0)
305 AC = vPbC(VhC, VhC, solver=sparsesolver);
306
367 Whi u = 0, v;
368 { //verification
369 Whi u = 1, v;
370 Update(u[], v[]);
371 u[] -= v[];
372 assert(u[].linfty < 1e-6);
373 }
374
375 settt
376 u[] = vBC(0, Whi, tgv=1); //set u with tgv BC value
377
409 if (ipart == 0)
410 cout << ipart << " err = " << errg << " u. max " << umaxg << endl;
411 if (errg < 1e-5) break;
412 }
413
414 if (vdebug)
415 plotMPIall(Thi, u[], "u-final");
416
417 settt
418
427 }
428
437 /*
(continues on next page)
451 ff << mpirank << " " << mpisize << " " << sPk << " ";
452 ff << ksplit << " " << nloc << " " << sizeoverlaps << " " << kiter;
453 for (int i = 1; i < ittt; ++i)
454 ff << " " << ttt[i]-ttt[i-1] << " ";
455 ff << epss << " " << Ai.nbcoef << " " << Ai.n << " " << gmres << endl;
456 }
6.6.2 MPI-GMRES 3D
Todo
todo
3 // Parameters
4 int[int] ICNTL(40); //declaration of ICNTL parameter for MUMPS
5
18 broadcast(processor(0), ICNTL);
19
23 {
24 // Problem
25 int SYM = 0;
26 int PAR = 1;
27 matrix A =
28 [
29 [40, 0, 45, 0, 0],
30 [0, 12, 0, 0, 0],
31 [0, 0, 40, 0, 0],
32 [12, 0, 0, 22, 0],
33 [0, 0, 20, 0, 22]
34 ];
35
50 // Solve
51 if (mpirank == 0)
52 cout << "Solve" << endl;
53 x = A^-1*b;
54 if (mpirank == 0)
55 cout << "b = " << b << endl;
56 if (mpirank == 0)
57 cout << "x = " << endl; cout << x << endl;
58 di = xx-x;
59 if (mpirank == 0){
60 cout << "x-xx = " << endl;
61 cout << "Linf = " << di.linfty << ", L2 = " << di.l2 << endl;
62 }
63 }
64
86 // Solve
87 if (mpirank == 0)
88 cout << "Solve" << endl;
89 x = A^-1*b;
90
91 if (mpirank == 0){
92 cout << "b = " << b << endl;
93 cout << "x = " << x << endl;
94 }
95 di = xx-x;
96 if (mpirank == 0){
97 cout << "x-xx = " << endl;
98 cout << "Linf = " << di.linfty << ", L2 = " << di.l2 << endl;
99 }
100 }
Todo
Todo
6.7 Developers
6.7.1 FFT
1 load "dfft"
2
3 // Parameters
4 int nx = 32;
5 real ny = 16;
6 real N = nx*ny;
7 func f1 = cos(2*x*2*pi)*cos(3*y*2*pi);
8
9 // Mesh
10 //warning: the fourier space is not exactly the unit square due to periodic condition
11 mesh Th = square(nx-1, ny-1, [(nx-1)*x/nx, (ny-1)*y/ny]);
12 //warning: the numbering of the vertices (x,y) is
13 //given by i = x/nx + nx*y/ny
14
15 // Fespace
16 fespace Vh(Th,P1);
17 Vh<complex> u = f1, v;
18 Vh w = f1;
19 Vh ur, ui;
20
21 // FFT
22 //in dfft the matrix n, m is in row-major order and array n, m is
23 //store j + m*i (the transpose of the square numbering)
24 v[] = dfft(u[], ny, -1);
25 u[] = dfft(v[], ny, +1);
26 cout << "||u||_\infty " << u[].linfty << endl;
27
28 u[] *= 1./N;
29 cout << "||u||_\infty " << u[].linfty << endl;
30
31 ur = real(u);
32
33 // Plot
34 plot(w, wait=1, value=1, cmm="w");
35 plot(ur, wait=1, value=1, cmm="u");
36 v = w - u;
37 cout << "diff = " << v[].max << " " << v[].min << endl;
38 assert( norm(v[].max) < 1e-10 && norm(v[].min) < 1e-10);
39
40 // Other example
41 //FFT Lapacian
42 //-\Delta u = f with biperiodic condition
43 func f = cos(3*2*pi*x)*cos(2*2*pi*y);
44 func ue = (1./(square(2*pi)*13.))*cos(3*2*pi*x)*cos(2*2*pi*y); //the exact solution
45 Vh<complex> ff = f;
46 Vh<complex> fhat;
47 Vh<complex> wij;
48
49 // FFT
(continues on next page)
52 //warning in fact we take mode between -nx/2, nx/2 and -ny/2, ny/2
53 //thanks to the operator ?:
54 wij = square(2.*pi)*(square(( x<0.5?x*nx:(x-1)*nx)) + square((y<0.5?y*ny:(y-1)*ny)));
55 wij[][0] = 1e-5; //to remove div / 0
56 fhat[] = fhat[] ./ wij[];
57 u[] = dfft(fhat[], ny, 1);
58 u[] /= complex(N);
59 ur = real(u); //the solution
60 w = real(ue); //the exact solution
61
62 // Plot
63 plot(w, ur, value=1, cmm="ue", wait=1);
64
65 // Error
66 w[] -= ur[];
67 real err = abs(w[].max) + abs(w[].min);
68 cout << "err = " << err << endl;
69 assert(err < 1e-6);
70
6.7.2 Complex
1 real a = 2.45, b = 5.33;
2 complex z1 = a + b*1i, z2 = a + sqrt(2.)*1i;
3
14 cout << "Standard output of the complex " << pc(z1) << " is the pair: " << z1 << endl;
15 cout << pc(z1) << " + " << pc(z2) << " = " << pc(z1+z2) << endl;
16 cout << pc(z1) << " - " << pc(z2) << " = " << pc(z1-z2) << endl;
17 cout << pc(z1) << " * " << pc(z2) << " = " << pc(z1*z2) << endl;
18 cout << pc(z1) << " / " << pc(z2) << " = " << pc(z1/z2) << endl;
19 cout << "Real part of " << pc(z1) << " = " << real(z1) << endl;
20 cout << "Imaginary part of " << pc(z1) << " = " << imag(z1) << endl;
(continues on next page)
25 cout << "Conjugate of " <<pc(z2) << " = " << pc(conj(z2)) <<endl;
26 cout << pc(z1) << " ^ " << pc(z2) << " = " << pc(z1^z2) << endl;
6.7.3 String
1 // Concatenation
2 string tt = "toto1" + 1 + " -- 77";
3
4 // Append
5 string t1 = "0123456789";
6 t1(4:3) = "abcdefghijk-";
7
8 // Sub string
9 string t55 = t1(4:14);
10
1 tt = toto11 -- 77
2 t1 = 0123abcdefghijk-456789
3 t1.find(abc) = 4
(continues on next page)
6.7.5 Array
1 real[int] tab(10), tab1(10); //2 array of 10 real
2 //real[int] tab2; //bug: array with no size
3
12 tab.resize(12); //change the size of array tab to 12 with preserving first value
13 tab(10:11) = 3.14; //set values 10 & 11
14 cout << "resized tab: " << tab << endl;
15
42
43 {
44 int[int] A1(2:10); //2, 3, 4, 5, 6, 7, 8, 9, 10
45 int[int] A2(2:3:10); //2, 5, 8
46 cout << "A1(2:10): " << A1 << endl;
47 cout << "A2(2:3:10): " << A1 << endl;
48 A1 = 1:2:5;
49 cout << "1:2:5 => " << A1 << endl;
50 }
51 {
52 real[int] A1(2:10); //2, 3, 4, 5, 6, 7, 8, 9, 10
53 real[int] A2(2:3:10); //2, 5, 8
54 cout << "A1(2:10): " << A1 << endl;
55 cout << "A2(2:3:10): " << A1 << endl;
56 A1 = 1.:0.5:3.999;
57 cout << "1.:0.5:3.999 => " << A1 << endl;
58 }
59 {
60 complex[int] A1(2.+0i:10.+0i); //2, 3, 4, 5, 6, 7, 8, 9, 10
61 complex[int] A2(2.:3.:10.); //2, 5, 8
62 cout << " A1(2.+0i:10.+0i): " << A1 << endl;
(continues on next page)
103 c = a .* b;
104 cout << "c = a .* b: " << c << endl;
105 c = a ./ b;
106 cout << "c = a ./ b: " << c << endl;
107 c = 2 * b;
108 cout << "c = 2 * b: " << c << endl;
109 c = b * 2;
110 cout << "c = b * 2: " << c << endl;
111
138 }
139
140 {
141 // Array versus matrix
142 int N = 3, M = 4;
143
190 i = A.imax;
191 j = A.jmax;
192
193 cout << "Max " << i << " " << j << ", = " << A.max << endl;
194
198 ii = A.imin;
199 jj = A.jmin;
200
201 cout << "Min " << ii << " " << jj << ", = " << A.min << endl;
202 }
1 tab: 10
2 1.03 2.15 1.03 1.03 1.03
3 1.03 1.03 1.03 1.03 1.03
4
5 min: 1.03
6 max: 2.15
7 sum: 11.42
8 resized tab: 12
9 1.03 2.15 1.03 1.03 1.03
10 1.03 1.03 1.03 1.03 1.03
11 3.14 3.14
12 sorted tab:12
13 1.03 1.03 1.03 1.03 1.03
(continues on next page)
21 d: 5
22 -5 -4 -3 -2 -1
23
24 ii: 5
25 4 3 2 1 0
26
27 A1(2:10): 9
28 2 3 4 5 6
29 7 8 9 10
30 A2(2:3:10): 9
31 2 3 4 5 6
32 7 8 9 10
33 1:2:5 => 3
34 1 3 5
35 A1(2:10): 9
36 2 3 4 5 6
37 7 8 9 10
38 A2(2:3:10): 9
39 2 3 4 5 6
40 7 8 9 10
41 1.:0.5:3.999 => 6
42 1 1.5 2 2.5 3
43 3.5
44 A1(2.+0i:10.+0i): 9
45 (2,0) (3,0) (4,0) (5,0) (6,0)
46 (7,0) (8,0) (9,0) (10,0)
47 A2(2.:3.:10.)= 3
48 (2,0) (5,0) (8,0)
49 A1.re real part array: 9
50 2 3 4 5 6
51 7 8 9 10
52 A1.im imag part array: 9
53 0 0 0 0 0
54 0 0 0 0
55 a: 5
56 2 1 2 4 4
57
58 b = a + a: 5
59 4 2 4 8 8
60
61 b += a: 5
62 6 3 6 12 12
63
64 b += 2*a: 5
65 10 5 10 20 20
(continues on next page)
67 b /= 2: 5
68 5 2.5 5 10 10
69
70 b .*= a: 5
71 10 2.5 10 40 40
72
73 b ./= a: 5
74 5 2.5 5 10 10
75
76 c = a + b: 5
77 7 3.5 7 14 14
78
79 c = 2*a + 4b: 5
80 24 12 24 48 48
81
82 c = a + 4b: 5
83 22 11 22 44 44
84
85 c = -a + 4b: 5
86 18 9 18 36 36
87
88 c = -a - 4b: 5
89 -22 -11 -22 -44 -44
90
91 c = -a -b: 5
92 -7 -3.5 -7 -14 -14
93
94 c = a .* b: 5
95 10 2.5 10 40 40
96
97 c = a ./ b: 5
98 0.4 0.4 0.4 0.4 0.4
99
100 c = 2 * b: 5
101 10 5 10 20 20
102
103 c = b * 2: 5
104 10 5 10 20 20
105
106 ||a||_1 = 13
107 ||a||_2 = 6.40312
108 ||a||_infty = 4
109 sum a_i = 13
110 max a_i = 4 a[ 3 ] = 4
111 min a_i = 1 a[ 1 ] = 1
112 a' * a = 41
113 a quantile 0.2 = 2
114 b = a(I) : 5
115 2 4 4 -3 4
116
117 c(I) = a 5
(continues on next page)
120 b = a(I) : 5
121 2 4 4 -3 4
122
123 c(I) = a 5
124 -3 -3 4 9 4
125
126 A = 3 4
127 1 5 2 1
128 3 3 3 1
129 4 5 2 4
130
131 C = 3 4
132 (-50,-40) (-100,-80) (-150,-120) (-200,-160)
133 (-100,-80) (-200,-160) (-300,-240) (-400,-320)
134 (-150,-120) (-300,-240) (-450,-360) (-600,-480)
135
136 A = 3 4
137 8 10 12 14
138 16 20 24 28
139 24 30 36 42
140
5 // Mesh
6 mesh Th1 = square(10, 10);
7 mesh Th2 = square(10, 10, [1+x, -1+y]);
8 plot(Th1, Th2);
9
17 // Macro
18 macro grad(u) [dx(u), dy(u)] //
19
20 // Problem
21 varf vPoisson1 (u, v)
22 = int2d(Th1)(
23 grad(u)' * grad(v)
24 )
25 - int2d(Th1)(
26 f1 * v
27 )
28 + on(1, 2, 3, 4, u=0)
29 ;
30
46 //block matrix
47 matrix<real> G = [[Poisson1, 0], [0, Poisson2]];
48 set(G, solver=sparsesolver);
49
53 // Solve
54 real[int] sol = G^-1 * Gb;
55
56 // Dispatch
57 [u1[], u2[]] = sol;
58
59 // Plot
60 plot(u1, u2);
4 // Fespace
5 fespace Vh(Th, P1);
6 Vh f, g;
7 f = x*y;
8 g = sin(pi*x);
9
14 // Problem
15 varf mat (u, v)
16 = int2d(Th)(
17 1*dx(u)*dx(v)
18 + 2*dx(u)*dy(v)
19 + 3*dy(u)*dx(v)
20 + 4*dy(u)*dy(v)
21 )
22 + on(1, 2, 3, 4, u=1)
23 ;
24
38 // Operations
39 Vh m0; m0[] = A*f[];
40 Vh m01; m01[] = A'*f[];
41 Vh m1; m1[] = f[].*g[];
42 Vh m2; m2[] = f[]./g[];
43
44 // Display
45 cout << "f = " << f[] << endl;
46 cout << "g = " << g[] << endl;
47 cout << "A = " << A << endl;
48 cout << "m0 = " << m0[] << endl;
49 cout << "m01 = " << m01[] << endl;
50 cout << "m1 = "<< m1[] << endl;
51 cout << "m2 = "<< m2[] << endl;
52 cout << "dot Product = "<< f[]'*g[] << endl;
53 cout << "hermitien Product = "<< ff[]'*gg[] << endl;
54 cout << "outer Product = "<< (A=f[]*g[]') << endl;
55 cout << "hermitien outer Product = "<< (AA=ff[]*gg[]') << endl;
56
57 // Diagonal
58 real[int] diagofA(A.n);
59 diagofA = A.diag; //get the diagonal of the matrix
60 A.diag = diagofA ; //set the diagonal of the matrix
61
66 [I, J, C] = A; //get the sparse term of the matrix A (the array are resized)
67 cout << "I = " << I << endl;
68 cout << "J = " << J << endl;
69 cout << "C = " << C << endl;
70
1 f = 6
2 0 0 0 0 0.5
3 1
4 g = 6
(continues on next page)
36 m0 = 6
37 -1.25 -2.25 0.5 0 5e+29
38 1e+30
39 m01 = 6
40 -1.25 -2.25 0 0.25 5e+29
41 1e+30
42 m1 = 6
43 0 0 0 0 0.5
44 1.224646799e-16
45 m2 = 6
46 -nan 0 0 -nan 0.5
47 8.165619677e+15
48 dot Product = 0.5
49 hermitien Product = (1.11022e-16,2.5)
50 outer Product = # Sparse Matrix (Morse)
51 # first line: n m (is symmetic) nbcoef
52 # after for each nonzero coefficient: i j a_ij where (i,j) \in {1,...,n}x{1,...,m}
53 6 6 0 8
54 5 2 0.5
55 5 3 6.1232339957367660359e-17
56 5 5 0.5
(continues on next page)
92 I = 8
93 4 4 4 4 5
94 5 5 5
95 J = 8
96 1 2 4 5 1
97 2 4 5
98 C = 8
99 0.5 6.123233996e-17 0.5 6.123233996e-17 1
100 1.224646799e-16 1 1.224646799e-16
101 -- Raw Matrix nxm =6x6 nb none zero coef. 8
102 -- Raw Matrix nxm =6x6 nb none zero coef. 6
103 D = # Sparse Matrix (Morse)
104 # first line: n m (is symmetic) nbcoef
105 # after for each nonzero coefficient: i j a_ij where (i,j) \in {1,...,n}x{1,...,m}
106 6 6 1 6
107 1 1 0
108 2 2 0
(continues on next page)
Warning
Due to Fortran indices starting at one, the output of a diagonal matrix D is indexed from 1. but in FreeFEM, the
indices start from 0.
4 // Matrix
5 int n = 5;
6 real[int, int] A(n, n), A1(n, n), B(n,n);
7 for (int i = 0; i < n; ++i)
8 for (int j = 0; j < n; ++j)
9 A(i, j) = (i == j) ? n+1 : 1;
10 cout << A << endl;
11
12 // Inversion (lapack)
13 A1 = A^-1; //def in "lapack"
14 cout << A1 << endl;
15
16 B = 0;
17 for (int i = 0; i < n; ++i)
18 for (int j = 0; j < n; ++j)
19 for (int k = 0; k < n; ++k)
20 B(i, j) += A(i,k)*A1(k,j);
21 cout << B << endl;
22
23 // Inversion (fflapack)
24 inv(A1); //def in "fflapack"
25 cout << A1 << endl;
8 5 5
9 0.18 -0.02 -0.02 -0.02 -0.02
10 -0.02 0.18 -0.02 -0.02 -0.02
11 -0.02 -0.02 0.18 -0.02 -0.02
(continues on next page)
15 5 5
16 1 1.040834086e-17 1.040834086e-17 1.734723476e-17 2.775557562e-17
17 3.469446952e-18 1 -1.734723476e-17 1.734723476e-17 2.775557562e-17
18 2.428612866e-17 -3.122502257e-17 1 1.734723476e-17 2.775557562e-17
19 2.081668171e-17 -6.938893904e-17 -3.469446952e-17 1 0
20 2.775557562e-17 -4.163336342e-17 -2.775557562e-17 0 1
21
22 5 5
23 6 1 1 1 1
24 1 6 1 1 1
25 1 1 6 1 1
26 1 1 1 6 1
27 1 1 1 1 6
Tip
To compile lapack.cpp and fflapack.cpp, you must have the lapack library on your system and compile the
plugin with the command:
1 ff-c++ lapack.cpp -llapack ff-c++ fflapack.cpp -llapack
6.7.9 FE array
1 // Mesh
2 mesh Th = square(20, 20, [2*x, 2*y]);
3
4 // Fespace
5 fespace Vh(Th, P1);
6 Vh u, v, f;
7
8 // Problem
9 problem Poisson (u, v)
10 = int2d(Th)(
11 dx(u)*dx(v)
12 + dy(u)*dy(v)
13 )
14 + int2d(Th)(
15 - f*v
16 )
17 + on(1, 2, 3, 4, u=0)
18 ;
19
34 // Plot
35 for (int i = 0; i < 3; i++)
36 plot(uu[i], wait=true);
6.7.10 Loop
1 for (int i = 0; i < 10; i=i+1)
2 cout << i << endl;
3
16 matrix A = a;
17 string[string] ss; //a map
18 ss["1"] = 1;
19 ss["2"] = 2;
20 ss["3"] = 5;
21 for [i, bi : ss]
22 bi = i + 6 + "-dddd";
23 cout << "ss = " << ss << endl;
24
25 int[string] si;
26 si[1] = 2;
27 si[50] = 1;
28 for [i, vi : si]{
29 cout << " i " << setw(3) << i << " " << setw(10) << vi << endl;
30 vi = atoi(i)*2;
31 }
32 cout << "si = " << si << endl;
33
1 0 1
2 1 2
3 2 3
4 3 4
5 4 5
6 5 6
7 6 7
8 7 8
9 8 9
10 9 10
11 b = 10
12 1 2 3 4 5
13 6 7 8 9 10
14
15 a = 10 10
16 0.5 0.3333333333 0.25 0.2 0 0 0 0 0 0
17 0.3333333333 0.25 0.2 0 0 0 0 0 0 0
18 0.25 0.2 0 0 0 0 0 0 0 0
19 0.2 0 0 0 0 0 0 0 0 0
20 0 0 0 0 0 0 0 0 0 0
21 0 0 0 0 0 0 0 0 0 0
22 0 0 0 0 0 0 0 0 0 0
23 0 0 0 0 0 0 0 0 0 0
24 0 0 0 0 0 0 0 0 0 0
25 0 0 0 0 0 0 0 0 0 0
26
27 ss = 1 1
28 2 2
29 3 5
30
31 i 1 2
32 i 50 1
33 si = 1 2
34 50 100
35
36 0 0 0.5
37 0 1 0.333333
38 0 2 0.25
39 0 3 0.2
40 1 0 0.333333
41 1 1 0.25
42 1 2 0.2
43 2 0 0.25
44 2 1 0.2
45 3 0 0.2
46 # Sparse Matrix (Morse)
47 # first line: n m (is symmetic) nbcoef
(continues on next page)
6.7.12 I/O
1 int i;
2 cout << "std-out" << endl;
3 cout << " enter i = ?";
4 cin >> i;
5
6 {
7 ofstream f("toto.txt");
8 f << i << "hello world'\n";
9 } //close the file f because the variable f is delete
10
11 {
12 ifstream f("toto.txt");
13 f >> i;
14 }
15
16 {
17 ofstream f("toto.txt", append);
18 //to append to the existing file "toto.txt"
19 f << i << "hello world'\n";
20 } //close the file f because the variable f is delete
21
5 {
6 ofstream file("f.txt", binary);
7 file.precision(16);
8 file << f << endl;
9 where = file.tellp();
10 file << 0.1 ;
11
37 {
38 real xx;
39 ifstream file("f.txt", binary);
40 cout << "Where " << file.seekg << endl;
41 file.seekg(where);
42 file >> xx;
43 cout << " xx = " << xx << " good ? " << file.good() << endl;
44 assert(xx == 0.1);
45 skipcomment(file) >> xx;
46 assert(xx == 0.2);
47 file.seekg(0); //rewind
48 cout << "Where " << file.tellg() << " " << file.good() << endl;
49 file >> g;
50 }
1 include "getARGV.idp"
2
6.7.15 Macro
1 // Macro without parameters
2 macro xxx() {
3 real i = 0;
4 int j = 0;
5 cout << i << " " << j << endl;
6 }//
7
8 xxx
9
13 toto({real i = 0; int j = 0; cout << i << " " << j << endl;})
14
30 // Macro concatenation
31 mesh Th = square(2, 2);
32 fespace Vh(Th, P1);
33 Vh Ux=x, Uy=y;
34
44 //NewMacro - EndMacro
45 NewMacro grad(u) [dx(u), dy(u)] EndMacro
46 cout << int2d(Th)(grad(Ux)' * grad(Uy)) << endl;
47
48 // IFMACRO - ENDIFMACRO
49 macro AA CAS1 //
50
51 IFMACRO(AA,CAS1 )
52 cout << "AA = " << Stringification(AA) << endl;
53 macro CASE file1.edp//
54 ENDIFMACRO
55 IFMACRO(AA, CAS2)
56 macro CASE file2.edp//
57 ENDIFMACRO
58
61 IFMACRO(CASE)
62 include Stringification(CASE)
63 ENDIFMACRO
64
65 // FILE - LINE
66 cout << "In " << FILE << ", line " << LINE << endl;
1 AA = CAS1
2 CASE = file1.edp
3 This is the file 1
4 In Macro.edp, line 59
1 1/0 : d d d
2 current line = 3
3 Exec error : Div by 0
4 -- number :1
5 Catch an ExecError
6 // Mesh
7 mesh Th = square(nn, nn);
8
9 // Fespace
(continues on next page)
13 // Problem
14 real cpu = clock();
15 problem laplace (uh, vh, solver=Cholesky, tolpivot=1e-6)
16 = int2d(Th)(
17 dx(uh)*dx(vh)
18 + dy(uh)*dy(vh)
19 )
20 + int2d(Th)(
21 - f*vh
22 )
23 ;
24
25 try{
26 cout << "Try Cholesky" << endl;
27
28 // Solve
29 laplace;
30
31 // Plot
32 plot(uh);
33
34 // Display
35 cout << "laplacian Cholesky " << nn << ", x_" << nn << " : " << -cpu+clock() << " s,␣
˓→max = " << uh[].max << endl;
36 }
37 catch(...) { //catch all error
38 cout << " Catch cholesky PB " << endl;
39 }
1 Try Cholesky
2 ERREUR choleskypivot (35)= -6.43929e-15 < 1e-06
3 current line = 29
4 Exec error : FATAL ERREUR dans ./../femlib/MatriceCreuse_tpl.hpp
5 cholesky line:
6 -- number :688
7 catch an erreur in solve => set sol = 0 !!!!!!!
8 Catch cholesky PB
707
FreeFEM Documentation, Release 4.13
[HORGAN2004] HORGAN, Cornelius O. and SACCOMANDI, Giuseppe. Constitutive models for compressible non-
linearly elastic materials with limiting chain extensibility. Journal of Elasticity, 2004, vol. 77, no 2,
p. 123-138.
[LEHOUCQ1998] LEHOUCQ, Richard B., SORENSEN, Danny C., and YANG, Chao. ARPACK users’ guide: solu-
tion of large-scale eigenvalue problems with implicitly restarted Arnoldi methods. Siam, 1998.
[NECAS2017] NECAS, Jindrich and HLAVÁCEK, Ivan. Mathematical theory of elastic and elasto-plastic bodies: an
introduction. Elsevier, 2017.
[OHTSUKA2000] OHTSUKA, K. Theoretical and Numerical analysis of energy release rate in 2D fracture. INFOR-
MATION, 2000, vol. 3, p. 303-315.
[TABATA1994] TABATA, M. Numerical solutions of partial differential equations II. Iwanami Applied Math, 1994.
[LUCQUIN1998] PIRONNEAU, O. and LUCQUIN-DESREUX, B. Introduction to scientific computing. Wiley, 1998.
[WILMOTT1995] WILMOTT, Paul, HOWISON, Sam and DEWYNNE, Jeff. A student introduction to mathematical
finance. 1995.
[ACHDOU2005] ACHDOU, Yves and PIRONNEAU, Olivier. Computational methods for option pricing. Siam, 2005.
[TEMAM1977] TEMAM, Roger. Navier-Stokes equations: theory and numerical analysis. 1977.
[ROBERTS1993] ROBERTS, J. E. and THOMAS, J. M. Mixed and Hybrid Methods, Handbook of Numerical Ana-
ysis, Vol. II. North-Holland, 1993, vol. 183, p. 184.
[GLOWINSKI1979] GLOWINSKI, R. and PIRONNEAU, O. On numerical methods for the Stokes problem. In:
Energy methods in finite element analysis.(A79-53076 24-39) Chichester, Sussex, England, Wiley-
Interscience, 1979, p. 243-264., 1979, p. 243-264.
[GLOWINSKI1985] GLOWINSKI, Roland and ODEN, J. Tinsley. Numerical methods for nonlinear variational prob-
lems. Journal of Applied Mechanics, 1985, vol. 52, p. 739.
[GLOWINSKI2003] GLOWINSKI, Roland. Finite element methods for incompressible viscous flow. Handbook of
numerical analysis, 2003, vol. 9, p. 3-1176.
[ITO2003] ITO, Kazufumi and KUNISCH, Karl. Semi–smooth Newton methods for variational inequalities of the
first kind. ESAIM: Mathematical Modelling and Numerical Analysis, 2003, vol. 37, no 1, p. 41-62.
[HINTERMULLER2002] HINTERMÜLLER, Michael, ITO, Kazufumi, et KUNISCH, Karl. The primal-dual active
set strategy as a semismooth Newton method. SIAM Journal on Optimization, 2002, vol. 13, no 3, p.
865-888.
[OXBORROW2007] OXBORROW, Mark. Traceable 2-D finite-element simulation of the whispering-gallery modes
of axisymmetric electromagnetic resonators. IEEE Transactions on Microwave Theory and Techniques,
2007, vol. 55, no 6, p. 1209-1218.
[GRUDININ2012] GRUDININ, Ivan S. and YU, Nan. Finite-element modeling of coupled optical microdisk res-
onators for displacement sensing. JOSA B, 2012, vol. 29, no 11, p. 3010-3014.
[ERN2006] ERN, A. and GUERMOND, J. L. Discontinuous Galerkin methods for Friedrichs’ symmetric systems.
I. General theory. SIAM J. Numer. Anal.
[BERNADOU1980] BERNADOU, Michel, BOISSERIE, Jean-Marie and HASSAN, Kamal. Sur l’implémentation
des éléments finis de Hsieh-Clough-Tocher complet et réduit. 1980. Thèse de doctorat. INRIA.
[BERNARDI1985] BERNARDI, Christine and RAUGEL, Genevieve. Analysis of some finite elements for the Stokes
problem. Mathematics of Computation, 1985, p. 71-79.
[THOMASSET2012] THOMASSET, François. Implementation of finite element methods for Navier-Stokes equa-
tions. Springer Science & Business Media, 2012.
708 Bibliography
FreeFEM Documentation, Release 4.13
[CROUZEIX1984] CROUZEIX, Michel and MIGNOT, Alain L. Analyse numérique des équations différentielles.
Masson, 1984.
[TAYLOR2005] TAYLOR, Mark A., WINGATE, Beth A. and BOS, Len P. Several new quadrature formulas for
polynomial integration in the triangle. arXiv preprint math/0501496, 2005.
[CHOW1997] CHOW, Edmond and SAAD, Yousef. Parallel Approximate Inverse Preconditioners. In : PPSC. 1997.
Bibliography 709
INDEX
mesh, 112
meshL, 112
710