Non Homo
Non Homo
u.v dx, u, v H
1
0
()
m(u, v) =
_
uv dx, u, v L
2
()
The bilinear form a(., .) denes a scalar product in H
1
0
() and is related to the energy form. This
form is associated to the operator. The m(., .) is here the classical scalar product on L
2
(),
and is related to the mass form.
9
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
10 Rheolef version 5.94 update 27 May 2011
1.2 Approximation
Let us introduce a mesh T
h
of and the nite dimensional space X
h
of continuous picewise
polynomial functions.
X
h
= v H
1
(); v
/K
P
k
, K T
h
where k = 1 or 2. Let V
h
= X
h
H
1
0
() be the functions of X
h
that vanishes on the boundary of
: The approximate problem expresses:
(V F)
h
: nd u
h
V
h
such that:
a(u
h
, v
h
) = m(1, v
h
) v
h
V
h
By developping u
h
on a basis of V
h
, this problem reduces to a linear system.
The following C++ code implement this problem in the rheolef environment.
1.3 File dirichlet.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
int main(int argc, char**argv) {
geo omega (argv[1]);
space Xh (omega, argv[2]);
Xh.block ("boundary");
form a (Xh, Xh, "grad_grad");
form m (Xh, Xh, "mass");
field fh (Xh, 1);
field uh (Xh);
uh ["boundary"] = 0;
if (omega.dimension() < 3) {
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
} else {
size_t max_iter = 10000;
Float tol = 1e-15;
uh.u = 0;
int status = pcg (a.uu, uh.u, m.uu*fh.u + m.ub*fh.b - a.ub*uh.b,
ic0(a.uu), max_iter, tol, &cerr);
}
cout << uh;
return 0;
}
1.4 Comments
This code applies for both one, two or three dimensional meshes and for both picewise linear or
quadratic nite element approximations. Four major classes are involved, namely: geo, space,
form and field.
Let us now comment the code, line by line.
geo omega (argv[1]);
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 11
This command get the rst unix command-line argument argv[1] as a mesh le name and store
the corresponding mesh in the variable omega.
space Xh (omega, argv[2]);
Build the nite element space Xh contains all the piecewise polynomial continuous functions. The
polynomial type is the second command-line arguments argv[2], and could be either P1 or P2.
Xh.block ("boundary");
The homgeneous Dirichlet conditions are declared on the boundary.
form a (Xh, Xh, "grad_grad");
The form a(., .) is the energy form.
form m (Xh, Xh, "mass");
The form m(., .) is the mass form.
field fh (Xh, 1);
The eld fh is initialized with constant value 1.
field uh (Xh);
The eld uh contains the the degrees of freedom.
uh ["boundary"] = 0;
Some degrees of freedom are prescribed as zero on the boundary.
Let (
i
)
0i<dim(X
h
)
be the basis of X
h
associated to the Lagrange nodes, i.e. the vertices of
the mesh for the P
1
approximation and the vertices and the middle of the edges for the P
2
approximation. The approximate solution u
h
expresses as a linear combination of the continuous
picewise polynomial functions (
i
):
u
h
=
i
u
i
i
Thus, the eld u
h
is completely represented by its coecients (u
i
). The coecients (u
i
) of this
combination are grouped into to sets: some have zero values, from the boundary condition and
are related to bloked coecients, and some others are unknown. Blocked coecients are stored
into the uh.b array while unknown one are stored into uh.u. Thus, the restriction of the bilinear
form a(., .) to X
h
X
h
can be conveniently represented by a block-matrix structure:
a(u
h
, v
h
) =
_
vh.u vh.b
_
_
a.uu a.ub
a.bu a.bb
__
uh.u
uh.b
_
This representation also applies for the restriction to X
h
X
h
of the mass form:
m(f
h
, v
h
) =
_
vh.u vh.b
_
_
m.uu m.ub
m.bu m.bb
__
fh.u
fh.b
_
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
12 Rheolef version 5.94 update 27 May 2011
Thus, the problem (V F)
h
writes now:
_
vh.u vh.b
_
_
a.uu a.ub
a.bu a.bb
__
uh.u
uh.b
_
=
_
vh.u vh.b
_
_
m.uu m.ub
m.bu m.bb
__
fh.u
fh.b
_
for any vh.u and where vh.b = 0.
After expansion, the problem reduces to nd uh.u such that:
a.uu uh.u = m.uu fh.u + m.ub fh.b a.ub uh.b
The LDL
t
factorization of the a.uu matrix is then performed:
ssk<Float> fact = ldlt(a.uu);
Then, the unkown part is solved:
uh.u = fact.solve(m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
When d > 3, an iterative strategy is prefered for solving the linear system:
uh.u = 0;
int status = pcg (a.uu, uh.u, m.uu*fh.u + m.ub*fh.b - a.ub*uh.b,
ic0(a.uu), max_iter, tol, &cerr);
The iterative algorithm is the conjugate gradient pcg, preconditionned by the incomplete Cfholeski
factorisation ic0. Finaly, the eld is printed to standard output:
cout << uh;
1.5 How to compile the code
First, create a Makefile as follow:
include $(shell rheolef-config --libdir)/rheolef/rheolef.mk
CXXFLAGS = $(INCLUDES_RHEOLEF)
LDLIBS = $(LIBS_RHEOLEF)
default: dirichlet
Then, enter:
make dirichlet
Now, your program, linked with rheolef, is ready to run on a mesh.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 13
1.6 How to run the program
Figure 1.1: Solution of the model problem for N = 2: (left) P
1
element; (right) P
2
element.
Enter the comands:
mkgeo_grid -t 10 -boundary > square.geo
geo square.geo
The rst command generates a simple 10x10 bidimensionnal mesh of =]0, 1[
2
and stores it in
the le square.geo. The second command shows the mesh. It uses mayavi visualization program
by default.
The next command performs the computation:
./dirichlet square.geo P1 | field -
The result is piped to the post-treatment, performed by the field command. The field command
reads this result on its standard input and presents it in a graphical form. A temporary le could
also be used to store the result, using the .field le format:
./dirichlet square.geo P1 > square.field
field square.field
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
14 Rheolef version 5.94 update 27 May 2011
Figure 1.2: Alternative representations of the solution of the model problem (N = 2 and the P
1
element): (left) in black-and-white; (right) in elevation and stereoscopic anagyph mode.
Also explore some graphic rendering modes (see Fig. 1.2):
field square.field -black-and-white
field square.field -gray
field square.field -elevation -nofill -stereo
The last command shows the solution in elevation and in stereoscopic anaglyph mode (see Fig. 1.4,
left). The anaglyph mode requires red-cyan glasses: red for the left eye and cyan for the right one,
as shown on Fig. 1.3.
Figure 1.3: Red-cyan anaglyph glasses for the stereoscopic visualization.
See http://en.wikipedia.org/wiki/Anaglyph image for more and
http://www.alpes-stereo.com/lunettes.html for how to nd anaglyphe red-cyan glasses.
Please, consults the corresponding unix manual page for more on field, geo and mkgeo grid:
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 15
man mkgeo_grid
man geo
man field
Turning to the P2 approximation simply writes:
./dirichlet square.geo P2 | field -
Fig. 1.1.right shows the result. Now, let us condider a mono-dimensional problem =]0, 1[:
mkgeo_grid -e 10 -boundary > line.geo
geo line.geo
./dirichlet line.geo P1 | field -
The rst command generates a subdivision containing ten edge elements. The last two lines show
the mesh and the solution via gnuplot visualization, respectively. Conversely, the P2 case writes:
./dirichlet line.geo P2 | field -
Figure 1.4: Solution of the model problem for N = 3 and the P
1
element : (left) mesh; (right)
isovalue, cut planes and stereo anaglyph renderings.
Finally, let us condider a three-dimensional problem =]0, 1[
3
. First, let us generate a mesh:
mkgeo_grid -T 10 -boundary > box.geo
geo box.geo
geo box.geo -stereo -full
geo box.geo -stereo -cut
The previous commands draw the mesh with all internal edges, stereoscopic anaglyph and then
with a cut inside the internal structure: a simple click on the central arrow draws the cut plane
normal vector or its origin, while the red square allows a translation.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
16 Rheolef version 5.94 update 27 May 2011
Then, we perform the computation and the visualization:
./dirichlet box.geo P1 > box.field
field box.field
The visualization presents an isosurface. Also here, you can interact with the cutting plane. Click
on IsoSurface in the left menu and change the value of the isosurface. Finally exit from the
visualization and explore the stereoscopic anaglyph mode (see Fig. 1.4, right):
field box.field -stereo
It is also possible to add a second IsoSurface or ScalarCutPlane module to this scene by using
the Visualize menu. After this exploration of the 3D visualisation capacities of our environment,
let us go back to the Dirichlet problem and perform the P2 approximation:
./dirichlet box.geo P2 | field -
Notices also that the one-dimensional exact solution writes:
u(x) =
x(1 x)
2
while the two-and three dimensional ones support a Fourier expansion (see e.g. [2], annex).
1.7 How to extend the program
A good unix command may check the validity of its arguments, and may have some default values.
These points are left to the reader as an exercice.
1.8 Limitations
When using the P
2
interpolation and some non-polygonal domains (i.e. curved boundaries), the
method works not optimaly: it converges in H
1
norm as h
3/2
instead of the optimal h
2
in the case
of polygonal domains. The optimal convergence requires the use of isoparametric P2 elements (i.e.
curved triangles): this will be a development in the future.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 2
Getting Started
The rst part of this book starts with the Dirichlet problem with homogeneous boundary condition:
this example is declined with details in dimension 1, 2 and 3, as a starting point to Rheolef.
Next chapters present various boundary conditions: for completeness, we treat non-homogeneous
Dirichlet, Neumann, and Robin boundary conditions for model problems. The last example
presents a special problem where the solution is dened up to a constant: the Neumann problem
for the Laplace operator.
This rst part can be viewed as a pedagogic preparation for more advanced applications, such as
Stokes and elasticity, that are treated in the second part of this book.
2.1 Non-homogeneous Dirichlet conditions
Formulation
We turn now to the case of a non-homogeneous Dirichlet boundary conditions. Let f H
1
()
and g H
1
2
(). The problem writes:
(P
2
)
h
nd u, dened in such that:
u = f in
u = g on
The variationnal formulation of this problem expresses:
(V F
2
) nd u V such that:
a(u, v) = m(f, v), v V
0
where
a(u, v) =
_
u.v dx
m(u, v) =
_
uv dx
V = v H
1
(); v
|
= g
V
0
= H
1
0
()
The computation of the right-hand side may be considered with attention since f is not a priori
piecewise polynomial.
17
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
18 Rheolef version 5.94 update 27 May 2011
Approximation
As usual, we introduce a mesh T
h
of and the nite dimensional space X
h
:
X
h
= v H
1
(); v
/K
P
k
, K T
h
Then, we introduce:
V
h
= v X
h
; v
|
=
h
(g)
V
0,h
= v X
h
; v
|
= 0
The approximate problem writes:
(V F
2
)
h
: nd u
h
V
h
such that:
a(u
h
, v
h
) = m(
h
(f), v
h
) v
h
V
0,h
Notices that the evaluation of the right-hand side is performed by remplacing f by its Lagrange in-
terpolation
h
(h) on , while the boundary condition g is remplaced by its Lagrange interpolation
h
(h) on .
Let us choose R
N
, N = 1, 2, 3 and
f(x) = N sin(
N
i=1
x
i
)
g(x) = sin(
N
i=1
x
i
)
This choice is convenient, since the exact solution is known:
u(x) = sin(
N
i=1
x
i
)
. The following C++ code implement this problem in the rheolef environment.
File dirichlet-nh.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
size_t N;
Float f (const point& x) { return 1.0*N*sin(x[0]+x[1]+x[2]); }
Float g (const point& x) { return sin(x[0]+x[1]+x[2]); }
int main(int argc, char**argv) {
geo omega (argv[1]);
N = omega.dimension();
space Vh (omega, argv[2]);
Vh.block ("boundary");
form a(Vh, Vh, "grad_grad");
form m(Vh, Vh, "mass");
field uh (Vh);
field fh = interpolate(Vh, f);
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 19
const domain& boundary = omega["boundary"];
space Wh (omega, boundary, argv[2]);
uh["boundary"] = interpolate(Wh, g);
if (omega.dimension() < 3) {
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
} else {
size_t max_iter = 10000;
Float tol = 1e-15;
uh.u = 0;
int status = pcg (a.uu, uh.u, m.uu*fh.u + m.ub*fh.b - a.ub*uh.b,
ic0(a.uu), max_iter, tol, &cerr);
}
cout << uh;
return 0;
}
Comments
The code looks like the previous one, related to homogeneous boundary conditions. Let us com-
ments the changes. The class point describes the coordinates of a point (x
1
, . . . , x
N
) R
N
as a
N-uplet of Float. The Float type is usually a double. This type depends upon the rheolef con-
guration (see also Installing,,Congure options), and could represent some high precision oating
point class such as doubledouble (quadruple) or bigfloat (arbitrary).
field fh = interpolate(Vh,f);
This implements the Lagrange interpolation operator
h
(f).
space Wh (omega, omega["boundary"], argv[2]);
The space of picewise P
k
functions is builded. This space is suitable for the interpolation of g on
the boundary:
uh[boundary] = interpolate(Wh, g);
The values of the degrees of freedom related to the boundary are stored into the eld u, where
non-homogeneous Dirichlet conditions applies. The rest of the code is similar to the homogeneous
Dirichlet case.
2.1.1 How to run the program
First, compile the program:
make dirichlet-nh
Running the program is obtained from the homogeneous Dirichlet case, by remplacing dirichlet
by dirichlet-nh:
mkgeo_grid -e 10 -boundary > line.geo
./dirichlet-nh line.geo P1 | field -
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
20 Rheolef version 5.94 update 27 May 2011
for the bidimensional case:
mkgeo_grid -t 10 -boundary > square.geo
./dirichlet-nh square.geo P1 | field -
and for the tridimensional case:
mkgeo_grid -T 10 -boundary > box.geo
./dirichlet-nh box.geo P1 | field -mayavi -
Here, the P1 approximation can be remplaced by the P2 one, by modifying the command-line
argument.
2.2 Non-homogeneous Neumann boundary conditions for
I operator
Formulation
Let us show how to insert Neumann boundary conditions. Let f H
1
() and g H
1
2
().
The problem writes:
(P
3
)
h
: nd u, dened in such that:
u u = f in
u
n
= g on
The variationnal formulation of this problem expresses:
(V F
3
) nd u H
1
() such that:
a(u, v) = l(v), v V
0
where
a(u, v) =
_
uv dx +
_
u.v dx
l(v) = m(f, v) +m
b
(g, v)
m(f, v) =
_
fv dx
m
b
(g, v) =
_
gv ds
Approximation
As usual, we introduce a mesh T
h
of and the nite dimensional space X
h
:
X
h
= v H
1
(); v
/K
P
k
, K T
h
h
g on .
Let us choose R
N
, N = 1, 2, 3 and
f(x) = 1 +
1
2N
N
i=1
x
i
(1 x
i
)
g(x) =
1
2N
This example is convenient, since the exact solution is known:
u(x) =
1
2N
N
i=1
x
i
(1 x
i
)
File neumann.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
size_t N;
Float f (const point& x) { return 1+(0.5/N)*(x[0]*(1-x[0])+x[1]*(1-x[1])+x[2]*(1-x[2])); }
Float g (const point& x) { return -0.5/N; }
int main(int argc, char**argv) {
geo omega (argv[1]);
N = omega.dimension();
space Vh (omega, argv[2]);
form m (Vh, Vh, "mass");
form a (Vh, Vh, "grad_grad");
a = a+m;
field fh = interpolate(Vh, f);
space Wh (omega, omega["boundary"], argv[2]);
field gh = interpolate(Wh, g);
form mb (Wh, Vh, "mass");
field uh(Vh);
if (omega.dimension() < 3) {
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve(m.uu*fh.u + m.ub*fh.b + mb.uu*gh.u + mb.ub*gh.b - a.ub*uh.b);
} else {
size_t max_iter = 10000;
Float tol = 1e-15;
uh.u = 0;
int status = pcg (a.uu, uh.u, m.uu*fh.u + m.ub*fh.b + mb.uu*gh.u + mb.ub*gh.b - a.ub*uh.b,
ic0(a.uu), max_iter, tol, &cerr);
}
cout << uh;
return 0;
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
22 Rheolef version 5.94 update 27 May 2011
Comments
The code looks like the previous one. Let us comments the changes.
space Wh (omega, omega["boundary"], argv[2]);
field gh = interpolate(Wh, g);
The space W
h
of piecewise P
k
continuous functions dened on the boundary of is dened here.
Then, the Lagrange interpolation is performed on g.
form mb (Wh, Vh, "mass");
The form mb implements the restriction of the L
2
scalar product on W
h
.
uh.u = fact.solve(m.uu*fh.u + m.ub*fh.b + mb.uu*gh.u + mb.ub*gh.b - a.ub*uh.b);
The direct solver related to uh.u is called on the system:
_
a.uu a.ub
a.bu a.bb
__
uh.u
uh.b
_
=
_
m.uu m.ub
m.bu m.bb
__
fh.u
fh.b
_
+
_
mb.uu mb.ub
mb.bu mb.bb
__
gh.u
gh.b
_
Reduced to the uh.u component, this problem writes also:
a.uu*uh.u = m.uu*fh.u + m.ub*fh.b + mb.uu*gh.u + mb.ub*gh.b - a.ub*uh.b
Notices that uh.b has here a null size.
2.2.1 How to run the program
First, compile the program:
make neumann
Running the program is obtained from the homogeneous Dirichlet case, by remplacing dirichlet
by neumann.
2.3 The Robin boundary conditions
Formulation
Let g H
1
2
(). The problem writes:
(P
4
)
h
nd u, dened in such that:
u = 1 in
u
n
+u = g on
The variationnal formulation of this problem expresses:
(V F
4
) nd u H
1
() such that:
a(u, v) = l(v), v H
1
()
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 23
where
a(u, v) =
_
u.v dx +
_
uv ds
l(v) = m(1, v) +m
b
(g, v)
m(u, v) =
_
uv dx
m
b
(g, v) =
_
gv ds
Approximation
As usual, let
X
h
= v H
1
(); v
/K
P
k
, K T
h
i=1
x
i
(1 x
i
)
_
This choice is convenient, since the exact solution is known:
u(x) =
1
2N
N
i=1
x
i
(1 x
i
)
The following C++ code implement this problem in the rheolef environment.
File robin.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
size_t N;
Float u_ex (const point& x) { return 0.5*(x[0]*(1-x[0]) + x[1]*(1-x[1]))/Float(N); }
Float g (const point& x) { return u_ex(x) - 0.5/N; }
int main(int argc, char**argv) {
geo omega (argv[1]);
N = omega.dimension();
domain boundary = omega["boundary"];
space Xh (omega, argv[2]);
space Wh (omega, boundary, argv[2]);
form a (Xh, Xh, "grad_grad");
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
24 Rheolef version 5.94 update 27 May 2011
form ab (Xh, Xh, "mass", boundary);
a = a + ab;
form m (Xh, Xh, "mass");
form mb (Wh, Xh, "mass") ;
field fh (Xh, 1);
field gh = interpolate(Wh, g);
field uh (Xh);
if (omega.dimension() < 3) {
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve(m.uu*fh.u + m.ub*fh.b + mb.uu*gh.u + mb.ub*gh.b - a.ub*uh.b);
} else {
size_t max_iter = 10000;
Float tol = 1e-15;
uh.u = 0;
int status = pcg (a.uu, uh.u, m.uu*fh.u + m.ub*fh.b + mb.uu*gh.u + mb.ub*gh.b - a.ub*uh.b,
ic0(a.uu), max_iter, tol, &cerr);
}
cout << uh;
return 0;
}
Comments
The code looks like the previous one. Let us comments the changes.
form ab (Xh, Xh, "mass", boundary);
a = a + ab;
The boundary contribution to the a(., .) form on X
h
X
h
is introduced.
Notices that the exact solution is a second-order polynomial. Thus, the P
2
approximation furnishes
the exact solution, up to the machine precision.
2.3.1 How to run the program
First, compile the program:
make robin
Running the program is obtained from the homogeneous Dirichlet case, by remplacing dirichlet
by robin.
2.4 Non-homogeneous Neumann boundary conditions for
the Laplace operator
Formulation
Let f L
2
() and g H
1
2
() satisfying the following compatibility condition:
_
f dx +
_
g ds = 0
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 25
The problem writes:
(P
5
)
h
: nd u, dened in such that:
u = f in
u
n
= g on
Since this problem only involves the derivatives of u, it is clear that its solution is never unique [3,
p. 11]. A discrete version of this problem could be solved iterative by the conjugate gradient or the
MINRES algorithm [4]. In order to solve it by a direct method, we turn the dicuty by seeking
u in the following space
V = v H
1
(); b(v, 1) = 0
where
b(v, ) =
_
v dx, v L
2
(), R
The variationnal formulation of this problem expresses:
(V F
5
): nd u V such that:
a(u, v) = l(v), v V
where
a(u, v) =
_
u.v dx
l(v) = m(f, v) +m
b
(g, v)
m(f, v) =
_
fv dx
m
b
(g, v) =
_
gv ds
Since the direct discretization of the space V is not an obvious task, the constraint b(u, 1) = 0
is enforced by a lagrange multiplier R. Let us introduce the Lagrangian, dened for all
v H
1
() and R by:
L(v, ) =
1
2
a(v, v) +b(v, ) l(v)
The saddle point (u, ) H
1
() R of this lagrangian is characterized as the unique solution of:
a(u, v) +b(v, ) = l(v), v H
1
()
b(u, ) = 0, R
It is clear that if (u, ) is solution of this problem, then u V and u is a solution of (V F
5
).
Conversely, let u V the solution of (V F
5
). Choosing v = v
0
where v
0
(x) = 1, x leads to
meas() = l(v
0
). From the denition of l(.) and the compatibility condition between the data f
and g, we get = 0. Notice that the saddle point problem extends to the case when f and g does
not saties the compatibility condition, and in that case = l(v
0
)/meas().
Approximation
As usual, we introduce a mesh T
h
of and the nite dimensional space X
h
:
X
h
= v H
1
(); v
/K
P
k
, K T
h
i=1
x
i
(1 x
i
)
The code looks like the previous ones. Let us comment the changes. The discrete bilinear form b
is computed as b
h
X
h
that interprets as a linear application from X
h
to R: b
h
(v
h
) = m(v
h
, 1).
Thus b
h
is computed as
field b = m*field(Xh,1.0);
where the discrete bilinear form m is identied to its matrix and field(Xh,1.0) is the constant
vector equal to 1. Let
/ =
_
a.uu trans(b.u)
b.u 0
_
, | =
_
uh.u
lambda
_
, L =
_
lh.u
0
_
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 27
The problem admits the following matrix form:
/ | = L
The matrix / is symetric and non-singular, but indenite : it admits eigenvalues that are eitehr
strictly positive or strictly negative. It can be factored in LDL
T
form:
csr<Float> A = neumann_laplace_assembly (a.uu, b.u);
ssk<Float> fact_A = ldlt(A);
The rest of the code is mostly standard. The statement
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("u") << uh
<< catchmark("lambda") << lambda << endl;
writes the solution (u
h
, ) with full digit precision and labeled format, suitable for post-traitement,
visualization and error analysis. It remain to look at the / matrix assembly function.
File neumann-laplace-assembly.h
template <class T>
csr<T> neumann_laplace_assembly (const csr<T>& a, const vec<T>& b) {
size_t n = a.nrow();
asr<T> A (n+1, n+1);
Array<size_t>::const_iterator ia = a.ia().begin();
Array<size_t>::const_iterator ja = a.ja().begin();
typename Array<T>::const_iterator va = a.a().begin();
typename Array<T>::const_iterator vb = b.begin();
for (size_t i = 0; i < n; i++) {
for (size_t p = ia[i]; p < ia[i+1]; p++) {
A.entry(i,ja[p]) = va[p];
}
A.entry(i,n) = A.entry(n,i) = vb[i];
}
return csr<T>(A);
}
Comments
The function takes as input the a matrix in compressed sparse row format csr (see e.g. [5]), and
the b vector. It uses a temporary associative sparse row data structure asr for / that is converted
to csr at the end of the function.
2.4.1 How to run the program
As usual, enter:
make neumann-laplace
mkgeo_grid -t 10 -boundary > square.geo
./neumann-laplace square P1 | field -
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
28 Rheolef version 5.94 update 27 May 2011
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Part II
Fluids and solids computations
29
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 3
The linear elasticity and the
Stokes problems
3.1 The linear elasticity problem
Formulation
The total Cauchy stress tensor expresses:
(u) = div(u).I + 2D(u)
where and are the Lame coecients. Let us consider the elasticity problem for the embankment,
in =]0, 1[
N
, N = 2, 3. The problem writes:
(E): nd u = (u
0
, . . . , u
N1
), dened in , such that:
div (u) = f in ,
u
n
= 0 on
top
right
u = 0 on
left
bottom
,
u = 0 on
front
back
, when N = 3
where f = (0, 1) when N = 2 and f = (0, 0, 1) when N = 3. The lame coecients are assumed
to satisfy > 0 and + > 0. Since the problem is linear, we can suppose that = 1 without
any loss of generality.
x
2
x
1
left
right
bottom
top
front
x
1
x
0
bottom
right left
top
x
0
back
Figure 3.1: The boundary domains for the square and the cube.
31
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
32 Rheolef version 5.94 update 27 May 2011
In order to avoid mistakes with the C++ style indexation, we denote by (x
0
, . . . , x
N1
) the cartesian
coordinate system in R
N
.
For N = 2 we dene the boundaries:
left
= 0]0, 1[,
right
= 1]0, 1[
bottom
= ]0, 1[0,
top
= ]0, 1[1
and for N = 3:
back
= 0]0, 1[
2
,
front
= 1]0, 1[
2
left
= ]0, 1[0]0, 1[,
right
= ]0, 1[1]0, 1[
bottom
= ]0, 1[
2
0,
top
= ]0, 1[
2
1
These boundaries are represented on Fig. 3.1.
The variational formulation of this problem expresses:
(V FE): nd u V such that:
a(u, v) = m(f , v), v V,
where
a(u, v) =
_
div udiv v dx +
_
u.v dx,
V = v (H
1
())
2
; v = 0 on
left
bottom
, when N = 2
V = v (H
1
())
2
; v = 0 on
left
bottom
right
back
, when N = 3
Approximation
We introduce a mesh T
h
of and for k = 1, 2, the following nite dimensional spaces:
X
h
= v (H
1
())
N
; v
/K
(P
k
)
N
, K T
h
,
V
h
= X
h
V
The approximate problem writes:
(V FE)
h
: nd u
h
V
h
such that:
a(u
h
, v) = m(f , v
h
), v V
h
File embankment.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
#include "embankment.h"
int main(int argc, char**argv) {
const Float lambda = 1;
geo omega (argv[1]);
space Vh = embankment_space (omega, argv[2]);
field uh (Vh, 0.0), fh (Vh, 0.0);
fh [omega.dimension()-1] = -1.0;
form m (Vh, Vh, "mass");
form a1 (Vh, Vh, "div_div");
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 33
form a2 (Vh, Vh, "2D_D");
form a = lambda*a1 + a2;
if (omega.dimension() < 3) {
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
} else {
size_t max_iter = 10000;
Float tol = 1e-15;
uh.u = 0;
int status = pcg (a.uu, uh.u, m.uu*fh.u + m.ub*fh.b - a.ub*uh.b,
ic0(a.uu), max_iter, tol, &cerr);
}
cout << catchmark("lambda") << lambda << endl
<< catchmark("u") << uh;
return 0;
}
File embankment.h
space embankment_space (const geo& omega_h, std::string approx) {
space Vh (omega_h, approx, "vector");
Vh.block("bottom"); Vh.block("left");
if (omega_h.dimension() == 3) {
Vh.block("right"); Vh.block("back");
}
return Vh;
}
Comments
The space is dened in a separate le embankment.h, for subsequent reusage:
space Vh (omega_h, "P2", "vector");
Note here the multi-components features for the velocity eld u. The boundary condition contain
a special case for tridimensionnal case. The right-hand-side f
h
represents the dimensionless gravity
forces, oriented on the vertical axis: the last component of f
h
is set to 1 as:
fh [omega_h.dimension()-1] = -1.0;
Finally, the parameter and the multi-eld result are printed, using mark labels, thanks to the
catchmark stream manipulator. Labels are convenient for post-processing purpose, as we will see
in the next paragraph.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
34 Rheolef version 5.94 update 27 May 2011
How to run the program
Figure 3.2: The linear elasticity for = 1 and N = 2 and N = 3: both wireframe and lled
surfaces ; stereoscopic anaglyph mode for 3D solutions.
We assume that the previous code is contained in the le embankment.cc. Compile the program
as usual (see page 12):
make embankment
and enter the comands:
mkgeo_grid -t 10 > square.geo
geo square.geo
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 35
The triangular mesh has four boundary domains, named left, right, top and bottom. Then,
enter:
./embankment square.geo P1 > square-P1.mfield
The previous command solves the problem for the corresponding mesh and writes the solution in
the multi-eld le format .mfield. Run the deformation vector eld visualization:
mfield square-P1.mfield -u -deformation
mfield square-P1.mfield -u -deformation -fill
Note the graphic options usage ; the unix manual for the mfield command is available as:
man mfield
The view is shown on Fig. 3.2. A specic eld can be also selected for a scalar visualization:
mfield -u0 square-P1.mfield | field -
mfield -u1 square-P1.mfield | field -
Next, perform a P
2
approximation of the solution:
./embankment square.geo P2 > square-P2.mfield
mfield -u square-P2.mfield -deformation
Finally, let us consider the three dimensional case
mkgeo_grid -T 10 > cube.geo
./embankment cube.geo P1 > cube-P1.mfield
mfield -u -deformation cube-P1.mfield -stereo
mfield -u -deformation cube-P1.mfield -stereo -fill
The two last commands show the solution in 3D stereoscopic anaglyph mode. The graphic is
represented on Fig. 3.2. The P
2
approximation writes:
./embankment cube.geo P2 > cube-P2.mfield
mfield -u -deformation cube-P2.mfield
3.2 Computing the stress tensor
Formulation and approximation
The following code computes the total Cauchy stress tensor, reading the Lame coecient and
the deformation eld u
h
from a .mfield le. Let us introduce:
T
h
=
h
(L
2
())
NN
;
h
=
T
h
and
h;ij/K
P
k1
, K T
h
, 1 i, j N
This computation expresses:
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
36 Rheolef version 5.94 update 27 May 2011
nd
h
such that:
m(
h
, ) = b(, u
h
), T
h
where
m(, ) =
_
: dx,
b(, u) =
_
div(u) tr dx +
_
2D(u) : dx,
tr =
N
i=1
ii
.
File stress.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
int main(int argc, char** argv) {
Float lambda;
field uh;
cin >> catchmark("lambda") >> lambda
>> catchmark("u") >> uh;
string approx = (uh.get_approx() == "P2") ? "P1d" : "P0";
space Th (uh.get_geo(), approx, "tensor");
space Xh (uh.get_geo(), approx);
form two_D (uh.get_space(), Th, "2D");
form div (uh.get_space(), Xh, "div");
form inv_mt (Th, Th, "inv_mass");
form inv_m (Xh, Xh, "inv_mass");
field q = inv_m*(div*uh);
field sh = inv_mt*(two_D*uh);
for (size_t i = 0; i < uh.dimension(); i++)
sh(i,i) += lambda*q;
cout << catchmark("s") << sh;
return 0;
}
Comments
First notice that this code applies for any deformation eld, and is not rectricted to our embank-
ment problem.
The P0 and P1d stands for the piecewise constant and picewise linear discontinuous approximations,
respectively. Since elements of T
h
are discontinuous accross interelement boundaries, the mass
operator is block-diagonal and can be inverted one time for all: this operation results in the
inv mass operator.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 37
How to run the program
Figure 3.3: The stress tensor visualization (linear elasticity = 1).
First, compile the program:
make stress
The visualization for the stress tensor as ellipes writes:
./stress < square-P1.mfield | mfield -s -tensor -proj -
Conversely, the 3D visualization bases on ellipsoides:
./stress < cube-P1.mfield | mfield -s -tensor -proj -
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
38 Rheolef version 5.94 update 27 May 2011
Figure 3.4: The
01
stress component (linear elasticity = 1): d = 2 (top) and d = 3 (bottom) ;
P
0
(left) and P
1
discontinuous approximation (right).
You can observe a discontinuous constant or piecewise linear representation of the approximate
stress component
00
(see Fig. 3.4):
./stress < square-P1.mfield | mfield -s01 - | field -
./stress < square-P2.mfield | mfield -s01 - | field -elevation -stereo -
Recall that the stress, as a derivative of the deformation, is P0 (resp. P1d) and discontinuous
when the deformation is P1 (resp. P2) and continuous. The approximate stress eld can be also
projected on a continuous piecewise linear space, using the -proj option:
./stress < square-P1.mfield | mfield -s01 - | field -proj -elevation -stereo -
./stress < square-P2.mfield | mfield -s01 - | field -proj -elevation -stereo -
The tridimensionnal case writes simply (see Fig. 3.4):
./stress < cube-P1.mfield | mfield -s01 - | field -stereo -
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 39
./stress < cube-P2.mfield | mfield -s01 - | field -stereo -fill -
and also the P1-projected versions write:
./stress < cube-P1.mfield | mfield -s01 - | field -proj -stereo -
./stress < cube-P2.mfield | mfield -s01 - | field -proj -stereo -
These operations can be repeated for each
ij
components and for both P1 and P2 approximation
of the deformation eld.
3.3 Mesh adaptation
The main principle of the auto-adaptive mesh writes [611]:
cin >> omega_h;
uh = solve(omega_h);
for (unsigned int i = 0; i < n; i++) {
ch = criteria(uh);
omega_h = adapt(ch);
uh = solve(omega_h);
}
The initial mesh is used to compute a rst solution. The adaptive loop compute an adaptive
criteria, denoted by ch, that depends upon the problem under consideration and the polynomial
approximation used. Then, a new mesh is generated, based on this critera. A second solution on
an adapted mesh can be constructed. The adaptation loop converges generaly in roughly 10 to 20
iterations.
Let us apply this principle to the elasticity problem.
File embankment-adapt-2d.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
#include "embankment.h"
field criteria(Float lambda, const field& uh) {
string approx = (uh.get_approx() == "P2") ? "P1d" : "P0";
if (approx == "P0") return sqrt(sqr(uh[0])+sqr(uh[1]));
space Th (uh.get_geo(), approx, "tensor");
space Xh (uh.get_geo(), approx);
form two_D (uh.get_space(), Th, "2D");
form div (uh.get_space(), Xh, "div");
form mt (Th, Th, "mass");
form m (Xh, Xh, "mass");
form inv_mt (Th, Th, "inv_mass");
form inv_m (Xh, Xh, "inv_mass");
field qh = inv_m*(div*uh);
field two_Duh = inv_mt*(two_D*uh);
return sqrt(lambda*sqr(qh) + sqr(field(two_Duh(0,0)))
+ sqr(field(two_Duh(1,1))) + 2*sqr(field(two_Duh(0,1))));
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
40 Rheolef version 5.94 update 27 May 2011
field solve(const geo& omega, const string& approx, Float lambda) {
space Vh = embankment_space(omega, approx);
field uh (Vh, 0.0), fh (Vh, 0.0);
fh[1] = -1.0;
form m (Vh, Vh, "mass");
form a1 (Vh, Vh, "div_div");
form a2 (Vh, Vh, "2D_D");
form a = lambda*a1 + a2;
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
return uh;
}
int main(int argc, char**argv) {
const Float lambda = 1;
geo omega_h (argv[1]);
string approx = (argc > 2) ? argv[2] : "P1";
size_t n_adapt = (argc > 3) ? atoi(argv[3]) : 5;
adapt_option_type options;
options.hcoef = 1.1;
options.hmin = 0.004;
for (size_t i = 0; true; i++) {
field uh = solve(omega_h, approx, lambda);
orheostream o (omega_h.name(), "mfield");
o << catchmark("lambda") << lambda << endl
<< catchmark("u") << uh;
if (i == n_adapt) break;
field ch = criteria(lambda,uh);
omega_h = geo_adapt(ch, options);
omega_h.save();
}
}
Comments
The code works for both linear or quadratic approximations. Since there is not yet any available
anisotropic adaptive mesh generator in three dimension, we have specialized here the code for the
dimension two.
The criteria is here:
c
h
=
_
[u
h
[ when using P
1
((u
h
) : D(u
h
))
1/2
when using P
2
The adapt option type declaration is used by rheolef to send options to the mesh generator.
The hcoef parameter controls the edge length of the mesh: the smaller it is, the smaller the edges
of the mesh are. In our example, is set by default to one. Conversely, the hmin parameter controls
minimal edge length.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 41
How to run the program
Figure 3.5: Adapted meshes: the deformation visualization for P
1
and P
2
approximations.
The compilation command writes:
make embankment-adapt-2d
For a piecewise linear approximation:
mkgeo_grid -t 10 > square-P1.geo
./embankment-adapt-2d square-P1 P1 5
The code performs a loop of ve mesh adaptations: the corresponding meshes are stored in les,
from square-P1-1.geo.gz to square-P1-5.geo.gz, and the associated solutions in les, from
square-P1-1.mfield.gz to square-P1-5.mfield.gz. The additional .gz sux expresses that
the les are compressed using gzip.
geo square-P1-5.geo
mfield -u -deformation square-P1-5.mfield
Note that the .gz sux is assumed by the geo and the mfield commands. Enter the w key in
the graphic window: you will obtain the graphic shown on Fig.3.5.
For a piecewise quadratic approximation:
mkgeo_grid -t 10 > square-P2.geo
./embankment-adapt-2d square-P2 P2
Then, the visualization writes:
geo square-P2-5.geo
mfield -u -deformation square-P2-5.mfield
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
42 Rheolef version 5.94 update 27 May 2011
3.4 The Stokes problem
Formulation
Let us consider the Stokes problem for the driven cavity in =]0, 1[
N
, N = 2, 3. The problem
writes:
(S) nd u = (u
0
, . . . , u
N1
) and p dened in such that:
div(2D(u)) + p = 0 in ,
div u = 0 in ,
u = (1, 0) on
top
,
u = 0 on
left
right
bottom
,
u
0
n
=
u
1
n
= u
2
= 0 on
back
front
when N = 3,
where D(u) = (u +u
T
)/2. The boundaries are represented on Fig. 3.1, page 31.
The variational formulation of this problem expresses:
(V FS) nd u V(1) and p L
2
0
() such that:
a(u, v) + b(v, p) = 0, v V(0),
b(u, q) = 0, q L
2
0
(),
where
a(u, v) =
_
div(v) q dx.
V() = v (H
1
())
2
; v = 0 on
left
right
bottom
and v = (, 0) on
top
, when N = 2,
V() = v (H
1
())
3
; v = 0 on
left
right
bottom
,
v = (, 0, 0) on
top
and v
2
= 0 on
back
front
, when N = 3,
L
2
0
() = q L
2
();
_
q dx = 0.
Approximation
The Talor-Hood [12] nite element approximation of the Stokes problem is considered. We intro-
duce a mesh T
h
of and the following nite dimensional spaces:
X
h
= v (H
1
())
N
; v
/K
(P
2
)
N
, K T
h
,
V
h
() = X
h
V(),
Q
h
= q L
2
()) C
0
(
); q
/K
P
1
, K T
h
,
The approximate problem writes:
(V FS)
h
nd u
h
V
h
(1) and p Q
h
such that:
a(u
h
, v) + b(v, p
h
) = 0, v V
h
(0),
b(u
h
, q) = 0, q Q
h
.
(3.1)
File cavity.h
space cavity_space (const geo& omega_h, std::string approx) {
space Vh (omega_h, approx, "vector");
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 43
Vh.block("top"); Vh.block("bottom");
if (omega_h.dimension() == 3) {
Vh.block("back"); Vh.block("front");
Vh[1].block("left"); Vh[1].block("right");
} else {
Vh.block("left"); Vh.block("right");
}
return Vh;
}
field cavity_field (const space& Vh, Float alpha) {
field uh (Vh, 0.);
uh[0]["top"] = alpha;
if (Vh.dimension() == 3) { // set velocity=0 at corners
uh[0]["back"] = uh[0]["front"] = 0;
} else {
uh[0]["left"] = uh[0]["right"] = 0;
}
return uh;
}
File stokes-cavity.cc
#include "rheolef.h"
#include "rheolef/mixed_solver.h"
using namespace rheolef;
using namespace std;
#include "cavity.h"
#include "pcg_solver.h"
int main(int argc, char**argv) {
geo omega (argv[1]);
space Vh = cavity_space (omega, "P2");
space Qh (omega, "P1");
field uh = cavity_field (Vh, 1);
field ph (Qh, 0.);
form mp (Qh, Qh, "mass");
form a (Vh, Vh, "2D_D");
form b (Vh, Qh, "div"); b = -b;
int max_iter = 5000;
Float tol = 1e-15;
if (omega.dimension() < 3) {
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, -(a.ub*uh.b), -(b.ub*uh.b),
ldlt(mp.uu), ldlt(a.uu), max_iter, tol, &cerr);
} else {
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, -(a.ub*uh.b), -(b.ub*uh.b),
pcg_solver(mp.uu), pcg_solver(a.uu), max_iter, tol, &cerr);
}
cout << catchmark("u") << uh
<< catchmark("p") << ph;
return 0;
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
44 Rheolef version 5.94 update 27 May 2011
Comments
The spaces and boundary conditions and grouped in specic functions, dened in le cavity.h.
This le is suitable for a future reusage. Next, forms are dened as usual, in le
stokes-cavity.cc.
The problem admits the following matrix form:
_
a.uu trans(b.uu)
b.uu 0
__
uh.u
ph.u
_
=
_
a.ub uh.b
b.ub uh.b
_
An initial value for the pressure eld is provided:
field ph (Qh, 0);
This system is solved by the preconditioned conjugate gradient algorithm :
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, -(a.ub*uh.b), -(b.ub*uh.b),
ldlt(mp.uu), ldlt(a.uu), max_iter, tol, &cerr);
The preconditioner is here the mass matrix mp.uu for the pressure: as showed in [13], the number of
iterations need by the conjugate gradient algorithm to reach a given precision is then independent
of the mesh size. For more details, see the Rheolef reference manual related to mixed solvers,
available e.g. via the unix command:
man mixed_solver
When d = 2, it is interessant to factorize both a.uu and the preconditionner mp.uu one time
for all the iterations: the arguments ldlt(mp.uu) and ldlt(a.uu) are passed to the pcg abtb
algorithm. When d = 3, it is preferable, for both computing time and memory occupation point
of view, to switch to an interative solver for subproblems related to a.uu and mp.uu:
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, -(a.ub*uh.b), -(b.ub*uh.b),
pcg_solver(mp.uu), pcg_solver(a.uu), max_iter, tol, &cerr);
Finaly, the pcg solver class is a convenient wrapper class for the call to the pcg algorithm, used
for 3D problems. It uses the incomplete Choeski factorization preconditionner ic0.
File pcg solver.h
struct pcg_solver {
pcg_solver (const csr<Float>& a1) : a(a1), m(ic0(a1)) {}
vec<Float> solve (const vec<Float>& b) const {
size_t max_iter = 10000;
Float tol = 1e-15;
vec<Float> x (b.size(), 0.0);
int status = pcg (a, x, b, m, max_iter, tol);
return x;
}
csr<Float> a;
basic_ic0<Float> m;
};
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 45
How to run the program
Figure 3.6: The velocity visualization for N = 2 and N = 3 with stereo anaglyph.
We assume that the previous code is contained in the le stokes-cavity.cc. Then, compile the
program as usual (see page 12):
make stokes-cavity
and enter the comands:
mkgeo_grid -t 10 > square.geo
./stokes-cavity square > cavity2d.mfield
The previous command solves the problem for the corresponding mesh and writes the solution in
the multi-eld le format .mfield. Run the velocity vector visualization :
mfield cavity2d.mfield -u -velocity
Run also some scalar visualizations:
mfield cavity2d.mfield -u0 | field -
mfield cavity2d.mfield -u1 | field -
mfield cavity2d.mfield -p | field -
Next, perform another computation on a ner mesh:
mkgeo_grid -t 20 > square-fine.geo
./stokes-cavity square-fine.geo > cavity2d-fine.mfield
and observe the convergence.
Finally, let us consider the three dimensional case:
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
46 Rheolef version 5.94 update 27 May 2011
mkgeo_grid -T 5 > cube.geo
./stokes-cavity cube.geo > cavity3d.mfield
and the corresponding visualization:
mfield cavity3d.mfield -u -velocity -stereo
mfield cavity3d.mfield -u0 | field -
mfield cavity3d.mfield -u1 | field -
mfield cavity3d.mfield -u2 | field -
mfield cavity3d.mfield -p | field -
3.5 Computing the vorticity
Formulation and approximation
When N = 2, we dene [3, page 30] for any distributions and v:
curl =
_
x
1
,
x
0
_
,
curl v =
v
1
x
0
v
0
x
1
,
and when N = 3:
curl v =
_
v
2
x
1
v
1
x
2
,
v
0
x
2
v
2
x
0
,
v
1
x
0
v
0
x
1
_
Let u be the solution of the Stokes problem (S). The vorticity is dened by:
= curl u when N = 2,
= curl u when N = 3.
Since the approximation of the velocity is piecewise quadratic, we are looking for a discontinuous
piecewise linear vorticity eld that belongs to:
Y
h
= L
2
();
/K
P
1
, K T
h
, when N = 2
Y
h
= (L
2
())
3
;
i/K
P
1
, K T
h
, when N = 3
The approximate variational formulation writes:
h
Y
h
,
_
h
dx =
_
curl u
h
dx, Y
h
when N = 2,
Y
h
,
_
h
. dx =
_
curl u
h
. dx, Y
h
when N = 3.
File vorticity.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
int main(int argc, char** argv) {
field uh;
cin >> uh;
string option = (uh.n_component() == 3) ? "vector" : "scalar";
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 47
space Lh (uh.get_geo(), "P1d", option);
form curl (uh.get_space(), Lh, "curl");
form inv_m (Lh, Lh, "inv_mass");
cout << catchmark("w") << inv_m*(curl*uh);
return 0;
}
How to run the program
Figure 3.7: The vorticity: elevation view for N = 2 and vector representation for N = 3 (with
anaglyph).
For N = 2, just enter:
make vorticity
./vorticity < cavity2d.mfield | field -evelavtion -stereo -
and you observe a discontinuous piecewise linear representation of the approximate vorticity. The
approximate vorticity eld can also be projected on a continuous piecewise linear space, using the
-proj option (See Fig. 3.7 left):
./vorticity < cavity2d.mfield | field -proj -
./vorticity < cavity2d.mfield | \
field -proj -elevation -scale 2 -stereo -nofill -
For N = 3, the whole vorticity vector can also be visualized (See Fig. 3.7 right):
./vorticity < cavity3d.mfield | \
mfield -proj -w -velocity -vscale 2 -stereo -
In the previous command, the -proj option has been used: since the 3D render has no support for
discontinuous picewise linear elds, the P1-discontinuous eld is transformed into a P1-continuous
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
48 Rheolef version 5.94 update 27 May 2011
one, thanks to a L
2
projection. P1 The following command shows the second component of the
vorticity vector, roughtly similar to the bidimensionnal case.
./vorticity < cavity3d.mfield | mfield -w1 - | field -
./vorticity < cavity3d.mfield | mfield -w1 - | field -proj -
3.6 Computing the stream function
Formulation and approximation
The stream function satises curl = u. When N = 2, the stream function is the solution of
the following problem [3, page 88]:
= curl u in ,
= 0 on .
and when N = 3, the stream function is a vector-valued eld that satises [3, page 90]:
= curl u in ,
= 0 on
back
front
top
bottom
,
n
= 0 on
left
right
.
File streamf-cavity.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
int main (int argc, char** argv) {
field wh;
cin >> wh;
string option = (wh.n_component() == 3) ? "vector" : "scalar";
space Ph (wh.get_geo(), "P2", option);
Ph.block("top"); Ph.block("bottom");
if (wh.dimension() == 3) {
Ph.block("back"); Ph.block("front");
} else {
Ph.block("left"); Ph.block("right");
}
form m (wh.get_space(), Ph, "mass");
form c (Ph, Ph, "grad_grad");
field psih (Ph, 0.);
ssk<Float> fact_c = ldlt(c.uu);
psih.u = fact_c.solve (m.uu*wh.u + m.ub*wh.b - c.ub*psih.b);
cout << catchmark("psi") << psih;
return 0;
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 49
How to run the program
Figure 3.8: The stream function visualization: isolines for N = 2, and combined vectors and
isonorm surface for N = 3.
For N = 2, just enter (see Fig. 3.8 left):
make streamf-cavity
./vorticity < cavity2d.mfield | ./streamf-cavity | field -black-and-white -
For N = 3, the second component of the stream function is showed by:
./vorticity < cavity3d.mfield | ./streamf-cavity | mfield -psi1 - | \
field -normal 0 1 0 -
The whole stream function vector can be visualized:
./vorticity < cavity3d.mfield | ./streamf-cavity | \
mfield -psi -velocity
The combined representation of Fig. 3.8.right has been obtained in three steps. First, enter:
./vorticity < cavity3d.mfield | ./streamf-cavity | mfield -psi1 - | \
field -normal 0 1 0 -noclean -noexecute -name psi1 -
This command creates a le psi1.vtk. Next, in the previous mayavi window associated to the
whole stream function, select the File/Load data/VTK file menu and load psi1.vtk. Finaly,
select the Vizualize/Module/IsoSurface menu. Observe that the 3D stream function is mainly
represented by its second component.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
50 Rheolef version 5.94 update 27 May 2011
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 4
Nearly incompressible elasticity
and the stabilized Stokes problems
4.1 The incompressible elasticity problem
Formulation
Let us go back to the linear elasticity problem.
When becomes large, this problem is related to the incompressible elasticity and cannot be
solved as it was previously done. To overcome this diculty, the pressure is introduced :
p = div u
and the problem becomes:
(E) nd u and p dened in such that:
div(2D(u)) + p = f in ,
div u
1
p = 0 in ,
+B.C.
The variational formulation of this problem expresses:
(V FE) nd u V (1) and p L
2
() such that:
a(u, v) + b(v, p) = m(f , v), v V (0),
b(u, q) c(p, q) = 0, q L
2
0
(),
where
m(u, v) =
_
u.v dx,
a(u, v) =
_
div(v) q dx.
c(p, q) =
1
p q dx.
V = v (H
1
())
2
; v = 0 on
left
bottom
When becomes large, we obtain the incompressible elasticity problem, that coincides with the
Stokes problem.
51
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
52 Rheolef version 5.94 update 27 May 2011
Approximation
As for the Stokes problem, the Talor-Hood [12] nite element approximation is considered. We
introduce a mesh T
h
of and the following nite dimensional spaces:
X
h
= v (H
1
()); v
/K
(P
2
)
2
, K T
h
,
V
h
() = X
h
V,
Q
h
= q L
2
()) C
0
(
); q
/K
P
1
, K T
h
,
The approximate problem writes:
(V FE)
h
nd u
h
V
h
(1) and p Q
h
such that:
a(u
h
, v) + b(v, p
h
) = 0, v V
h
(0),
b(u
h
, q) c(p, q) = 0, q Q
h
.
File incompresible-elasticity.cc
#include "rheolef.h"
#include "rheolef/mixed_solver.h"
using namespace rheolef;
using namespace std;
#include "embankment.h"
#include "pcg_solver.h"
int main(int argc, char**argv) {
geo omega (argv[1]);
Float inv_lambda = (argc > 2 ? atof(argv[2]) : 0);
space Vh = embankment_space(omega, "P2");
space Qh (omega, "P1");
field uh (Vh,0.0), fh (Vh, 0.0);
fh [omega.dimension()-1] = -1.0;
field ph (Qh, 0.);
form mu (Vh, Vh, "mass");
form mp (Qh, Qh, "mass");
form a (Vh, Vh, "2D_D");
form b (Vh, Qh, "div"); b = -b;
form c = inv_lambda*mp;
int max_iter = 5000;
Float tol = 1e-15;
if (omega.dimension() < 3) {
int status = pcg_abtbc (a.uu, b.uu, c.uu, uh.u, ph.u, -(a.ub*uh.b) + (mu*fh).u,
-(b.ub*uh.b), ldlt(mp.uu), ldlt(a.uu), max_iter, tol, &cerr);
} else {
int status = pcg_abtbc (a.uu, b.uu, c.uu, uh.u, ph.u, -(a.ub*uh.b) + (mu*fh).u,
-(b.ub*uh.b), pcg_solver(mp.uu), pcg_solver(a.uu), max_iter, tol, &cerr);
}
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("inv_lambda") << inv_lambda << endl
<< catchmark("u") << uh
<< catchmark("p") << ph;
return 0;
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 53
Comments
The problem admits the following matrix form:
_
a.uu trans(b.uu)
b.uu c.uu
__
uh.u
ph.u
_
=
_
mfh.u a.ub uh.b
b.ub uh.b
_
The problem is similar to the Stokes one (see page 44): this system is solved by the preconditioned
conjugate gradient algorithm :
int status = pcg_abtbc (a.uu, b.uu, c.uu, uh.u, ph.u, -(a.ub*uh.b) + (mu*fh).u,
-(b.ub*uh.b), ldlt(mp.uu), ldlt(a.uu), max_iter, tol, &cerr);
The preconditioner is here the mass matrix mp.uu for the pressure. As showed in [13], the number of
iterations need by the conjugate gradient algorithm to reach a given precision is then independent
of the mesh size and is uniformly bounded when becomes small, i.e. in the incompressible case.
How to run the program
Figure 4.1: The incompressible linear elasticity ( = +) for N = 2 and N = 3.
We assume that the previous code is contained in the le incompressible-elasticity.cc.
Compile the program as usual (see page 12):
make incompressible-elasticity
and enter the comands:
mkgeo_grid -t 10 > square.geo
./incompressible-elasticity square.geo 0 > square.mfield
mfield -u -deformation square.mfield
mkgeo_grid -T 10 > cube.geo
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
54 Rheolef version 5.94 update 27 May 2011
./incompressible-elasticity cube.geo 0 > cube.mfield
mfield -u -deformation cube.mfield -fill
The visualization is performed as usual: see section 3.1, page 34. Compare the results on Fig. 4.1,
obtained for = + with those of Fig. 3.2, page 34, obtained for = 1.
Finally, the stress computation and the mesh adaptation loop is left as an execice to the reader.
4.2 The P
1
bubble element for the Stokes problem
Formulation and approximation
Let us go back to the Stokes problem. In section 3.4, page 42, the Taylor-Hood nite element was
considered. Here, we turn to the mini-element [14] proposed by Arnold, Brezzi and Fortin, also
well-known as the P1-bubble element. This element is generaly less precise than the Taylor-Hood
one, but becomes popular, mostly because it is easy to implement in two and three dimensions
and furnishes a P
1
approximation of the velocity eld. Moreover, this problem develops some links
with stabilization technics and will presents some new rheolef features.
We consider a mesh T
h
of R
N
, N = 2, 3 composed only of simplicial elements: triangles when
N = 2 and tetraedra when N = 3. The following nite dimensional spaces are introduced:
X
(1)
h
= v (H
1
())
N
; v
/K
(P
1
)
N
, K T
h
,
B
h
= (C
0
(
))
N
;
/K
B(K)
N
, K T
h
X
h
= X
(1)
h
B
h
V
h
() = X
h
V(),
Q
h
= q L
2
()) C
0
(
); q
/K
P
1
, K T
h
,
where B(K) = vect(
1
. . .
N+1
) and
i
are the barycentric coordinates of the simplex K.
The B(K) space is related to the bubble local space. The approximate problem is similar to (3.1),
page 42, up to the choice of nite dimensional spaces.
Remark that the velocity eld splits in two terms: u
h
= u
(1)
h
+u
(b)
h
, where u
(1)
h
X
(1)
h
is continuous
and piecewise linear, and u
(b)
h
B
h
is the bubble term.
File contraction-bubble.cc
#include "rheolef.h"
#include "rheolef/mixed_solver.h"
using namespace rheolef;
using namespace std;
Float u_upstream (const point& x) { return sqr(8.-x[0])/512.; }
int main(int argc, char**argv) {
geo omega (argv[1]);
domain upstream = omega["upstream"];
space X1h (omega, "P1", "vector");
space Bh (omega, "bubble", "vector");
space Qh (omega, "P1");
space Wh (omega, upstream, "P1");
X1h.block ("wall");
X1h.block ("upstream");
X1h[0].block ("axis");
X1h[0].block ("downstream");
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 55
X1h[0].block ("axis");
space Xh = X1h * Bh;
field uh (Xh);
field ph (Qh, 0.);
uh[0]["wall"] = 0;
uh[1]["wall"] = 0;
uh[0]["upstream"] = 0;
uh[1]["upstream"] = interpolate(Wh,u_upstream);
uh[0]["downstream"] = 0;
uh[0]["axis"] = 0;
form a1 (X1h, X1h, "2D_D");
form ab (Bh, Bh, "2D_D");
form a0 = form_nul (Bh, X1h);
form_manip a_manip;
a_manip << size(2,2)
<< a1 << a0
<< trans(a0) << ab;
form a (Xh, Xh);
a_manip >> a;
ssk<Float> fact_a = ldlt(a.uu);
form b1 = - form(X1h, Qh, "div");
form bb = - form(Bh, Qh, "div");
form_manip b_manip;
b_manip << size(1,2)
<< b1 << bb;
form b (Xh, Qh);
b_manip >> b;
form mp (Qh, Qh, "mass");
ssk<Float> fact_mp = ldlt(mp.uu);
int max_iter = 500;
Float tol = 1e-14;
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, -(a.ub*uh.b), -(b.ub*uh.b),
fact_mp, fact_a, max_iter, tol, &cerr);
cout << catchmark("u") << uh
<< catchmark("p") << ph;
return status;
}
Comments
The boundary conditions are suitable for a ow in an abrupt contraction geometry. The matrix
structure is similar to those of the Taylor-Hood element, and thus the same ecient augmented
Lagrangian algorithms applies.
The global form a(., .) over X
h
is obtained by concatenation of the forms a
1
(., .) and a
b
(., .) over
X
(1)
h
and B
h
repectively, thanks to the form manip class:
a =
_
a1 0
0 ab
_
How to run the program
The boundary conditions in this example are related to an abrupt contraction geometry with a
free surface. The corresponding mesh contraction.geo can be easily builded from the geometry
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
56 Rheolef version 5.94 update 27 May 2011
description les contraction.bamgcad and contraction.dmn provided with the the rheolef
distribution. The directory where examples are available is given by the following unix command:
rheolef-config --exampledir
The building mesh procedure is presented in appendix A, page A. Running this example is left as
an exercice to the reader.
4.3 The stabilized Stokes problem
An alternative and popular implementation of this element eliminates the unknowns related to
the bubble components (see e.g. [15], page 24). This elimination can be easily performed since
the form a
b
(., .) over B
h
is diagonal, due to the fact that the bubble functions vanishes on the
boundary of elements. The system reduces to:
_
A1 B1
T
B1 C
__
U1
P
_
=
_
F
G
_
Remarks that the matrix structure is similar to those of the nealy incompressible elasticity (see 4.1,
page 4.1). A direct nite element formulation for this problem is known as the P
1
P
1
stabilized
element, proposed by Brezzi and Pitkaranta [16].
4.4 Axisymetric geometries
The coordinate system is associated to the geometry description, stored together with the mesh
in the .geo:
mkgeo_grid -t 10 -rz > square-rz.geo
more square-rz.geo
Note the additional line in the header:
...
coordinate_system rz
...
Here "rz" means that the coordinate system (x
0
, x
1
) = (r, z). Notes that the coordinate system
(z, r) is also supported but not fully tested yet: it may also be implemented by a suitable coordinate
swap. The "cartesian" argument string is also supported and means that the usual coordinate
system may be used. In the "rz" case, the L
2
functional space is equipped with the following
weighted scalar product
(f, g) =
_
t
+u. = 0 in ]0, T[
(0) =
0
in
(t) =
(t) on ]0, T[
where u,
0
and
being known.
1
At this time, the avi output feature is broken in paraview, and an alternate mpeg output is here suggested.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 63
Time approximation
This problem is approximated by the following rst-order implicit Euler scheme:
n+1
n
X
n
t
n+1
= 0 in
where t > 0,
n
(nt) and
(0)
=
0
.
Let t
n
= nt, n 0. The term X
n
(x) is the position at t
n
of the particule that is in x at t
n+1
and is transported by u
n
. Thus, X
n
(x) = X(x, t
n
) where X(x, t) is the solution of the dierential
equation
_
dX
dt
= u(X(x, t), t) p.p. t ]t
n
, T
n+1
[,
X(x, t
n+1
) = x.
Then X
n
(x) is approximated by the rst-order Euler approximation
X
n
(x) x t n
n
(x).
This algorithm has been introduced by O. Pironneau (see e.g. [17]), and is known as the method
of characteristic. The ecient localization of X
n
(x) in an unstructured mesh involves a quadtree
data structure
2
.
The following code implements the classical rotating Gaussian hill (see e.g. [18]).
File convect.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
Float nu = 1e-3;
point u (const point & x) { return point(-x[1], x[0]); }
struct phi : unary_function<point,Float> {
Float operator() (const point& x) {
point xc (0.25, 0, 0);
Float sigma = 0.01;
return sigma/(sigma + 4*nu*t)
* exp(-(sqr( x[0]*cos(t) + x[1]*sin(t) - xc[0])
+ sqr(-x[0]*sin(t) + x[1]*cos(t) - xc[1]))/(sigma + 4*nu*t));
}
phi (Float tau) : t(tau) {}
protected: Float t;
};
int main (int argc, char **argv) {
geo omega (argv[1]);
size_t n_max = (argc > 2) ? atoi(argv[2]) : 24;
Float delta_t = 2*acos(-1.)/n_max;
space Vh (omega, "P1");
Vh.block ("boundary");
form m (Vh, Vh, "mass");
form a (Vh, Vh, "grad_grad");
form c = m + delta_t*nu*a;
ssk<Float> c_fact = ldlt (c.uu);
field uh = interpolate (Vh*Vh, u);
2
The quadtree is only implemented for two-dimensional problems in rheolef. It is planed to be extended to
tridimensional problems.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
64 Rheolef version 5.94 update 27 May 2011
field phih = interpolate (Vh, phi(0));
cerr << "# # t\terror" << endl;
geomap X (Vh, -delta_t*uh);
branch event ("t","phi");
cout << event (0, phih);
for (size_t n = 1; n <= n_max; n++) {
Float t = Float(n)*delta_t;
field prec_phih = compose (phih, X);
phih.u = c_fact.solve (m.uu*prec_phih.u + m.ub*prec_phih.b - c.ub*phih.b);
field e = phih - interpolate (Vh, phi(t));
cerr << "# " << t << "\t" << sqrt(m(e,e)) << endl;
cout << event (t, phih);
}
}
Comments
We take =]
1
2
,
1
2
[
2
and T = . This problem provides an example for a convection-diusion
equation with a variable velocity eld u(x
0
, x
1
) = (x
1
, x
0
) and a known analytical solution:
(x, t) =
+ 4t
exp
_
(x
0
cos(t) +x
1
sin(t) x
c,0
)
2
+ (x
0
sin(t) +x
1
cos(t) x
c,1
)
2
+ 4t
_
where > 0 is the slope and x
c
is the center of the hill.
Notice the use of a class-function implementation phi for the implementation of (t) as a function
of x. Such programation style has been introduced in the Standard Template Library [19], which
is a part of the standard C++ library. By this way, for a given t, (t) can be interpolated as an
usual function on a mesh.
The geomap variable X implements the localizer X
n
(x) and the compose function is used to perform
the composition
h
X
n
.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 65
How to run the program
Figure 5.2: Animation of the solution of the rotating hill problem.
We assume that the previous code is contained in the le convect.cc. Then, compile the program
as usual (see page 12):
make convect
and enter the comands:
mkgeo_grid -t 50 -boundary -a -0.5 -b 0.5 -c -0.5 -d 0.5 > omega-50.geo
./convect omega-50.geo > omega-50.branch
The visualization and animation are similar to those of the head problem previously presented in
paragraph 5.1:
branch -paraview omega-50
paraview&
The result is shown on Fig. 5.2.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
66 Rheolef version 5.94 update 27 May 2011
5.3 The Navier-Stokes problem
Formulation
This longer example combines most fonctionalities presented in the previous examples.
Let us consider the Navier-Stokes problem for the driven cavity in =]0, 1[
N
, N = 2, 3. Let
Re > 0 be the Reynolds number, and T > 0 a nal time. The problem writes:
(NS): nd u = (u
0
, . . . , u
N1
) and p dened in ]0, T[ such that:
Re
_
u
t
+u.u
_
div(2D(u)) + p = 0 in ]0, T[,
div u = 0 in ]0, T[,
u(t =0) = 0 in 0, T,
u = (1, 0) on
top
]0, T[,
u = 0 on (
left
right
bottom
)]0, T[,
u
0
n
=
u
1
n
= u
2
= 0 on (
back
front
)]0, T[ when N = 3,
where D(u) = (u + u
T
)/2. This nonlinear problem is the natural extension of the linear
Stokes problem, as presented in paragraph 5.3, page 66. The boundaries are represented on
Fig. 3.1, page 31.
Time approximation
Let t > 0. Let us consider the following backward second order scheme, for all C
2
([0, T]) :
d
dt
(t) =
3(t) 4(t t) +(t 2t)
2t
+O(t
2
)
The problem is approximated by the following second-order implicit Euler scheme:
Re
3u
n+1
4u
n
X
n
+u
n1
X
n1
2t
div(2D(u
n+1
)) + p
n+1
= 0 in ,
div u
n+1
= 0 in ,
u
n+1
= (1, 0) on
top
,
u
n+1
= 0 on
left
right
bottom
,
u
n+1
0
n
=
u
n+1
1
n
= u
n+1
2
= 0 on
back
front
when N = 3,
where, following [20, 21]:
X
n
(x) = x t u
(x)
X
n1
(x) = x 2t u
(x)
u
= 2u
n
u
n1
It is a second order extension of the method previously introduced in paragraph 5.2 page 63. The
scheme denes a second order recurence for the sequence (u
n
)
n1
, that starts with u
1
= u
0
= 0.
Variationnal formulation
The variationnal formulation of this problem expresses:
(NS)
t
: nd u
n+1
V(1) and p
n+1
L
2
0
() such that:
a(u
n+1
, v) + b(v, p
n+1
) = m(f
n
, v), v V(0),
b(u
n+1
, q) = 0, q L
2
0
(),
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 67
where
f
n
=
Re
2t
_
4 u
n
X
n
u
n1
X
n
_
where
a(u, v) =
3Re
2t
_
u.v dx +
_
2D(u) : D(v) dx
and b(., .) and V() was already introduced in paragraph 3.4, page 42, while studying the Stokes
problem.
Space approximation
The Taylor-Hood [12] nite element approximation of this generalised Stokes problem was also
considered in paragraph 3.4, page 42. We introduce a mesh T
h
of and the nite dimensional
spaces X
h
, V
h
() and Q
h
. The approximate problem writes:
(NS)
t,h
: nd u
n+1
h
V
h
(1) and p
n+1
Q
h
such that:
a(u
n+1
h
, v) + b(v, p
n+1
h
) = m(f
n
h
, v), v V
h
(0),
b(u
n+1
h
, q) = 0, q Q
h
.
(5.1)
where
f
n
h
=
Re
2t
_
4 u
n
h
X
n
u
n1
h
X
n
_
The problem reduces to a sequence resolution of a generalized Stokes problems.
File navier-stokes-solve.h
// second order scheme (experimental)
#include "rheolef/mixed_solver.h"
#include "cahouet-chabart.h"
using namespace std;
int navier_stokes_solve (Float Re, Float delta_t, field f0h, field& uh, field& ph,
size_t& max_iter, Float& tol, std::ostream *p_cerr=0, std::string label = "navier-stokes") {
const space& Vh = uh.get_space();
const space& Qh = ph.get_space();
form m (Vh, Vh, "mass");
form a (Vh, Vh, "2D_D");
a = a + 1.5*(Re/delta_t)*m;
ssk<Float> fact_a = ldlt(a.uu);
form b (Vh, Qh, "div"); b = -b;
cahouet_chabart preconditioner (Qh, 1.5*(Re/delta_t));
if (p_cerr != 0) *p_cerr << "[" << label << "] #n |du/dt|" << endl;
field uh1 = uh;
geomap_option_type opts;
opts.n_track_step = 10;
for (size_t n = 0; true; n++) {
field uh2 = uh1;
uh1 = uh;
field uh_star = 2.0*uh1 - uh2;
geomap X1 (Vh, -delta_t*uh_star, opts);
geomap X2 (Vh, -2.0*delta_t*uh_star, opts);
field fh = f0h + 0.5*(Re/delta_t)*(4.0*compose(uh1,X1) - compose(uh2,X2));
size_t gs_max_iter = 500;
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
68 Rheolef version 5.94 update 27 May 2011
Float gs_tol = 1e-15;
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, (m*fh).u - a.ub*uh.b, -(b.ub*uh.b),
preconditioner, fact_a, gs_max_iter, gs_tol);
if (status != 0) warning_macro ("solve generalized stokes: precision not reached: tol = " << tol)
field duh_dt = (3*uh - 4*uh1 + uh2)/(2*delta_t);
Float residual = sqrt(m(duh_dt,duh_dt));
if (p_cerr != 0) *p_cerr << "[" << label << "] "<< n << " " << residual << endl;
if (residual < tol) {
tol = residual;
max_iter = n;
return 0;
}
if (n == max_iter-1) {
tol = residual;
return 1;
}
}
}
Comments
The navier stokes solve function is similar to the stokes-cavity.cc. It solves here a gener-
alised Stokes problem and manages a right-hand side f
h
:
field uh_star = 2.0*uh1 - uh2;
geomap X1 (Vh, -delta_t*uh_star);
geomap X2 (Vh, -2.0*delta_t*uh_star);
field fh = f0h + 0.5*(Re/delta_t)*(4.0*compose(uh1,X1) - compose(uh2,X2));
This last computation is similar to those done in the convect.cc example. The generalized
Stokes problem is solved by the conjugate gradient algorithm :
int status = pcg_abtb (a.uu, b.uu, uh.u, ph.u, (m*fh).u - a.ub*uh.b, -(b.ub*uh.b),
preconditioner, fact_a, gs_max_iter, gs_tol, &cerr);
The preconditioner is here the Cahouet and Chabart one [22]. As showed in [23], the number of
iterations need by the conjugate gradient algorithm to reach a given precision is then independent
of the mesh size. This preconditioner leads to the resolution of the follwoing subproblem:
(M
1
h
+A
1
h
)q
h
= r
h
where = Re/t, r
h
Q
h
is given and M and A are respectively the the mass matrix and the
discrete Poisson operator with Neumann boundary condition. The resolution of this subproblem
hqs been previously developed in section 2.4, page 24.
File cahouet-chabart.h
#include "neumann-laplace-assembly.h"
struct cahouet_chabart {
cahouet_chabart (const space& Qh, Float lambda_1)
: fact_m(), fact_c(), lambda(lambda_1) {
form m (Qh, Qh, "mass");
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 69
fact_m = ldlt(m.uu);
form a (Qh, Qh, "grad_grad");
form_diag dm (Qh, "mass");
field mh (dm);
csr<Float> c = neumann_laplace_assembly (a.uu, mh.u);
fact_c = ldlt(c);
}
vec<Float> solve (const vec<Float>& Mp) const {
vec<Float> q1 = fact_m.solve(Mp);
vec<Float> Mp_e (Mp.size()+1);
for (size_t i = 0; i < Mp.size(); i++) Mp_e.at(i) = Mp.at(i);
Mp_e.at(Mp.size()) = 0;
vec<Float> q2_e = fact_c.solve(Mp_e);
vec<Float> q2 (Mp.size());
for (size_t i = 0; i < q2.size(); i++) q2.at(i) = q2_e.at(i);
vec<Float> q = q1 + lambda*q2;
return q;
}
ssk<Float> fact_m;
ssk<Float> fact_c;
Float lambda;
};
File navier-stokes-cavity.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
#include "navier-stokes-solve.h"
#include "cavity.h"
field criteria (Float Re, const field& uh) {
space Xh (uh.get_geo(), "P1d");
form inv_m (Xh, Xh, "inv_mass");
form mpt (Xh, uh.get_space()[0], "mass");
form p = inv_m * trans(mpt);
field c1 = sqr(p*uh[0]) + sqr(p*uh[1]);
space Th (uh.get_geo(), "P1d", "tensor");
form two_D (uh.get_space(), Th, "2D");
form inv_mt (Th, Th, "inv_mass");
field two_Duh = inv_mt*(two_D*uh);
field c2 = sqr(field(two_Duh(0,0))) + sqr(field(two_Duh(1,1))) + 2*sqr(field(two_Duh(0,1)));
return sqrt(Re*c1 + c2);
}
int main (int argc, char**argv) {
if (argc < 2) {
cerr << "usage: " << argv[0] << " <geo> <Re> <Delta t> <n_adapt> <hcoef> <hmin>" << endl;
exit (1);
}
geo omega_h (argv[1]);
Float Re = (argc > 2) ? atof(argv[2]) : 100;
Float delta_t = (argc > 3) ? atof(argv[3]) : 0.05;
size_t n_adapt = (argc > 4) ? atoi(argv[4]) : 3;
adapt_option_type options;
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
70 Rheolef version 5.94 update 27 May 2011
options.hcoef = (argc > 5) ? atof(argv[5]) : 2;
options.hmin = (argc > 6) ? atof(argv[6]) : 0.004; // 0.004
options.hmax = 0.1;
space Vh = cavity_space (omega_h, "P2");
space Qh (omega_h, "P1");
field uh = cavity_field (Vh, 1.0);
field ph (Qh, 0.0);
field fh (Vh, 0.0);
for (size_t i = 0; true; i++) {
size_t max_iter = 500;
Float tol = 1e-10;
navier_stokes_solve (Re, delta_t, fh, uh, ph, max_iter, tol, &cerr);
orheostream o (omega_h.name(), "mfield");
o << setprecision(numeric_limits<Float>::digits10)
<< catchmark("Re") << Re << endl
<< catchmark("delta_t") << delta_t << endl
<< catchmark("u") << uh
<< catchmark("p") << ph;
if (i >= n_adapt) break;
field ch = criteria(Re,uh);
omega_h = geo_adapt(ch, options);
omega_h.save();
Vh = cavity_space (omega_h, "P2");
Qh = space (omega_h, "P1");
uh = cavity_field (Vh, 1.0);
ph = field (Qh, 0.0);
fh = field (Vh, 0);
}
}
Comments
The code performs a computation by using adaptive mesh renement, in order to capture recircu-
lation zones. The adapt option type declaration is used by rheolef to send options to the mesh
generator. The code reuse the le cavity.h introduced page 42. This le contains two functions
that denes boundary conditions associated to the cavity driven problem.
The criteria function computes the adaptive mesh renement criteria:
c
h
= (Re[u
h
[
2
+ 2[D(u
h
)[
2
)
1/2
The criteria function is similar to those presented in the embankment-adapt-2d.cc example.
The main function enters in a loop, that solves at each iteration a generalised Stokes problem,
then recompute the mesh and nally re-interpolates the solution u on the new mesh:
uh = interpolate (Vh, uh);
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 71
How to run the program
Re = 100: 2481 vertices
max
= 1.27 10
4
,
min
= 0.10
Re = 400: 3001 vertices
max
= 6.4 10
4
,
min
= 0.11
Figure 5.3: Meshes and stream functions associated to the solution of the Navier-Stokes equations
for Re = 100 (top) and Re = 400 (bottom).
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
72 Rheolef version 5.94 update 27 May 2011
Re = 1000: 3544 vertices
max
= 1.72 10
3
,
min
= 0.12
Figure 5.4: Meshes and stream functions associated to the solution of the Navier-Stokes equations
for Re = 1000.
First, creates an initial mesh:
mkgeo_grid -t 10 > square.geo
Then, compile and run the Navier-Stokes solver for the driven cavity for Re = 100:
make navier-stokes-cavity
./navier-stokes-cavity square.geo 100
The program performs a computation with Re = 100. By default the time step is t = 0.05
and the computation loops for three mesh adaptations. At each time step, the program prints
an approximation of the time derivative, and stops when a stationary solution is reached. Then,
extract and visualise the adapted mesh and the solution:
geo square-100-3.geo
mfield square-3.mfield.gz -u -velocity -vscale 10
The representation of the stream function writes:
zcat square-3.mfield.gz | ./vorticity | ./streamf-cavity | \
field -n-iso 15 n-iso-negative 10 -nofill -
The programs vorticity and streamf-cavity has been introduced pages 46 and 48, respectively.
The last options of the field program draws isocontours of the stream function using lines, as
shown on Fig. 5.3. The zero isovalue separates the main ow from recirculations located in corners
at the bottom of the cavity. For Re = 400 and 1000 the computation writes:
./navier-stokes-cavity square.geo 400
./navier-stokes-cavity square.geo 1000
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 73
Comparison: Re = 1000
Re = 1000
Comparison: Re = 400
Re = 400
Comparison: Re = 100
Re = 100
x
1
u
0
(0.5, x
1
)
1 0.5 0 -0.5
1
0.5
0
Re = 1000, comparsion
Re = 1000
Re = 400, comparison
Re = 400
Re = 100, comparison
Re = 100
u
1
(x
0
, 0.5)
x
0
1 0.5 0
0.5
0
-0.5
Figure 5.5: Navier-Stokes: velocity proles along lines passing throught the center of the cavity,
compared with data from [24]: (a) u
0
along the vertical line; (b) u
1
along the horizontal line line.
The visualization of the cut of the horizontal velocity along the vertical median line writes:
mfield square-3.mfield.gz -u0 | field -cut -normal -1 0 -origin 0.5 0 -gnuplot -
mfield square-3.mfield.gz -u1 | field -cut -normal 0 1 -origin 0 0.5 -gnuplot -
Fig. 5.5 compare the cuts with data from [24], table 1 and 2 (see also [25]). Observe that the
solution is in good agreement with these previous computations.
Re x
c
y
c
min
100 present 0.617 0.736 0.104
Labeur and Wells [26] 0.608 0.737 0.104
Donea and Huerta [27] 0.62 0.74 0.103
400 present 0.552 0.602 0.117
Labeur and Wells [26] 0.557 0.611 0.115
Donea and Huerta [27] 0.568 0.606 0.110
1000 present 0.532 0.569 0.121
Labeur and Wells [26] 0.524 0.560 0.121
Donea and Huerta [27] 0.540 0.573 0.110
Figure 5.6: Cavity ow: primary vortex position and stream function value.
Finaly, table 5.6 compares the primary vortex position and its associated stream function value.
Notice also the good agreement with previous simulations. The small program that computes
the primary vortex is showed below. Notice that degrees of freedom for the P
2
approximation of
the stream function are listed with rst the degrees of freedom associated to vertices and then
thoses located at the middle of edges. When the extremum of the stream function is located on
an edge, the program build rst the edges, and then compute the middle-point associated to the
edge degree of freedom.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
74 Rheolef version 5.94 update 27 May 2011
File vortex-position.cc
#include "rheolef.h"
#include "rheolef/geo-connectivity.h"
using namespace rheolef;
using namespace std;
int main (int argc, char** argv) {
field psi_h; cin >> psi_h; psi_h *= -1;
Float psi_max = 0;
size_t i_dof_max = 0;
for (size_t i_dof = 0; i_dof < psi_h.size(); i_dof++) {
if (psi_h.at(i_dof) <= psi_max) continue;
psi_max = psi_h.at(i_dof);
i_dof_max = i_dof;
}
point x_max;
const geo& omega = psi_h.get_geo();
if (i_dof_max < omega.n_vertex()) {
x_max = omega.vertex(i_dof_max);
} else {
vector<pair<size_t,size_t> > edge;
build_edge (omega, edge);
size_t i_edge = i_dof_max - omega.n_vertex();
point a = omega.vertex (edge[i_edge].first);
point b = omega.vertex (edge[i_edge].second);
x_max = (a+b)/2;
}
cout << "xc\t\tyc\t\tpsi" << endl
<< x_max[0] << "\t" << x_max[1] << "\t" << psi_max << endl;
}
For higher Reynolds number, Shen [28] showed in 1991 that the ow converges to a stationary
state for Reynolds numbers up to 10000; for Reynolds numbers larger than a critical value 10 000 <
Re
1
< 10 500 and less than another critical value 15 000 < Re
2
< 16 000, these authors founded
that the ow becomes periodic in time which indicates a Hopf bifurcation; the ow loses time
periodicity for Re Re
2
. In 2002, Auteri et al. [29] estimated the critical value for the apparition
of the rst instability to Re
1
8018. In 2005, Erturk et al. [30] computed steady driven cavity
solutions up to Re 21 000. Also in 2005, this result was inrmed by [31]: these authors estimated
Re
1
close to 8000, in agreement with [29]. The 3D driven cavity has been investigated in [32] by
the method of characteristic. In conclusion, the exploration of the driven cavity at large Reynolds
number is a fundamental challenge in computational uid dynamics.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Part III
Advanced and highly nonlinear
problems
75
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 6
Equation dened on a surface
This chapter deals with equations dened on a surface.
Let be a closed map in R
d
, i.e. a closed surface when d = 3 or a closed curve when d = 2. We
consider the following problem:
nd u, dened on , such that
u
s
u = f
where
s
is the Laplace-Beltrami operator and f is a given function, dened on . Remark that
this is a Helmholtz problem on and, since is closed, we have = and there is no boundary
conditions.
In this chapter, we describe three approaches for the resolution of this problem. The rst one
bases on an explict discretization of and is suitable when is not too complex. The second
one suppose that is expressed implicitly by an equation (x) = 0, where , called the level
set function, is dened on a bounded domain R
d
containing . The second approach build
explicitly an intersection mesh of , but there is not theoretical proof that it is convergent. Finally,
the third approach is a variant of the second one, with rigorous convergence proof.
6.1 Formulation on an explicit surface mesh
TODO
File helmholtz s sphere.cc
#include "rheolef.h"
#include "sphere.h"
using namespace std;
using namespace rheolef;
int main (int argc, char**argv) {
geo omega (argv[1]);
size_t d = omega.dimension();
space Xh (omega, "P1");
form m(Xh, Xh, "mass");
form a(Xh, Xh, "grad_grad");
a = a + m;
ssk<Float> fact_a = ldlt(a.uu);
field fh = interpolate(Xh, f(d));
field uh(Xh);
77
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
78 Rheolef version 5.94 update 27 May 2011
uh.u = fact_a.solve((m*fh).u);
cout << setprecision(numeric_limits<Float>::digits10)
<< uh;
}
Comments
TODO
How to run the program ?
Figure 6.1: Explicit surface surface method: (left) for a circle: (right) for a sphere; (top) mesh;
(bottom) solution.
2D
gmsh -1 circle-gmsh.mshcad
msh2geo circle-gmsh.msh > circle-gmsh.geo
geo circle-gmsh.geo -gnuplot
make helmholtz_s_sphere
./helmholtz_s_sphere circle-gmsh.geo | field -
3D
gmsh -2 sphere-gmsh.mshcad
msh2geo sphere-gmsh.msh > sphere-gmsh.geo
geo sphere-gmsh.geo
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 79
./helmholtz_s_sphere sphere-gmsh.geo | field -
6.2 Building a surface mesh from level set
Figure 6.2: Building a surface mesh from level set: (left) for a circle: (right) for a sphere.
2D
mkgeo_grid -t 20 -a -2 -b 2 -c -2 -d 2 > square2.geo
make isovalue_sphere geo_min_aera
./isovalue_sphere square2.geo > square2-iso.geo
geo square2-iso-iso.geo -gnuplot
./helmholtz_s_sphere square2-iso.geo | field -
./geo_min_aera square2-iso.geo
3D
mkgeo_grid -T 20 -a -2 -b 2 -c -2 -d 2 -f -2 -g 2 > cube2.geo
./isovalue_sphere cube2.geo > cube2-iso.geo
geo cube2-iso.geo
./helmholtz_s_sphere cube2-iso.geo
./geo_min_aera cube2-iso.geo
File isovalue sphere.cc
#include "rheolef.h"
#include "sphere.h"
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
80 Rheolef version 5.94 update 27 May 2011
using namespace rheolef;
using namespace std;
int main (int argc, char**argv) {
geo Lambda (argv[1]);
space Vh (Lambda, "P1");
field phi_h = interpolate(Vh, phi);
geo Gamma = zero_level_set (phi_h);
cout << setprecision(16) << Gamma;
}
File geo min aera.cc
#include "rheolef.h"
#include "sphere.h"
using namespace rheolef;
using namespace std;
int main (int argc, char**argv) {
geo gamma (argv[1]);
space Vh (gamma, "P0");
form_diag m (Vh, "mass");
Float min_aera = field(m).min();
Float min_abs_aera = abs(field(m)).min();
cout << setprecision(16)
<< "min aera = " << min_aera << endl
<< "min abs aera = " << min_abs_aera << endl;
return (min_aera > 1e-8) ? 0 : 1;
}
6.3 Formulation based on the banded level-set method
TODO
File helmholtz band sphere.cc
#include "rheolef.h"
#include "helmholtz_band_assembly.h"
#include "sphere.h"
using namespace std;
using namespace rheolef;
int main (int argc, char**argv) {
geo Lambda (argv[1]);
size_t d = Lambda.dimension();
space Vh (Lambda, "P1");
field phi_h_lambda = interpolate(Vh, phi);
geo band = banded_level_set (phi_h_lambda);
space Bh (band, "P1");
Bh.block("isolated");
field phi_h = interpolate(Bh, phi);
form m (Bh, Bh, "mass_s", phi_h);
form a (Bh, Bh, "grad_grad_s", phi_h);
a = m+a;
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 81
field fh = interpolate(Bh, f(d));
field lh = m*fh;
csr<Float> A = band_assembly<Float> (a, phi_h);
vec<Float> L(A.nrow(), 0.0);
for (size_t i = 0; i < lh.u.size(); i++) L.at(i) = lh.u.at(i);
for (size_t i = lh.u.size(); i < L.size(); i++) L.at(i) = 0;
vec<Float> U (L.size());
ssk<Float> fact_A = ldlt(A);
U = fact_A.solve(L);
field uh(Bh);
uh.b = 0;
for (size_t i = 0; i < uh.u.size(); i++) uh.u.at(i) = U.at(i);
Bh.get_geo().save();
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("u") << uh
<< catchmark("phi") << phi_h;
}
File helmholtz band assembly.h
namespace rheolef {
template <class T>
csr<T>
band_assembly (const form& a, const field& phi_h) {
const space& Bh = a.get_first_space();
check_macro (Bh == phi_h.get_space(), "incompatible spaces");
const geo& band = Bh.get_geo();
size_t n = a.uu.nrow();
size_t m = band.n_domain() - 2; // n_connected components: skip zero & isolated
asr<T> A (n+m, n+m);
Array<size_t>::const_iterator ia = a.uu.ia().begin();
Array<size_t>::const_iterator ja = a.uu.ja().begin();
typename Array<T>::const_iterator va = a.uu.a().begin();
for (size_t i = 0; i < n; i++) {
for (size_t p = ia[i]; p < ia[i+1]; p++) {
A.entry(i,ja[p]) = va[p];
}
}
typename Array<T>::const_iterator vb = phi_h.u.begin();
for (size_t i = 0; i < m; i++) {
const domain& ci = band["connected_component_"+itos(i)];
for (size_t p = 0; p < ci.size(); p++) {
const geo_element& P = ci.at(p);
size_t jdx = P[0];
size_t j = Bh.index(jdx);
A.entry(n+i, j) = vb[j];
A.entry(j, n+i) = vb[j];
}
}
return csr<T>(A);
}
} // namespace rheolef
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
82 Rheolef version 5.94 update 27 May 2011
How to run the program ?
Figure 6.3: Banded level set method: (left) for a circle: (right) for a sphere (n = 10).
2D
mkgeo_grid -t 41 -a -2 -b 2 -c -2 -d 2 > square2.geo
make helmholtz_band_sphere
./helmholtz_band_sphere square2 | field -
3D
mkgeo_grid -T 20 -a -2 -b 2 -c -2 -d 2 -f -2 -g 2 > cube2.geo
./helmholtz_band_sphere cube2 | field -
TODO: add a gure with a cut the solution on the intersection surface mesh, for a clean visual-
ization
TODO: also comment the dirichlet s torus.cc example
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 7
Multi-regions and non-constant
coecients problems
This chapter is related to the so-called transmission problem.
Formulation
Let us consider the diusion problem with a non-constant diusion coecient in a domain
bounded R
N
, N = 1, 2, 3:
(P): nd u dened in such that:
div(u) = 1 in
u = 0 on
left
right
u
n
= 0 on
top
bottom
when N 2
u
n
= 0 on
front
back
when N = 3
We consider here very important special case when is picewise constant. Let:
(x) =
_
when x
west
1 when x
east
where (
west
,
east
) is a partition of . This problem is known as the transmission problem,
since the solution and the ux are continuous on the interface:
u
west
= u
east
on
0
u
/west
n
=
u
east
n
on
0
where
0
=
west
east
It expresses the transmission of the quantity u and its ux accross the interface
0
between two
regions that have dierent diusion properties: We go back to the standard problem when = 1.
The variational formulation of this problem expresses:
(V F): nd u V such that:
a(u, v) = m(1, v), v V
83
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
84 Rheolef version 5.94 update 27 May 2011
where the bilinear forms a(., .) and m(., .) are dened by
a(u, v) =
_
u.v dx, u, v H
1
()
m(u, v) =
_
uv dx, u, v L
2
()
V = v H
1
(); v = 0 on
left
right
The bilinear form a(., .) denes a scalar product in V and is related to the energy form. This form
is associated to the div operator. The m(., .) is here the classical scalar product on L
2
(),
and is related to the mass form as usual.
The approximation of this problem could performed by a standard Lagrange P
k
continuous ap-
proximation. We switch here to a mixed formulation that implements easily.
Mixed Formulation
Let us introduce the following vector-valued eld:
p = u
The problem can be rewritten as:
(M): nd p and u dened in such that:
1
p u = 0 in
div p = 1 in
u = 0 on
left
right
u
n
= 0 on
top
bottom
when N 2
u
n
= 0 on
front
back
when N = 3
The variationnal formulation writes:
(MV F): nd p (L
2
())
N
and u V such that:
a(p, q) +
b(q, u) = 0, q (L
2
())
N
p.qdx,
b(q, v) =
_
v.qdx, u, v L
2
()
This problem appears as more complex than the previous one, since there is two unknown instead
of one. Nevertheless, the p unknown could be eliminated by inverting the operator associated to
the a form. This can be done easily since p is approximated by discontinuous piecewise polynomial
functions.
Mixed approximation
Let k 1 and
Q
h
= q (L
2
())
N
; q
/K
(P
k1
)
N
V
h
= v V ; v
/K
P
k
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 85
The mixed nite element approximation writes:
(MV F)
h
: nd p
h
Q
h
and u
h
V
h
such that:
a(p
h
, q) +
b(q, u
h
) = 0, q Q
h
b(p
h
, v) = m(1, v), v V
h
Here the operator associated to a is block-diagonal and can be inverted at the element level. The
problem reduced to a standard linear system, and solved by a direct method.
File transmission.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
const Float epsilon = 0.01;
int main(int argc, char**argv) {
geo omega (argv[1]);
space Vh (omega, "P1");
if (omega.dimension() <= 2) {
Vh.block ("left"); Vh.block ("right");
} else {
Vh.block ("back"); Vh.block ("front");
}
field uh (Vh);
if (omega.dimension() <= 2)
uh["left"] = uh["right"] = 0;
else
uh["back"] = uh["front"] = 0;
field fh (Vh, 1);
space Qh (omega, "P0", "vector");
field eta (Qh);
eta ["east"] = 1;
eta ["west"] = epsilon;
form_diag d (eta);
form grad (Vh, Qh, "grad");
form m (Vh, Vh, "mass");
form inv_m (Qh, Qh, "inv_mass");
form a = trans(grad)*(inv_m*d)*grad;
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("epsilon") << epsilon << endl
<< catchmark("u") << uh;
return 0;
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
86 Rheolef version 5.94 update 27 May 2011
Comments
The code begins as usual. In the second part, the space Q
h
is dened. For convenience, the
diusion coecient is introduced diagonal diusion tensor operator d, i.e. for N = 2:
d =
_
0
0
_
It is rst dened as an element of Q
h
i.e. a vector-valued eld:
field eta (Qh);
and then converted to a diagonal form representing the diusion tensor operator:
form_diag d (eta);
Notice that, by this way, an anisotropic diusion operator could be considered. The statement
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("epsilon") << epsilon << endl
<< catchmark("u") << uh;
writes and u
h
in a labeled format, suitable for post-traitement, visualization and error analysis.
The number of digits printed depends upon the Float type. When Float is a double, this is
roughly 15 disgits; this number depends upon the machine precision. Here, we choose to print all
the relevant digits: this choice is suitable for the following error analysis.
How to run the program ?
Build the program as usual: make transmission. Then, creates a one-dimensional geometry with
two regions:
mkgeo_grid -e 100 -region > line-region.geo
geo -gnuplot line-region.geo
The trivial mesh generator mkgeo grid, denes two regions east and west when used with the
-region option. This correspond to the situation:
= [0, 1]
N
,
west
= x
0
< 1/2 and
east
= x
0
> 1/2.
In order to avoid mistakes with the C++ style indexation, we denote by (x
0
, . . . , x
N1
) the cartesian
coordinate system in R
N
.
Finaly, run the program and look at the solution:
./transmission line-region.geo > transmission1d.mfield
mfield transmission1d.mfield -u | field -
The two dimensionnal case corresponds to the commands:
mkgeo_grid -t 10 -region > square-region.geo
geo square-region.geo
./transmission square-region.geo > transmission2d.mfield
mfield transmission2d.mfield -u | field -
while the tridimensional to
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 87
mkgeo_grid -T 10 -region > cube-region.geo
./transmission cube-region.geo > transmission3d.mfield
mfield transmission3d.mfield -u | field -mayavi -
This problem is convenient, since the exact solution is known and is piecewise second order poly-
nomial:
u(x) =
_
_
x
0
2
_
1 + 3
2(1 +)
x
0
_
when x
0
< 1/2
1 x
0
2
_
x
0
+
1
2(1 +)
_
otherwise
Since the change in the diusion coecient value ts the element boundaries, obtain the exact
solution at the vertices of the mesh for the P
1
approximation, as shown on Fig. 7.1.
h=1/14
h=1/10
h=1/6
exact
1 0.75 0.5 0.25 0
3
2
1
0
Figure 7.1: Transmission problem: u
h
=
h
(u) ( = 10
2
, N = 1, P
1
approximation).
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
88 Rheolef version 5.94 update 27 May 2011
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 8
The p-laplacian problem
rheolef provides build-in classes for solving some linear and non-linear partial dierential equa-
tions, based on the nite element method (see e.g. [1]). All example les presented along
the present manual are available in the examples/ directory of the rheolef distribution, e.g.
/usr/local/share/doc/rheolef/examples/.
8.1 Problem statement
Let us consider the classical p-laplacian problem with homogeneous Dirichlet boundary conditions
in a domain bounded R
N
, N = 1, 2, 3:
(P): nd u, dened in such that:
div
_
[u[
p2
u
_
= f in
u = 0 on
where f is known and f = 1 in the computational examples. When p = 2, this problem reduces
to the linear Poisson problem with homogeneous Dichlet boundary conditions. Otherwise, for any
p > 1, the nonlinear problem is equivalent to the following minimisation problem:
(MP): nd u W
1,p
0
() such that:
u = arg min
vW
1,p
0
()
1
p
_
[v[
p
dx
_
f v dx,
where W
1,p
0
() denotes the usual Sobolev spaces of functions in W
1,p
() that vanishes on the
boundary [33, p. 118]. The variational formulation of this problem expresses:
(VF): nd u W
1,p
0
() such that:
a(u; u, v) = m(f, v), v W
1,p
0
()
where a(., .) and m(., .) are dened for any u
0
, u, v W
1,p
() by
a(u
0
; u, v) =
_
[u
0
[
p2
u.v dx, u, v W
1,p
0
()
m(u, v) =
_
uv dx, u, v L
2
()
The m(., .) is here the classical scalar product on L
2
(), and is related to the mass form. The
quantity a(u; u, u) = |u|
p,
induces a norm in W
1,p
0
, equivalent to the standard norm. The
form a(.; ., .) is bilinear with respect to the two last variable and is related to the energy form.
89
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
90 Rheolef version 5.94 update 27 May 2011
8.2 The xed-point algorithm
8.2.1 Principe of the algorithm
This nonlinear problem is then reduced to a sequence of linear subproblems by using the xed-point
algorithm. The sequence
_
u
(n)
_
n0
is dened by recurrence as:
n = 0: let u
(0)
W
1,p
0
() be known.
n 0: suppose that u
(n)
W
1,p
0
() is known and nd u
(n+1)
W
1,p
0
() such that:
a
_
u
(n)
; u
(n+1)
, v
_
= m(f, v), v W
1,p
0
()
Let u
(n+1)
= G
_
u
(n)
_
denotes the operator that solve the previous linear subproblem for a given
u
(n)
. Since the solution u satises u = G(u), it is a xed-point of G.
Let us introduce a mesh T
h
of and the nite dimensional space X
h
of continuous picewise poly-
nomial functions and V
h
, the subspace of X
h
containing elements that vanishes on the boundary
of :
X
h
= v
h
C
0
0
_
_
; v
h/K
P
k
, K T
h
V
h
= v
h
X
h
; v
h
= 0 on
where k = 1 or 2. The approximate problem expresses: suppose that u
(n)
h
V
h
is known and nd
u
(n+1)
h
V
h
such that:
a
_
u
(n)
h
; u
(n+1)
h
, v
h
_
= m(f, v
h
), v
h
V
h
By developping u
h
on a basis of V
h
, this problem reduces to a linear system. The implementation
with rheolef, involving weighted forms, is quite standard: the weight eld wh is inserted as the
last argument to the form constructor. The following code implement this problem in the rheolef
environment.
8.2.2 File p-laplacian-fixed-point.h
int p_laplacian_fixed_point (Float p, field fh, field& uh, Float& r, size_t& n) {
Float tol = r;
Float r0 = 0;
size_t max_iter = n;
const geo& omega_h = uh.get_geo();
const space& Vh = uh.get_space();
string grad_approx = (Vh.get_approx() == "P2") ? "P1d" : "P0";
space Th (omega_h, grad_approx, "vector");
form m (Vh, Vh, "mass");
form inv_mt (Th, Th, "inv_mass");
form grad (Vh, Th, "grad");
cerr << "# Fixed-point algorithm on p-laplancian: p = " << p << endl
<< "# n r v" << endl;
n = 0;
do {
field grad_uh = inv_mt*(grad*uh);
field wh = pow(sqr(grad_uh[0]) + sqr(grad_uh[1]), p/2.-1);
form a (Vh, Vh, "grad_grad", wh);
field rh = a*uh - m*fh;
r = rh.u.max_abs();
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 91
if (n == 0) r0 = r;
Float v = (n == 0) ? 0 : log10(r0/r)/n;
cerr << n << " " << r << " " << v << endl;
if (r <= tol || n++ >= max_iter) break;
ssk<Float> fact_a = ldlt(a.uu);
uh.u = fact_a.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
} while (true);
return r <= tol;
}
8.2.3 File p-laplacian-fixed-point.cc
#include "rheolef.h"
using namespace rheolef;
using namespace std;
#include "p-laplacian-fixed-point.h"
#include "poisson-dirichlet.icc"
int main(int argc, char**argv) {
geo omega_h (argv[1]);
string approx = (argc > 2) ? argv[2] : "P1";
Float p = (argc > 3) ? atof(argv[3]) : 2.5;
cerr << "# P-Laplacian problem by fixed-point:" << endl
<< "# geo = " << omega_h.name() << endl
<< "# approx = " << approx << endl
<< "# p = " << p << endl;
space Vh (omega_h, approx);
Vh.block (omega_h["boundary"]);
field uh (Vh);
uh ["boundary"] = 0;
field fh (Vh, 1);
poisson_dirichlet (fh, uh);
Float tol = 10*numeric_limits<Float>::epsilon();
size_t max_iter = 500;
int status = p_laplacian_fixed_point (p, fh, uh, tol, max_iter);
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("p") << p << endl
<< catchmark("u") << uh;
return status;
}
8.2.4 File poisson-dirichlet.icc
void poisson_dirichlet (field fh, field& uh) {
const space& Vh = uh.get_space();
form a (Vh, Vh, "grad_grad");
form m (Vh, Vh, "mass");
ssk<Float> fact = ldlt(a.uu);
uh.u = fact.solve (m.uu*fh.u + m.ub*fh.b - a.ub*uh.b);
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
92 Rheolef version 5.94 update 27 May 2011
8.2.5 Comments
The xed-point algorithm is amorced with u
(0)
as the solution of the linear problem associated to
p = 2, i.e. the standard Poisson problem with Dirichlet boundary conditions. The construction of
the weighted form a(.; ., .) writes:
field eta_h = pow(sqr(grad_uh[0]) + sqr(grad_uh[1]), p/2.-1);
form a (Vh, Vh, "grad_grad", eta_h);
8.2.6 Running the program
We assume that the previous code is contained in the le p-laplacian-fixed-point.cc. Com-
pile the program, as usual:
make p-laplacian-fixed-point
and enter the comands:
mkgeo_grid -t 10 -boundary > square.geo
geo square.geo
The triangular mesh has a boundary domain named boundary.
./p-laplacian-fixed-point square.geo P1 1.1 > square-P1.field
The previous command solves the problem for the corresponding mesh and writes the solution in
the le format .field.
0.010
0
1.010
8
2.010
8
3.010
8
4.010
8
5.010
8
6.010
8
6.910
8
Y
A
x
is
0
1
X
A
xis
0
1
u
h
(x
0
, x
1
)
s =
x
2
0
+ x
2
1
2
0
2
0.015
0.010
0.005
0
Figure 8.1: The p-laplacian for N = 2: (a) elevation view for p = 1.1; (b) cut along the rst
bissectice x
0
x
1
= 0.
Run the eld visualization:
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 93
field square-P1.field -elevation
field square-P1.field -cut -origin 0.5 0.5 -normal 1 1 -gnuplot
The rst command shows an elevation view of the solution (see 8.1.a) while the second one shows
a cut along the rst bissectrice x
0
= x
1
. (see 8.1.b).
8.2.7 Convergence properties of the xed-point algorithm
p = 1.50
p = 1.20
p = 1.10
r
h
1,h
n
500 0
10
0
10
5
10
10
10
15
p = 2.95
p = 2.90
p = 2.50
r
h
1,h
n
500 0
10
0
10
5
10
10
10
15
v
p
3 2 1
2
1
0
p > 2
p < 2
v
| p 2|
10
0
10
1
10
2
2
1
0
Figure 8.2: The xed-point algorithm on the p-laplacian for N = 2: (a) convergence when p < 2;
(b) when p > 2; (c) convergence rate versus p; (d) convergence rate versus p in semi-log scale.
The xed-point algorithm prints also at each iteration n, the residual term r
n
in discrete H
1
()
and the convergence rate v
n
= log10(r
n
/r
0
)/n. The residual term is dened by
r
(n)
h
= A
h
_
u
(n)
_
M
h
f
h
where A
h
and M
h
are the discrete operators induced by the forms a(.; ., .) and m(., .) on V
h
, and
dened for all u
h
, v
h
V h by:
A
h
(u
h
) v
T
h
= a(u
h
; u
h
, v
h
)
(M
h
u
h
) v
T
h
= m(u
h
, v
h
)
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
94 Rheolef version 5.94 update 27 May 2011
where the elements of V
h
are identied to elements of R
dim(V
h
)
. The W
1,p
() norm, dened for
all r W
1,p
() by duality:
|r|
1,p,
= sup
vW
1,p
()
v1,p,=1
m(r, v)
By analogy, the discrete W
1,p
() norm, denoted as |.|
1,h
, is dened by duality for all r
h
V
h
by:
|r
h
|
1,h
= sup
v
h
V
h
v
h
1,p,=1
m(r
h
, v
h
) = sup
xxdof(V
h
)
[r
h
(x)[
where xdof(V
h
) denotes the set of nodes associated to the V
h
degrees of freedom. Since elements
of V
h
vanishes on the boundary, the xdof(V
h
) contains all nodes associated to the degrees of
freedoms of X
h
except nodes located on the boudnary. Fig 8.2.a and 8.2.b show that the residual
term decreases exponentially versus n, since the slope of the plot in semi-log scale tends to be strait.
Thus, the convergence rate v
n
= log10(r
n
/r
0
)/n tends to a constant, denoted by v. Fig 8.2.c shows
the dependence of v: r
n
r
0
10
v n
. Observe that v tends to + when p = 2, since the system
becomes linear and the algorithm converge in one iteration. Observe also that v tends to zero in
p = 1 and p = 3. The singularity in p = 1 is not surprising, since the problem is dened only
when p > 1. Conversely, the singularity in p = 3 is not clear and requires more analysis. Fig 8.2.d
shows the same plot in semi-log scale and shows that v behaves as: v 2 log
10
([p 2[). Finally,
this study shows that the residual term behaves as:
r
n
2 [p 2[ r
0
10
n
8.3 The Newton algorithm
8.3.1 Principe of the algorithm
An alternative to the xed-point algorithm is to solve the nonlinear problem (P) by using the
Newton algorithm. Let us consider the following operator:
F : W
1,p
0
() W
1,p
()
u F(u) = div
_
[u[
p2
u
_
f
The F operator computes simply the residual term and the problem expresses now as: nd u
W
1,p
0
() such that F(u) = 0.
The Newton algorithm reduces the nonlinear problem into a sequence of linear subproblems: the
sequence
_
u
(n)
_
n0
is classically dened by recurrence as:
n = 0: let u
(0)
W
1,p
0
() be known.
n 0: suppose that u
(n)
is known, nd u
(n)
, dened in , such that:
F
_
u
(n)
_
u
(n)
= F
_
u
(n)
_
and then compute explicitely:
u
(n+1)
:= u
(n)
+u
(n)
The notation F
(u) u = r
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 95
After the computation of the Frechet derivative, we obtain the strong form of this problem:
(LT): nd u, dened in , such that
div
_
[u[
p2
(u) + (p 2)[u[
p4
u.(u) u
_
= r in
u = 0 on
This is a Poisson-like problem with homogeneous Dirichlet boundary conditions and a non-constant
tensorial coecient. The variational form of the linear tangent problem writes:
(V LT): nd u W
1,p
0
() such that
a
1
(u; u, v) = m(r, v), v W
1,p
0
()
where the a
1
(.; ., .) is dened for any u, u, v W
1,p
0
() by:
a
1
(u; u, v) =
_
_
[u[
p2
(u).(v) + (p 2)[u[
p4
u.(u) u.(v)
_
dx
For any R
N
let us denote by () the following N N matrix:
() = [[
p2
I + (p 2)[[
p4
where I stands for the N-order identity matrix. Then the a
1
expresses in a more compact form:
a
1
(u; u, v) =
_
((u)(u)) .(v) dx
Clearly a
1
is linear and symmetric with respect to the two last variables.
8.3.2 File p-laplacian-newton.cc
#include "rheolef.h"
#include "rheolef/newton.h"
using namespace rheolef;
using namespace std;
#include "p-laplacian.h"
int main(int argc, char**argv) {
geo omega_h (argv[1]);
string approx = (argc > 2) ? argv[2] : "P1";
Float p = (argc > 3) ? atof(argv[3]) : 2.5;
cerr << "# P-Laplacian problem by Newton:" << endl
<< "# geo = " << omega_h.name() << endl
<< "# approx = " << approx << endl
<< "# p = " << p << endl;
p_laplacian F (p, omega_h, approx);
field uh = F.initial();
Float tol = 1e6*numeric_limits<Float>::epsilon();
size_t max_iter = 500;
int status = newton (F, uh, tol, max_iter, &cerr);
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("p") << p << endl
<< catchmark("u") << uh;
return status;
}
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
96 Rheolef version 5.94 update 27 May 2011
8.3.3 File p-laplacian.h
class p_laplacian {
public:
typedef field value_type;
typedef Float float_type;
p_laplacian(Float p, const geo& omega_h, string approx = "P1");
void reset(const geo& omega_h, string approx = "previous");
field initial () const;
field residue (const field& uh) const;
void update_derivative (const field& uh) const;
field derivative_solve (const field& mrh) const;
field derivative_trans_mult (const field& mrh) const;
Float norm (const field& uh) const;
Float dual_norm (const field& Muh) const;
Float dot (const field& uh, const field& vh) const;
Float dual_dot (const field& Muh, const field& Mvh) const;
field criteria(const field& uh) const;
Float p;
space Vh, Kh;
field fh;
form m, inv_mt, grad;
ssk<Float> fact_m;
mutable form a1;
mutable ssk<Float> fact_a1;
};
#include "p-laplacian.icc"
8.3.4 File p-laplacian.icc
#include "poisson-dirichlet.icc"
p_laplacian::p_laplacian(Float p1, const geo& omega_h, string approx1)
: p(p1), Vh(), Kh(), fh(),
m(), inv_mt(), grad(), fact_m(), a1(), fact_a1() {
reset(omega_h, approx1);
}
void p_laplacian::reset(const geo& omega_h1, string approx1) {
if (approx1 == "previous") approx1 = Vh.get_approx();
Vh = space(omega_h1, approx1);
Vh.block (omega_h1["boundary"]);
fh = field(Vh, 1);
m = form (Vh, Vh, "mass");
fact_m = ldlt(m.uu);
string grad_approx = (Vh.get_approx() == "P2") ? "P1d" : "P0";
space Th (fh.get_geo(), grad_approx, "vector");
inv_mt = form (Th, Th, "inv_mass");
grad = form (Vh, Th, "grad");
Kh = space(fh.get_geo(), grad_approx, "tensor");
}
field p_laplacian::initial () const {
field uh(Vh);
uh [Vh.get_geo()["boundary"]] = 0;
poisson_dirichlet (fh, uh);
return uh;
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 97
}
void p_laplacian::update_derivative (const field& uh) const {
field grad_uh = inv_mt*(grad*uh);
field norm2_grad_uh = euclidian_norm2(grad_uh);
field w0h = pow(norm2_grad_uh, p/2)/norm2_grad_uh;
field w1h = pow(norm2_grad_uh, p/2)/sqr(norm2_grad_uh);
field eta_h (Kh);
eta_h(0,0) = w0h + (p-2)*w1h*sqr(grad_uh[0]);
if (uh.dimension() >= 2) {
eta_h(1,1) = w0h + (p-2)*w1h*sqr(grad_uh[1]);
eta_h(0,1) = (p-2)*w1h*grad_uh[0]*grad_uh[1];
}
if (uh.dimension() == 3) {
eta_h(2,2) = w0h + (p-2)*w1h*sqr(grad_uh[2]);
eta_h(1,2) = (p-2)*w1h*grad_uh[1]*grad_uh[2];
eta_h(0,2) = (p-2)*w1h*grad_uh[0]*grad_uh[2];
}
a1 = form (Vh, Vh, "grad_grad", eta_h);
fact_a1 = ldlt(a1.uu);
}
field p_laplacian::residue (const field& uh) const {
field grad_uh = inv_mt*(grad*uh);
field norm2_grad_uh = euclidian_norm2(grad_uh);
field w0h = pow(norm2_grad_uh, p/2-1);
form a (Vh, Vh, "grad_grad", w0h);
field mrh = a*uh - m*fh;
mrh.b = 0;
return mrh;
}
field p_laplacian::derivative_solve (const field& mrh) const {
field delta_uh (Vh,0);
delta_uh.u = fact_a1.solve(mrh.u);
delta_uh.b = 0;
return delta_uh;
}
field p_laplacian::derivative_trans_mult (const field& mrh) const {
field rh (Vh);
rh.u = fact_m.solve(mrh.u);
rh.b = 0;
field mgh;
mgh = a1*rh;
mgh.b = 0;
return mgh;
}
Float p_laplacian::dot (const field& uh, const field& vh) const {
return m(uh,vh);
}
Float p_laplacian::norm (const field& uh) const {
return sqrt(m(uh,uh));
}
Float p_laplacian::dual_dot (const field& mrh, const field& msh) const {
field sh (Vh);
sh.u = fact_m.solve(msh.u);
sh.b = 0;
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
98 Rheolef version 5.94 update 27 May 2011
return ::dot(mrh,sh);
}
Float p_laplacian::dual_norm (const field& mrh) const {
return sqrt(dual_dot(mrh,mrh));
}
field p_laplacian::criteria(const field& uh) const {
if (uh.get_approx() == "P1") return abs(uh);
field grad_uh = inv_mt*(grad*uh);
field norm2_grad_uh = euclidian_norm2(grad_uh);
return pow(norm2_grad_uh, p/4);
}
8.3.5 Comments
The code implements a generic Newton algorithm in the le newton.h. The main program
is p-laplacian-newton.cc, that uses a class p-laplacian. This class interface is denied in the
le p-laplacian.h and its implementation in p-laplacian.icc The residual term F(u
h
) is
computed by the member function residual while the resolution of F
(u
h
)u
h
= Mr
h
is performed
by the function derivative solve. The derivative F
(u
h
) is computed separately by the function
update derivative. Notice that the a
1
(u; ., .) bilinear form is a tensorial weighted form, where
(u) is the weight tensor. In rheolef, the tensorial weight eld eta h is inserted as an usual
scalar weight, by passing the weight parameter as the last argument to the form constructor. The
introduction of the class p-laplacian allows an easiest implementation of several variants of the
Newton algorithm.
8.3.6 Running the program
p = 1.7
p = 1.6
p = 1.5
r
h
1,h
n
25 0
10
0
10
5
10
10
10
15
p = 4.0
p = 3.5
p = 3.0
p = 2.5
r
h
1,h
n
25 0
10
0
10
5
10
10
10
15
Figure 8.3: The Newton algorithm on the p-laplacian for N = 2: (a) convergence when p < 2; (b)
when p > 2.
We assume that the previous code is contained in the le p-laplacian-newton.cc. As usual,
enter:
make p-laplacian-newton
mkgeo_grid -t 10 -boundary > square.geo
./p-laplacian-newton square.geo P1 1.5 | field -
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 99
The program prints at each iteration n, the residual term r
n
in discrete L
2
() norm. Fig. 8.3.a
and. 8.3.b shows that the residual terms tends very fast to zero. Observe that the slope is no more
constant in semi-log scale: the convergence rate accelerates and the slope tends to be vertical, the
so-called super-linear convergence. This is the major advantage of the Newton method. Also the
algorithm converge when p 3, until p = 4. It was not the case with the xed point algorithm
that diverges in that case. Finally, the Newton algorithm diverges for small values of p, e.g. p < 1.5
and the plot is not showed here. Conversely, when p > 4, the rst iterations increases dramatically
the residual terms, before to decrease. In that case, another strategy should be considered: the
damped Newton algorithm. This is the subject of the next section.
8.4 The damped Newton algorithm
8.4.1 Principe of the algorithm
The Newton algorithm diverges when the initial u
(0)
is too far from a solution. Our aim is to
modify the Newton algorithm and to obtain a globaly convergent algorith, i.e to converge to a
solution for any initial u
(0)
. The basic idea is to decrease the step length while maintaining the
direction of the original Newton algorithm:
u
(n+1)
:= u
(n)
+
n
u
(n)
where
(n)
]0, 1] and u
(n)
is the direction from the Newton algorithm, given by:
F
_
u
(n)
_
u
(n)
= F
_
u
(n)
_
Let V a Banach space and let T : V R dened for any v V by:
T(v) =
1
2
|C
1
F(v)|
2
V
,
where C is some non-singular operator, easy to invert, used as a non-linear preconditioner. The
simplest case, without preconditioner, is C = I. The T function furnishes a measure of the residual
term in L
2
norm. The convergence is global when for any initial u
(0)
, we have for any n 0:
T
_
u
(n+1)
_
T
_
u
(n)
_
+
_
T
_
u
(n)
_
, u
(n+1)
u
(n)
_
V
,V
(8.1)
where ., .
V
,V
is the duality product between V and its dual V
(u) = C
1
F
(u)
C
1
F(u)
where the superscript
denotes the adjoint operator, i.e. the transpose matrix the in nite
dimensional case. In practice we consider = 10
4
and we also use a minimal step length
min
= 1/10 in order to avoid too small steps. Let us consider a xed step n 0: for convenience
the n superscript is dropped in u
(n)
and u
(n)
. Let g : R R dened for any R by:
g() = T (u +u)
Then :
g
() = T
(u +u), u
V
,V
= C
1
F(u +u), F
(u +u)C
1
u
V,V
where the superscript
denotes the adjoint operator, i.e. the transpose matrix the in nite
dimensional case. The practical algorithm for obtaining was introduced rst in [34] and is also
presented in [35, p. 385]. The step length that satify (8.1) is computed by using a nite sequence
k
, k = 0, 1 . . . with a second order recurrence:
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
100 Rheolef version 5.94 update 27 May 2011
k = 0 : initialisation
0
= 1. If (8.1) is satied whith u +
0
d then let :=
0
and the
sequence stop here.
k = 1 : rst order recursion. The quantities g(0) = f(u) et g
(0) = f
(0)
2
+g
(0) +g(0)
After a short computation, we nd that the minimum of this polynom is:
1
=
g
(0)
2g(1) g(0) g
(0)
Since the initialisation at k = 0 does not satisfy (8.1), it is possible to show that, when is
small enougth, we have
1
1/2 and
1
1/2. Let
1
:= max(
min
,
1
). If (8.1) is satised
with u +
1
d then let :=
1
and the sequence stop here.
k 2 : second order recurrence. The quantities g(0) = f(u) et g
(0) =f
(u), d are
available, ytogether with
k1
, g(
k1
),
k2
and g(
k2
). Then, g() is approximed by
the following third order polynom:
g
k
() = a
3
+b
2
+g
(0) +g(0)
where a et b are expressed by:
_
a
b
_
=
1
k1
k2
_
_
_
_
1
2
k1
2
k2
k2
2
k1
k1
2
k2
_
_
_
_
_
g(
k1
) g
(0)
k1
g(0)
g(
k2
) g
(0)
k2
g(0)
_
The minimum of g
k
() is
k
=
b +
_
b
2
3ag
(0)
3a
Let
k
= min(1/2
k
, max(
k
/10,
k+1
) in order for
k
to be at the same order of magnitude
as
k1
. If (8.1) is satised with u +
k
d then let :=
k
and the sequence stop here.
The sequence (
k
)
k0
is strictly decreasing: when the stopping criteria is not satied until
k
reaches the machine precision
mach
then the algorithm stops with an error.
8.4.2 File p-laplacian-damped-newton.cc
#include "rheolef.h"
#include "rheolef/damped-newton.h"
using namespace rheolef;
using namespace std;
#include "p-laplacian.h"
int main(int argc, char**argv) {
geo omega_h (argv[1]);
string approx = (argc > 2) ? argv[2] : "P1";
Float p = (argc > 3) ? atof(argv[3]) : 2.5;
cerr << "# P-Laplacian problem by Newton:" << endl
<< "# geo = " << omega_h.name() << endl
<< "# approx = " << approx << endl
<< "# p = " << p << endl;
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 101
p_laplacian F (p, omega_h, approx);
field uh = F.initial();
Float tol = numeric_limits<Float>::epsilon();
size_t max_iter = 500;
int status = damped_newton (F, uh, tol, max_iter, &cerr);
cout << setprecision(numeric_limits<Float>::digits10)
<< catchmark("p") << p << endl
<< catchmark("u") << uh;
return status;
}
8.4.3 Comments
The le damped-newton-generic.h implements the damped Newton algorithm for a generic T(u)
function, i.e. a generic nonlinear preconditioner. This algorithms use a backtrack strategy imple-
mented in le newton-backtrack.h. The simplest choice of the identity preconditioner C = I
i.e. T(u) = |F(u)|
2
V
/2 is showed in le damped-newton.h. The gradient at = 0 is
T
(u) = F
(u)
F(u)
and the slope at = 0 is:
g
(0) = T
(u), u
V
,V
= F(u), F
(u)u
V
,V
= |F(u)|
2
V
The p-laplacian-damped-newton.cc is the application program to the p-Laplacian problem
together with the |.|
L
2
()
discrete norm for the function T.
8.4.4 Running the program
p = 1.5
p = 1.4
p = 1.3
p = 1.2
r
h
L
2
()
n
50 0
10
0
10
5
10
10
10
15
p = 10
p = 5
p = 4
p = 3
r
h
L
2
()
n
50 0
10
0
10
5
10
10
10
15
Figure 8.4: The damped Newton algorithm on the p-laplacian for N = 2: (a) convergence when
p < 2; (b) when p > 2.
We assume that the previous code is contained in the le p-laplacian-damped-newton.cc. As
usual, enter:
make p-laplacian-damped-newton
mkgeo_grid -t 10 -boundary > square.geo
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
102 Rheolef version 5.94 update 27 May 2011
./p-laplacian-damped-newton square.geo P1 1.5 | field -
./p-laplacian-damped-newton square.geo P1 5.0 | field -
The algorithm is now quite robust: the convergence occurs for a large range of p > 1 values. and
has been pushed until p = 100. The only limitation is due to machine roundo when p = 1.1: the
residual term reaches only 10
10
instead of 10
15
.
8.4.5 Robustness and mesh invariance
50 50
40 40
30 30
20 20
10 10
r
h
L
2
n
15 10 5 0
10
0
10
5
10
10
10
15
50 50
40 40
30 30
20 20
10 10
r
h
L
2
n
15 10 5 0
10
0
10
5
10
10
10
15
50 50
40 40
30 30
20 20
10 10
r
h
L
2
n
150 100 50 0
10
0
10
5
10
10
10
15
50 50
40 40
30 30
20 20
10 10
r
h
L
2
n
15 10 5 0
10
0
10
5
10
10
10
15
Figure 8.5: Convergence versus n for various meshes: (a) Newton algorithm when p = 1.7 and (b)
p = 1.6; (c) Newton algorithm when p = 1.5; (d) damped-Newton algorithm when p = 1.5.
Fig. 8.5.a, 8.5.b and. 8.5.c show the convergence of the Newton method when p = 1.7, 1.6 and
1.5, respectively. Observe that the convergence is asymptotically invariant of the mesh when the
element size decreases. The convergence is more dicult when p decreases to 1.5 and the Newton
algorithm is no more mesh invariant. Fig. 8.5.d shows the convergence of the damped Newton
method when p = 1.5 : the convergence is now very fast and also mesh-invariant.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Chapter 9
Error analysis and singularities
This chapter is in progess...
9.1 Error analysis
(Source le: doc/usrman/dirichlet-nh-error.cc)
9.1.1 Principe
Since the solution u is regular, the following error estimates holds:
|u u
h
|
0,2,
O(h
k+1
)
|u u
h
|
0,,
O(h
k+1
)
providing the approximate solution u
h
uses P
k
continuous nite element method, k 1. Here,
|.|
0,2,
and |.|
0,,
denotes as usual the L
2
() and L
() norms.
By denoting
h
the Lagrange interpolation operator, the triangular inequality leads to:
|u u
h
|
0,2,
|u
h
u|
0,2,
+|u
h
h
u|
0,2,
Since |u
h
u|
0,2,
O(h
k+1
), we have just to check the |u
h
h
u|
0,2,
error term.
9.1.2 Implementation
#include "rheolef/rheolef.h"
using namespace rheolef;
using namespace std;
Float u (const point& x) { return sin(x[0]+x[1]+x[2]); }
int main(int argc, char**argv)
{
field u_h;
cin >> u_h;
space Vh = u_h.get_space();
field pi_h_u = interpolate(Vh, u);
field eh = pi_h_u - u_h;
form m(Vh, Vh, "mass");
103
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
104 Rheolef version 5.94 update 27 May 2011
cout << "error_inf " << eh.max_abs() << endl;
cout << "error_l2 " << sqrt(m(eh,eh)) << endl;
return 0;
}
9.1.3 Running the program
Remarks on step (b) the use of the get space member function. Thus, the previous implementa-
tion does not depend upon the degree of the polynomial approximation.
After compilation, run the code by using the command:
dirichlet-nh square-h=0.1.geo P1 | dirichlet-nh-error
The two errors in L
and L
2
are printed for a h = 0.1 quasi-uniform mesh.
Let nelt denotes the number of elements in the mesh. Since the mesh is quasi-uniform, we have
h nelt
1
N
. Here N = 2 for our bidimensionnal mesh. The gure 9.1 plots in logarithmic scale
the error versus nelt
1
2
for both P
1
(on the left) and P
2
(on the right) approximations.
2=k+1
u
h
h
u
0,,
u
h
h
u
0,2,
nelt
1
2
P
1
element
10
1
10
2
10
3
10
2
10
3
10
4
10
5
10
6
3=k+1
u
h
h
u
0,,
u
h
h
u
0,2,
nelt
1
2
P
2
element
10
1
10
2
10
3
10
4
10
5
10
6
10
7
10
8
Figure 9.1: Error analysis in L
2
and L
norms.
9.2 Computing the Gradient
(Source le: doc/usrman/d-dx.cc)
Remark that the gradient q
h
= u
h
of a continuous picewise linear function u
h
is a discontinuous
piecewise constant function. Conversely, the gradient of a continuous picewise quadratic function
is a discontinuous piecewise linear function.
9.2.1 Formulation
For all i = 1 . . . N, the i-th component q
h,i
of the gradient belongs to the space:
T
h
h
L
2
();
h/K
P
k1
, K T
h
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 105
By introducing the two following bilinear forms:
m
T
(
h
,
h
) =
_
h
dx
b
i
(u
h
,
h
) =
_
u
h
x
i
h
dx
the gradient satises the following variational formulation:
m
T
(q
h,i
,
h
) = b
i
(u
h
,
h
),
h
Th
Remark that the matrix associated to the m
T
(., , ) bilinear form is block-diagonal. Each block is
associated to an element, and can be inverted at the element level. The following code uses this
property on step (e), as the "inv mass" form.
9.2.2 Implementation
#include "rheolef/rheolef.h"
using namespace rheolef;
using namespace std;
string get_approx_grad (const space &Vh)
{
string Pk = Vh.get_approx();
if (Pk == "P1") return "P0";
if (Pk == "P2") return "P1d";
cerr << "unexpected approximation " << Pk << endl;
exit (1);
}
int main(int argc, char**argv)
{
field uh;
cin >> uh;
space Vh = uh.get_space();
geo omega_h = Vh.get_geo();
string approx_grad = get_approx_grad(Vh);
space Th (omega_h, approx_grad);
form b(Vh, Th, "d_dx0");
form inv_mass (Th, Th, "inv_mass");
field du_dx0 = inv_mass*(b*uh);
int digits10 = numeric_limits<Float>::digits10;
cout << setprecision(digits10) << du_dx0;
}
9.3 Error analysis for the gradient
(Source le: doc/usrman/demo2-error-grad.cc)
The following estimation holds for the gradient:
|u u
h
|
0,2,
O(h
k
)
|u u
h
|
0,,
O(h
k
)
The gure 9.2 plots in logarithmic scale the error for the gradient versus nelt
1
2
for both P
1
(on
the left) and P
2
(on the right) approximations.
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
106 Rheolef version 5.94 update 27 May 2011
1=k
(u
h
h
u)
0,,
(u
h
h
u)
0,2,
nelt
1
2
P
1
element
10
1
10
2
10
3
10
1
10
2
10
3
2=k
(u
h
h
u)
0,,
(u
h
h
u)
0,2,
nelt
1
2
P
2
element
10
1
10
2
10
3
10
1
10
2
10
3
10
4
10
5
Figure 9.2: Error analysis for the gradiend: H
1
and W
1,
norms.
9.3.1 Implementation
#include "rheolef/rheolef.h"
using namespace rheolef;
using namespace std;
Float du_dxi (const point& x) { return cos(x[0]+x[1]+x[2]); }
int main(int argc, char**argv)
{
field qh;
cin >> qh;
space Th = qh.get_space();
field pi_h_q = interpolate(Th, du_dxi);
field eh = pi_h_q - qh;
form m(Th, Th, "mass");
cerr << "error_inf " << eh.max_abs() << endl;
cerr << "error_l2 " << sqrt(m(eh,eh)) << endl;
if (argc > 1) cout << eh;
return 0;
}
9.4 A problem with singularity
(Source le: doc/usrman/crack.cc)
9.4.1 Formulation
Let us now consider the Laplace operator in a non-convex domain. The domain is a disk with a
missing part, thus there exists a re-entrant corner (see Figure, on the left). The angle associated
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2
Rheolef version 5.94 update 27 May 2011 107
to the re-entrant corner is denoted by and whe suppose < 2. We assume homogeneous
Dirichlet conditions on the boundaries corresponding to the radius, and denoted by
0
. The rest
of the boundary is related to the envelop boundary and denoted by
e
.
(a) (b)
x
1
x
2
Figure 9.3: The domain of computation: (a) with a crack; (b) by using the Ox
1
symetry.
The problem expresses:
nd u dened in such that
u = 0 in
u = 0 on
0
u = g on
e
where g is expressed by the use of polar coordinates:
g(r, ) = r
sin()
and
=
The parameter is the intensity of the singularity at x = 0 (see e.g. [36]). This problem is
convenient since the exact solution is known: u = r
0,2,
u u
h
0,,
P
1
element
nelt
1
2
10
0
10
1
10
2
10
3
10
1
10
2
10
3
1=+
1
2
1
2
=
u u
h
0,2,
u u
h
0,,
P
2
element
nelt
1
2
10
0
10
1
10
2
10
3
10
1
10
2
10
3
Figure 9.4: Error analysis for a domain with a crack.
The gure plots the error in L
2
and L
norms. . . . . . . . . . . . . . . . . . . . . . . . . . . 104
9.2 Error analysis for the gradiend: H
1
and W
1,
norms. . . . . . . . . . . . . . . . . 106
9.3 The domain of computation: (a) with a crack; (b) by using the Ox
1
symetry. . . . 107
9.4 Error analysis for a domain with a crack. . . . . . . . . . . . . . . . . . . . . . . . 109
A.1 Visualization of the gmsh meshes my-square.geo and my-cube.geo. . . . . . . . 119
c
e
l
-
0
0
5
7
3
9
7
0
,
v
e
r
s
i
o
n
2
-
2
7
F
e
b
2
0
1
2