Developers
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
1MeshVersionFormatted 0
2
3Dimension [DIM](int)
4
5Vertices
6[Number of vertices](int)
7X_1(double) Y_1(double) (Z_1(double)) Ref_1(int)
8...
9X_nv(double) Y_nv(double) (Z_nv(double)) Ref_nv(int)
10
11Edges
12[Number of edges](int)
13Vertex1_1(int) Vertex2_1(int) Ref_1(int)
14...
15Vertex1_ne(int) Vertex2_ne(int) Ref_ne(int)
16
17Triangles
18[Number of triangles](int)
19Vertex1_1(int) Vertex2_1(int) Vertex3_1(int) Ref_1(int)
20...
21Vertex1_nt(int) Vertex2_nt(int) Vertex3_nt(int) Ref_nt(int)
22
23Quadrilaterals
24[Number of Quadrilaterals](int)
25Vertex1_1(int) Vertex2_1(int) Vertex3_1(int) Vertex4_1(int) Ref_1(int)
26...
27Vertex1_nq(int) Vertex2_nq(int) Vertex3_nq(int) Vertex4_nq(int) Ref_nq(int)
28
29Geometry
30[File name of geometric support](char*)
31
32 VertexOnGeometricVertex
33 [Number of vertex on geometric vertex](int)
34 Vertex_1(int) VertexGeometry_1(int)
35 ...
36 Vertex_nvg(int) VertexGeometry_nvg(int)
37
38 EdgeOnGeometricEdge
39 [Number of geometric edge](int)
40 Edge_1(int) EdgeGeometry_1(int)
41 ...
42 Edge_neg(int) EdgeGeometry_neg(int)
43
44CrackedEdges
45[Number of cracked edges](int)
46Edge1_1(int) Edge2_1(int)
47...
48Edge1_nce(int) Edge2_nce(int)
When the current mesh refers to a previous mesh, we have in addition
1MeshSupportOfVertices
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)
nv
means the number of verticesne
means the number of edgesnt
means the number of trianglesnq
means the number of quadrilateralsnvg
means the number of vertex on geometric vertexneg
means the number of edges on geometric edgence
means the number of cracked edges
bb file type to Store Solutions
The file is formatted such that:
12 [Number of solutions](int) [Number of vertices](int) 2
2
3U_1_1(double) ... U_ns_1(double)
4...
5U_1_nv(double) ... U_ns_nv(double)
ns
means the number of solutionsnv
means the number of verticesU_i_j
is the solution componenti
at the vertexj
on the associated mesh.
BB file type to store solutions
The file is formatted such that:
12 [Number of solutions](int) [Type 1](int) ... [Type ns](int) [Number of vertices](int) 2
2
3U_1_1_1(double) ... U_(type_k)_1_1(double)
4...
5U_1_1_1(double) ... U_(type_k)_nbv_1(double)
6
7...
8
9U_1_1_ns(double) ... U_(type_k)_1_ns(double)
10...
11U_1_nbv_ns(double) ... U_(type_k)_nbv_ns(double)
ns
means the number of solutionstype_k
mean the type of solutionk
:1: the solution is scalar (1 value per vertex)
2: the solution is vectorial (2 values per vertex)
3: the solution is a \(2\times 2\) symmetric matrix (3 values per vertex)
4: the solution is a \(2\times 2\) matrix (4 values per vertex)
nbv
means the number of verticesU_i_j_k
is the value of the componenti
of the solutionk
at vertexj
on the associated mesh
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
2h_0(double)
3...
4h_nv(double)
nv
is the number of verticesh_i
is the wanted mesh size near the vertexi
on associated mesh.
The metric is \(\mathcal{M}_i = h_i^{-2}I\) where \(I\) is the identity matrix.
The anisotropic file is such that
1[Number of vertices](int) 3
2a11_0(double) a21_0(double) a22_0(double)
3...
4a11_nv(double) a21_nv(double) a22_nv(double)
nv
is the number of verticesa11_i
,a21_i
anda22_i
represent metric \(\mathcal{M}_i = \left(\begin{array}{cc}a_{11,i} & a_{12,i}\\a{12}_i & a_{22,i}\end{array}\right)\) which define the wanted size in a vicinity of the vertexi
such that \(h\) in direction \(u \in \mathbb{R}^2\) is equal to \(|u|/\sqrt{u\cdot\mathcal{M}_i\, u}\), where \(\cdot\) is the dot product in \(\mathbb{R}^2\), and \(|\cdot|\) is the classical norm.
List of AM_FMT, AMDBA Meshes
The mesh is only composed of triangles and can be defined with the help of the following two integers and four arrays:
nbt
the number of trianglesnbv
the number of verticesnu(1:3, 1:nbt)
an integer array giving the three vertex numbers counterclockwise for each trianglec(1:2, 1:nbv)
a real array giving tje two coordinates of each vertexrefs(1:nbv)
an integer array giving the reference numbers of the verticesreft(1:nbt)
an integer array giving the reference numbers of the triangles
AM_FMT Files
In Fortran
the am_fmt
files are read as follows:
1open (1, file='xxx.am_fmt', form='formatted', status='old')
2read (1, *) nbv, nbt
3read (1, *) ((nu(i, j), i=1, 3), j=1, nbt)
4read (1, *) ((c(i, j), i=1, 2), j=1, nbv)
5read (1, *) ( reft(i), i=1, nbt)
6read (1, *) ( refs(i), i=1, nbv)
7close(1)
AM Files
In Fortran
the am
files are read as follows:
1open (1, file='xxx.am', form='unformatted', status='old')
2read (1, *) nbv, nbt
3read (1) ((nu(i, j), i=1, 3), j=1, nbt),
4& ((c(i, j), i=1, 2), j=1, nbv),
5& (reft(i), i=1, nbt),
6& (refs(i), i=1, nbv)
7close(1)
AMDBA Files
In Fortran
the amdba
files are read as follows:
1open (1, file='xxx.amdba', form='formatted', status='old')
2read (1, *) nbv, nbt
3read (1, *) (k, (c(i, k), i=1, 2), refs(k), j=1, nbv)
4read (1, *) (k, (nu(i, k), i=1, 3), reft(k), j=1, nbt)
5close(1)
msh Files
First, we add the notions of boundary edges
nbbe
the number of boundary edgenube(1:2, 1:nbbe)
an integer array giving the two vertex numbers of boundary edgesrefbe(1:nbbe)
an integer array giving the reference numbers of boundary edges
In Fortran
the msh
files are read as follows:
1open (1, file='xxx.msh', form='formatted', status='old')
2read (1, *) nbv, nbt, nbbe
3read (1, *) ((c(i, k), i=1, 2), refs(k), j=1, nbv)
4read (1, *) ((nu(i, k), i=1, 3), reft(k), j=1, nbt)
5read (1, *) ((ne(i, k), i=1, 2), refbe(k), j=1, nbbe)
6close(1)
ftq Files
In Fortran
the ftq
files are read as follows:
1open(1,file='xxx.ftq',form='formatted',status='old')
2read (1,*) nbv,nbe,nbt,nbq
3read (1,*) (k(j),(nu(i,j),i=1,k(j)),reft(j),j=1,nbe)
4read (1,*) ((c(i,k),i=1,2),refs(k),j=1,nbv)
5close(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.
sol and solb files
With the keyword savesol
, we can store a scalar functions, a scalar finite element functions, a vector fields, a vector finite element fields, a symmetric tensor and a symmetric finite element tensor.
Such format is used in medit
.
Extension file .sol
The first two lines of the file are :
MeshVersionFormatted 0
Dimension [DIM](int)
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 :
stored in the extension .sol
are respectively \(ST_{xx}^{3d}, ST_{yx}^{3d}, ST_{yy}^{3d}, ST_{zx}^{3d}, ST_{zy}^{3d}, ST_{zz}^{3d}\) and \(ST_{xx}^{2d}, ST_{yx}^{2d}, ST_{yy}^{2d}\)
An example of field with the keyword SolAtTetrahedra
:
1SolAtTetrahedra
2[Number of tetrahedra](int)
3[Number of solutions](int) [Type of solution 1](int) ... [Type of soution nt](int)
4
5U_1_1_1(double) ... U_nrs_1_1(double)
6...
7U_1_ns_1(double) ... U_(nrs_k)_ns_1(double)
8
9...
10
11U_1_1_nt(double) ... U_nrs_1_nt(double)
12...
13U_1_ns_nt(double) ... U_(nrs_k)_ns_nt(double)
ns
is the number of solutionstypesol_k
, type of the solution numberk
typesol_k = 1
the solutionk
is scalartypesol_k = 2
the solutionk
is vectorialtypesol_k = 3
the solutionk
is a symmetric tensor or symmetric matrix
nrs_k
is the number of real to describe solutionk
nrs_k = 1
if the solutionk
is scalarnrs_k = dim
if the solutionk
is vectorial (dim
is the dimension of the solution)nrs_k = dim*(dim+1)/2
if the solution k is a symmetric tensor or symmetric matrix
U_i_j_^k
is a real equal to the value of the componenti
of the solutionk
at tetrahedronj
on the associated mesh
The format .solb
is the same as format .sol
but in binary (read/write is faster, storage is less).
A real scalar functions \(f1\), a vector fields \(\mathbf{\Phi} = [\Phi1, \Phi2, \Phi3]\) and a symmetric tensor \(ST^{3d}\) (52) at the vertices of the three dimensional mesh Th3
is stored in the file f1PhiTh3.sol
using :
1savesol("f1PhiST3dTh3.sol", Th3, f1, [Phi(1), Phi(2), Phi(3)], VV3, order=1);
where \(VV3 = [ST_{xx}^{3d}, ST_{yx}^{3d}, ST_{yy}^{3d}, ST_{zx}^{3d}, ST_{zy}^{3d}, ST_{zz}^{3d}]\).
For a two dimensional mesh Th
, A real scalar functions \(f2\), a vector fields \(\mathbf{\Psi} = [\Psi1, \Psi2]\) and a symmetric tensor \(ST^{2d}\) (52) at triangles is stored in the file f2PsiST2dTh3.solb
using :
1savesol("f2PsiST2dTh3.solb", Th, f2, [Psi(1), Psi(2)], VV2, order=0);
where \(VV2 = [ST_{xx}^{2d}, ST_{yx}^{2d}, ST_{yy}^{2d}]\)
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.
Adding a new finite element
Some notations
For a function \(\boldsymbol{f}\) taking value in \(\mathbb{R}^{N},\, N=1,2,\cdots\), we define the finite element approximation \(\Pi_h\boldsymbol{f}\) of \(\boldsymbol{f}\).
Let us denote the number of the degrees of freedom of the finite element by \(NbDoF\). Then the \(i\)-th base \(\boldsymbol{\omega}^{K}_{i}\) (\(i=0,\cdots,NbDoF-1\)) of the finite element space has the \(j\)-th component \(\mathbf{\omega}^{K}_{ij}\) for \(j=0,\cdots,N-1\).
The operator \(\Pi_{h}\) is called the interpolator of the finite element.
We have the identity \(\boldsymbol{\omega}^{K}_{i} = \Pi_{h} \boldsymbol{\omega}^{K}_{i}\).
Formally, the interpolator \(\Pi_{h}\) is constructed by the following formula:
where \(P_{p}\) is a set of \(npPi\) points,
In the formula (53), the list \(p_{k},\, j_{k},\, i_{k}\) depend just on the type of finite element (not on the element), but the coefficient \(\alpha_{k}\) can be depending on the element.
Tip
Classical scalar Lagrange finite element
With the classical scalar Lagrange finite element, we have \(\mathtt{kPi}=\mathtt{npPi}=\mathtt{NbOfNode}\) and
\(P_{p}\) is the point of the nodal points.
the \(\alpha_k=1\), because we take the value of the function at the point \(P_{k}\).
\(p_{k}=k\) , \(j_{k}=k\) because we have one node per function.
\(j_{k}=0\) because \(N=1\).
Tip
The Raviart-Thomas finite element
The degrees of freedom are the flux through an edge \(e\) of the mesh, where the flux of the function \(\mathbf{f} : \mathbb{R}^2 \longrightarrow \mathbb{R}^2\) is \(\int_{e} \mathbf{f}.n_{e}\), \(n_{e}\) is the unit normal of edge \(e\) (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 \(T\) with three vertices \((\mathbf{a},\mathbf{b},\mathbf{c})\).
Let denote the vertices numbers by \(i_{a},i_{b},i_{c}\), and define the three edge vectors \(\mathbf{e}^{0},\mathbf{e}^{1},\mathbf{e}^{2}\) by \(sgn(i_{b}-i_{c})(\mathbf{b}-\mathbf{c})\), \(sgn(i_{c}-i_{a})(\mathbf{c}-\mathbf{a})\), \(sgn(i_{a}-i_{b})(\mathbf{a}-\mathbf{b})\).
The three basis functions are:
where \(|T|\) is the area of the triangle \(T\).
So we have \(N=2\), \(\mathtt{kPi}=6; \mathtt{npPi}=3;\) and:
\(P_{p} = \left\{\frac{\mathbf{b}+\mathbf{c}}{2}, \frac{\mathbf{a}+\mathbf{c}}{2}, \frac{\mathbf{b}+\mathbf{a}}{2} \right\}\)
- \(\alpha_{0}= - \mathbf{e}^{0}_{2}, \alpha_{1}= \mathbf{e}^{0}_{1}\),
\(\alpha_{2}= - \mathbf{e}^{1}_{2}, \alpha_{3}= \mathbf{e}^{1}_{1}\), \(\alpha_{4}= - \mathbf{e}^{2}_{2}, \alpha_{5}= \mathbf{e}^{2}_{1}\) (effectively, the vector \((-\mathbf{e}^{m}_{2}, \mathbf{e}^{m}_{1})\) is orthogonal to the edge \(\mathbf{e}^{m}= (e^m_{1},e^m_{2})\) with a length equal to the side of the edge or equal to \(\int_{e^m} 1\)).
\(i_{k}=\{0,0,1,1,2,2\}\),
\(p_{k}=\{0,0,1,1,2,2\}\) , \(j_{k}=\{0,1,0,1,0,1,0,1\}\).
Which class to add?
Add file FE_ADD.cpp
in directory FreeFem-sources/src/femlib
for
example first to initialize :
1#include "error.hpp"
2#include "rgraph.hpp"
3using namespace std;
4#include "RNM.hpp"
5#include "fem.hpp"
6#include "FESpace.hpp"
7#include "AddNewFE.h"
8
9namespace Fem2D { ... }
Then add a class which derive for public TypeOfFE
like:
1class TypeOfFE_RTortho : public TypeOfFE { public:
2 static int Data[]; //some numbers
3 TypeOfFE_RTortho():
4 TypeOfFE(
5 0+3+0, //nb degree of freedom on element
6 2, //dimension N of vectorial FE (1 if scalar FE)
7 Data, //the array data
8 1, //nb of subdivision for plotting
9 1, //nb of sub finite element (generaly 1)
10 6, //number kPi of coef to build the interpolator
11 3, //number npPi of integration point to build interpolator
12 0 //an array to store the coef \alpha_k to build interpolator
13 //here this array is no constant so we have
14 //to rebuilt for each element
15 )
16 {
17 const R2 Pt[] = {R2(0.5, 0.5), R2(0.0, 0.5), R2(0.5, 0.0) };
18 // the set of Point in hat{K}
19 for (int p = 0, kk = 0; p < 3; p++){
20 P_Pi_h[p] = Pt[p];
21 for (int j = 0; j < 2; j++)
22 pij_alpha[kk++] = IPJ(p, p, j);
23 }
24 } //definition of i_k, p_k, j_k in interpolator
25
26 void FB(const bool *watdd, const Mesh &Th, const Triangle &K,
27 const R2 &PHat, RNMK_ &val) const;
28
29 void Pi_h_alpha(const baseFElement &K, KN_<double> &v) const;
30} ;
where the array data is formed with the concatenation of five array of
size NbDoF
and one array of size N
.
This array is:
1int 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
\(\boldsymbol{\omega}^{K}_{i}\), 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 \(i,j,k\) of the array \(val(i,j,k)\) correspond to:
\(i\) is the basic function number on finite element \(i \in [0,NoF[\)
\(j\) is the value of component \(j \in [0,N[\)
\(k\) is the type of computed value \(f(P),dx(f)(P), dy(f)(P), ...\ i \in [0,\mathtt{last\_operatortype}[\).
Note
For optimization, this value is computed only if
whatd[k]
is true, and the numbering is defined with1enum operatortype { 2op_id = 0, 3op_dx = 1, op_dy = 2, 4op_dxx = 3,op_dyy = 4, 5op_dyx = 5,op_dxy = 5, 6op_dz = 6, 7op_dzz = 7, 8op_dzx = 8, op_dxz = 8, 9op_dzy = 9, op_dyz = 9 10}; 11const int last_operatortype = 10;
The shape function:
1void 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
16 if (whatd[op_id]){ //value of the function
17 assert(val.K() > op_id);
18 RN_ f0(val('.', 0,0)); //value first component
19 RN_ f1(val('.', 1,0)); //value second component
20 f1[0] = (P.x - A.x)*a0;
21 f0[0] = -(P.y - A.y)*a0;
22
23 f1[1] = (P.x - B.x)*a1;
24 f0[1] = -(P.y - B.y)*a1;
25
26 f1[2] = (P.x - C.x)*a2;
27 f0[2] = -(P.y - C.y)*a2;
28 }
29
30 if (whatd[op_dx]){ //value of the dx of function
31 assert(val.K() > op_dx);
32 val(0,1,op_dx) = a0;
33 val(1,1,op_dx) = a1;
34 val(2,1,op_dx) = a2;
35 }
36 if (whatd[op_dy]){
37 assert(val.K() > op_dy);
38 val(0,0,op_dy) = -a0;
39 val(1,0,op_dy) = -a1;
40 val(2,0,op_dy) = -a2;
41 }
42
43 for (int i = op_dy; i < last_operatortype; i++)
44 if (whatd[op_dx])
45 assert(op_dy);
46}
The function to defined the coefficient \(\alpha_{k}\):
1void TypeOfFE_RT::Pi_h_alpha(const baseFElement &K, KN_<double> &v) const
2{
3 const Triangle &T(K.T);
4
5 for (int i = 0, k = 0; i < 3; i++){
6 R2 E(T.Edge(i));
7 R signe = T.EdgeOrientation(i) ;
8 v[k++] = signe*E.y;
9 v[k++] = -signe*E.x;
10 }
11}
Now , we just need to add a new key work in FreeFEM.
Two way, with static or dynamic link so at the end of the file, we add:
With dynamic link it is very simple (see section Dynamical link), just add before the end of FEM2d namespace
:
1 static TypeOfFE_RTortho The_TypeOfFE_RTortho;
2 static AddNewFE("RT0Ortho", The_TypeOfFE_RTortho);
3} //FEM2d namespace
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
1//let the 2 globals variables
2static TypeOfFE_RTortho The_TypeOfFE_RTortho;
3//the name in freefem
4static ListOfTFE typefemRTOrtho("RT0Ortho", &The_TypeOfFE_RTortho);
5
6//link with FreeFEM do not work with static library .a
7//so add a extern name to call in init_static_FE
8//(see end of FESpace.cpp)
9void init_FE_ADD() { };
10//end
11} //FEM2d namespace
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//correct problem of static library link with new make file
2void init_static_FE()
3{ //list of other FE file.o
4 extern void init_FE_P2h() ;
5 init_FE_P2h() ;
6 extern void init_FE_ADD(); //new line 1
7 init_FE_ADD(); //new line 2
8}
and now you have to change the makefile.
First, create a file FE_ADD.cpp
contening all this code, like in file src/femlib/Element_P2h.cpp
, after modify the Makefile.am
by adding the name of your file to the variable EXTRA_DIST
like:
1# Makefile using Automake + Autoconf
2# ----------------------------------
3# Id
4
5# This is not compiled as a separate library because its
6# interconnections with other libraries have not been solved.
7
8EXTRA_DIST=BamgFreeFem.cpp BamgFreeFem.hpp CGNL.hpp CheckPtr.cpp \
9ConjuguedGradrientNL.cpp DOperator.hpp Drawing.cpp Element_P2h.cpp \
10Element_P3.cpp Element_RT.cpp fem3.hpp fem.cpp fem.hpp FESpace.cpp \
11FESpace.hpp FESpace-v0.cpp FQuadTree.cpp FQuadTree.hpp gibbs.cpp \
12glutdraw.cpp gmres.hpp MatriceCreuse.hpp MatriceCreuse_tpl.hpp \
13MeshPoint.hpp mortar.cpp mshptg.cpp QuadratureFormular.cpp \
14QuadratureFormular.hpp RefCounter.hpp RNM.hpp RNM_opc.hpp RNM_op.hpp \
15RNM_tpl.hpp FE_ADD.cpp
and do in the FreeFEM root directory
1autoreconf
2./reconfigure
3make
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.
Dynamical link
Now, it’s possible to add built-in functionnalites in FreeFEM under the three environnents Linux, Windows and MacOS X 10.3 or newer.
It is agood idea to first try the example load.edp
in directory example++-load.
You will need to install a compiler
(generally g++/gcc
compiler) to compile your function.
Windows Install the
cygwin
environnent or themingw
oneMacOs Install the developer tools
Xcode
on the apple DVDLinux/Unix Install the correct compiler (
gcc
for instance)
Now, assume that you are in a shell window (a cygwin
window under Windows) in the directory example++-load.
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,
Compilation of your module:
The script ff-c++
compiles and makes the link with FreeFEM, but be careful, the script has no way to known if you try to compile for a pure Windows environment or for a cygwin environment so to build the load module under cygwin you must add the -cygwin
parameter.
A first example myfunction.cpp
The following defines a new function call myfunction
with no parameter, but using the \(x,y\) current value.
1#include <iostream>
2#include <cfloat>
3using 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
12using namespace Fem2D;
13double myfunction(Stack stack){
14 //to get FreeFEM data
15 MeshPoint &mp = *MeshPointStack(stack); //the struct to get x, y, normal, value
16 double x = mp.P.x; //get the current x value
17 double y = mp.P.y; //get the current y value
18 //cout << "x = " << x << " y=" << y << endl;
19 return sin(x)*cos(y);
20}
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].
1//A class build the link with FreeFEM
2//generaly this class are already in AFunction.hpp
3//but unfortunatly, I have no simple function with no parameter
4//in FreeFEM depending of the mesh
5template<class R>
6class OneOperator0s : public OneOperator {
7 //the class to define and evaluate a new function
8 //It must devive from E_F0 if it is mesh independent
9 //or from E_F0mps if it is mesh dependent
10 class E_F0_F :public E_F0mps {
11 public:
12 typedef R (*func)(Stack stack);
13 func f; //the pointeur to the fnction myfunction
14 E_F0_F(func ff) : f(ff) {}
15 //the operator evaluation in FreeFEM
16 AnyType operator()(Stack stack) const {return SetAny<R>(f(stack));}
17 };
18 typedef R (*func)(Stack);
19 func f;
20 public:
21 //the function which build the FreeFEM byte code
22 E_F0 *code(const basicAC_F0 &) const { return new E_F0_F(f); }
23 //the constructor to say ff is a function without parameter
24 //and returning a R
25 OneOperator0s(func ff) : OneOperator(map_type[typeid(R).name()]),f(ff){}
26};
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);
It will be called automatically at load module time.
To compile and link, use the ff-c++
script :
1ff-c++ myfunction.cpp
2g++ -c -g -Iinclude myfunction.cpp
3g++ -bundle -undefined dynamic_lookup -g myfunction.o -o ./myfunction.dylib
To try the simple example under Linux or MacOS, do FreeFem++-nw load.edp
The output must be:
1-- FreeFem++ v *.****** (date *** ** *** ****, **:**:** (UTC+0*00))
2 Load: lg_fem lg_mesh lg_mesh3 eigenvalue
3 1 : // Example of dynamic function load
4 2 : // --------------------------------
5 3 : // $Id$
6 4 :
7 5 : load "myfunction"
8 6 : // dumptable(cout);
9 7 : mesh Th=square(5,5);
10 8 : fespace Vh(Th,P1);
11 9 : Vh uh= myfunction(); // warning do not forget ()
12 10 : cout << uh[].min << " " << uh[].max << endl;
13 11 : cout << " test io ( " << endl;
14 12 : testio();
15 13 : cout << " ) end test io .. " << endl; sizestack + 1024 =1416 ( 392 )
16
17 -- Square mesh : nb vertices =36 , nb triangles = 50 , nb boundary edges 20
180 0.841471
19 test io (
20 test cout 3.14159
21 test cout 512
22 test cerr 3.14159
23 test cerr 512
24 ) end test io ..
25times: compile 0.012854s, execution 0.000313s, mpirank:0
26 CodeAlloc : nb ptr 2715, size :371104 mpirank: 0
27Ok: Normal End
Under Windows, launch FreeFEM with the mouse (or ctrl O) on the example.
Example: Discrete Fast Fourier Transform
This will add FFT to FreeFEM, taken from FFTW. To download and install under download/include
just go in download/fftw
and try make
.
The 1D dfft (fast discret fourier transform) for a simple array \(f\) of size \(n\) is defined by the following formula:
The 2D DFFT for an array of size \(N=n\times m\) is:
Note
The value \(n\) is given by \(size(f)/m\), and the numbering is row-major order.
So the classical discrete DFFT is \(\hat{f}=\mathtt{dfft}(f,-1)/\sqrt{n}\) and the reverse dFFT \(f=\mathtt{dfft}(\hat{f},1)/\sqrt{n}\)
Note
The 2D Laplace operator is
\[f(x,y) = 1/\sqrt{N} \sum_{j'=0}^{m-1} \sum_{j=0}^{n-1} \hat{f}_{i+nj} e^{\varepsilon 2\pi i (x j+ yj') }\]and we have
\[f_{k+nl} = f(k/n,l/m)\]So
\[\begin{split}\widehat{\Delta f_{kl}} = -( (2\pi)^2 ( (\tilde{k})^2+(\tilde{l})^2)) \widehat{ f_{kl}} \\\end{split}\]where \(\tilde{k} = k\) if \(k \leq n/2\) else \(\tilde{k} = k-n\) and \(\tilde{l} = l\) if \(l \leq m/2\) else \(\tilde{l} = l-m\).
And to have a real function we need all modes to be symmetric around zero, so \(n\) and \(m\) must be odd.
Compile to build a new library
1ff-c++ dfft.cpp ../download/install/lib/libfftw3.a -I../download/install/include
2export MACOSX_DEPLOYMENT_TARGET=10.3
3g++ -c -Iinclude -I../download/install/include dfft.cpp
4g++ -bundle -undefined dynamic_lookup dfft.o -o ./dfft.dylib ../download/install/lib/libfftw3.a
To test, try FFT example.
Load Module for Dervieux P0-P1 Finite Volume Method
The associed edp file is examples++-load/convect_dervieux.edp.
See mat_dervieux.cpp.
More on Adding a new finite element
First read the Adding a new finite element section, we add two new finite elements examples in the directory examples++-load.
The Bernardi-Raugel Element
The Bernardi-Raugel finite element is meant to solve the Navier Stokes equations in \(u,p\) formulation; the velocity space \(P^{br}_K\) is minimal to prove the inf-sup condition with piecewise constant pressure by triangle.
The finite element space \(V_h\) is
where
with notation \(4=1, 5=2\) and where \(\lambda^K_i\) are the barycentric coordinates of the triangle \(K\), \((e_k)_{k=1,2}\) the canonical basis of \(\mathbb{R}^2\) and \(n^K_k\) the outer normal of triangle \(K\) opposite to vertex \(k\).
See BernardiRaugel.cpp.
A way to check the finite element
1load "BernardiRaugel"
2
3// Macro
4//a macro the compute numerical derivative
5macro DD(f, hx, hy) ( (f(x1+hx, y1+hy) - f(x1-hx, y1-hy))/(2*(hx+hy)) ) //
6
7// Mesh
8mesh Th = square(1, 1, [10*(x+y/3), 10*(y-x/3)]);
9
10// Parameters
11real x1 = 0.7, y1 = 0.9, h = 1e-7;
12int it1 = Th(x1, y1).nuTriangle;
13
14// Fespace
15fespace Vh(Th, P2BR);
16Vh [a1, a2], [b1, b2], [c1, c2];
17
18
19for (int i = 0; i < Vh.ndofK; ++i)
20 cout << i << " " << Vh(0,i) << endl;
21
22for (int i = 0; i < Vh.ndofK; ++i)
23{
24 a1[] = 0;
25 int j = Vh(it1, i);
26 a1[][j] = 1;
27 plot([a1, a2], wait=1);
28 [b1, b2] = [a1, a2]; //do the interpolation
29
30 c1[] = a1[] - b1[];
31 cout << " ---------" << i << " " << c1[].max << " " << c1[].min << endl;
32 cout << " a = " << a1[] <<endl;
33 cout << " b = " << b1[] <<endl;
34 assert(c1[].max < 1e-9 && c1[].min > -1e-9); //check if the interpolation is correct
35
36 // check the derivative and numerical derivative
37 cout << " dx(a1)(x1, y1) = " << dx(a1)(x1, y1) << " == " << DD(a1, h, 0) << endl;
38 assert( abs(dx(a1)(x1, y1) - DD(a1, h, 0) ) < 1e-5);
39 assert( abs(dx(a2)(x1, y1) - DD(a2, h, 0) ) < 1e-5);
40 assert( abs(dy(a1)(x1, y1) - DD(a1, 0, h) ) < 1e-5);
41 assert( abs(dy(a2)(x1, y1) - DD(a2, 0, h) ) < 1e-5);
42}
A real example using this finite element, just a small modification of the Navier-Stokes P2-P1 example, just the begenning is change to
1load "BernardiRaugel"
2
3real s0 = clock();
4mesh Th = square(10, 10);
5fespace Vh2(Th, P2BR);
6fespace Vh(Th, P0);
7Vh2 [u1, u2], [up1, up2];
8Vh2 [v1, v2];
And the plot instruction is also changed because the pressure is constant, and we cannot plot isovalues of peacewise constant functions.
The Morley Element
See the example bilapMorley.edp.