0% found this document useful (0 votes)
11 views307 pages

Rheo Lef

The document presents 'Rheolef', a C++ programming environment designed for efficient finite element method computing. It allows users to solve complex multi-physics problems with concise and readable code, utilizing advanced data structures and algorithms for various applications in partial differential equations. The document includes examples, usage instructions, and details on the library's capabilities, including support for distributed memory and parallel computations.

Uploaded by

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

Rheo Lef

The document presents 'Rheolef', a C++ programming environment designed for efficient finite element method computing. It allows users to solve complex multi-physics problems with concise and readable code, utilizing advanced data structures and algorithms for various applications in partial differential equations. The document includes examples, usage instructions, and details on the library's capabilities, including support for distributed memory and parallel computations.

Uploaded by

rbudiman
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 307

Efficient C++ finite element computing with Rheolef

Pierre Saramito

To cite this version:


Pierre Saramito. Efficient C++ finite element computing with Rheolef. Master. Grenoble, France,
France. 2022, pp.279. �cel-00573970v16�

HAL Id: cel-00573970


https://cel.hal.science/cel-00573970v16
Submitted on 2 Jun 2022

HAL is a multi-disciplinary open access L’archive ouverte pluridisciplinaire HAL, est


archive for the deposit and dissemination of sci- destinée au dépôt et à la diffusion de documents
entific research documents, whether they are pub- scientifiques de niveau recherche, publiés ou non,
lished or not. The documents may come from émanant des établissements d’enseignement et de
teaching and research institutions in France or recherche français ou étrangers, des laboratoires
abroad, or from public or private research centers. publics ou privés.
Efficient C++ finite element
computing with Rheolef
Pierre Saramito

version 7.2

Re = 10 000

Bi = 0.5 W e = 0.7
Copyright (c) 2003-2018 Pierre Saramito
Permission is granted to copy, distribute and/or modify this document under the terms of the
GNU Free Documentation License, Version 1.3 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy
of the license is included in the section entitled "GNU Free Documentation License".
Introduction
Rheolef is a programming environment for finite element method computing. The reader is as-
sumed to be familiar with (i) the c++ programming language and (ii) the finite element method.
As a Lego game, the Rheolef bricks allow the user to solve most problems, from simple to complex
multi-physics ones, in few lines of code. The concision and readability of codes written with
Rheolef is certainly a major keypoint of this environment. Here is an example of a Rheolef code
for solving the Poisson problem with homogeneous boundary conditions:

Example: find u such that −∆u = 1 in Ω and u = 0 on ∂Ω


int main (int argc, char** argv) {
environment rheolef (argc, argv);
geo omega (argv[1]); Let Ω ⊂ RN , N = 1, 2, 3
space Xh (omega, argv[2]); Xh = {v ∈ H 1 (Ω); v|K ∈ Pk , ∀K ∈ Th }
Xh.block ("boundary"); Vh = Xh ∩ H01 (Ω)
trial u (Xh); test v (Xh); Z
form a = integrate (dot(grad(u),grad(v))); a(u, v) = ∇u.∇v dx
field lh = integrate (v); Z Ω
l(v) = v dx
field uh (Xh); Ω
uh ["boundary"] = 0;
problem p (a); (P ) : find uh ∈ Vh such that
p.solve (lh, uh); a(uh , vh ) = l(vh ), ∀vh ∈ Vh
dout ≪ uh;
}

The right column shows the one-to-one line correspondence between the code and the
variational formulation. Let us quote Stroustrup [2002], the conceptor of the c++ language:

"The time taken to write a program is at best roughly proportional to the number of
lines written, and so is the number of errors in that code. If follows that a good way
of writing correct programs is to write short programs. In other words, we need good
libraries to allow us to write correct code that performs well. This in turn means that
we need libraries to get our programs finished in a reasonable time. In many fields,
such c++ libraries exist."

Rheolef is an attempt to provide such a library in the field of finite element methods for partial
differential equations. Rheolef provides both a c++ library and a set of unix commands for shell
programming, providing data structures and algorithms [Wirth, 1985].

• Data structures fit the variational formulation concept: field, bilinear form and functional
space, are c++ types for variables. They can be combined in algebraic expressions, as you
write it on the paper.

• Algorithms refer to the most up-to-date ones: direct an iterative sparse matrix solvers for
linear systems. They supports efficient distributed memory and parallel computations. Non-
linear c++ generic algorithms such as fixed point, damped Newton and continuation methods
are also provided.

General high order piecewise polynomial finite element approximations are implemented, together
with some mixed combinations for Stokes and incompressible elasticity. The characteristic method
can be used for diffusion-convection problems while hyperbolic systems can be discretized by the
discontinuous Galerkin method.
4 Rheolef version 7.2

Contacts

email [email protected]

home page http://www-ljk.imag.fr/membres/Pierre.Saramito/rheolef

Please send all patches, comments and bug reports by mail to

[email protected]
Contents

Notations 9

1 Getting started 13
1.1 The model problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.1 Problem statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.1.2 Approximation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.1.3 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.1.4 How to compile the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.1.5 How to run the program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.1.6 Advanced and stereo visualization . . . . . . . . . . . . . . . . . . . . . . . 17
1.1.7 High-order finite element methods . . . . . . . . . . . . . . . . . . . . . . . 19
1.1.8 Tridimensional computations . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.1.9 Quadrangles, prisms and hexahedra . . . . . . . . . . . . . . . . . . . . . . 20
1.1.10 Direct versus iterative solvers . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.1.11 Distributed and parallel runs . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.1.12 Non-homogeneous Dirichlet conditions . . . . . . . . . . . . . . . . . . . . . 24
1.2 Non-homogeneous Neumann boundary conditions for the Helmholtz operator . . . 32
1.3 The Robin boundary conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.4 Neumann boundary conditions for the Laplace operator . . . . . . . . . . . . . . . 35
1.5 Non-constant coefficients and multi-regions . . . . . . . . . . . . . . . . . . . . . . 37

2 Fluids and solids computations 43


2.1 The linear elasticity and the Stokes problems . . . . . . . . . . . . . . . . . . . . . 43
2.1.1 The linear elasticity problem . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.1.2 Computing the stress tensor . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.1.3 Mesh adaptation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.1.4 The Stokes problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.1.5 Computing the vorticity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.1.6 Computing the stream function . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.2 Nearly incompressible elasticity and the stabilized Stokes problems . . . . . . . . . 60
2.2.1 The incompressible elasticity problem . . . . . . . . . . . . . . . . . . . . . 60
2.2.2 The P1 b − P1 element for the Stokes problem . . . . . . . . . . . . . . . . . 62
2.2.3 Axisymmetric geometries . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
2.2.4 The axisymmetric stream function and stress tensor . . . . . . . . . . . . . 69
2.3 [New] Slip boundary conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

5
6 Rheolef version 7.2

2.4 Time-dependent problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76


2.4.1 The heat equation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
2.4.2 The convection-diffusion problem . . . . . . . . . . . . . . . . . . . . . . . . 79
2.5 The Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

3 Advanced and highly nonlinear problems 93


3.1 Equation defined on a surface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.1.1 Approximation on an explicit surface mesh . . . . . . . . . . . . . . . . . . 94
The Helmholtz-Beltrami problem . . . . . . . . . . . . . . . . . . . . . . . . 94
The Laplace-Beltrami problem . . . . . . . . . . . . . . . . . . . . . . . . . 98
3.1.2 Building a surface mesh from a level set function . . . . . . . . . . . . . . . 101
3.1.3 The banded level set method . . . . . . . . . . . . . . . . . . . . . . . . . . 104
3.1.4 Improving the banded level set method with a direct solver . . . . . . . . . 106
3.2 The highly nonlinear p-laplacian problem . . . . . . . . . . . . . . . . . . . . . . . 111
3.2.1 Problem statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.2.2 The fixed-point algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
3.2.3 The Newton algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.2.4 The damped Newton algorithm . . . . . . . . . . . . . . . . . . . . . . . . . 125
3.2.5 Error analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
3.3 Continuation and bifurcation methods . . . . . . . . . . . . . . . . . . . . . . . . . 130
3.3.1 Problem statement and the Newton method . . . . . . . . . . . . . . . . . . 131
3.3.2 Error analysis and multiplicity of solutions . . . . . . . . . . . . . . . . . . 134
3.3.3 The Euler-Newton continuation algorithm . . . . . . . . . . . . . . . . . . . 137
3.3.4 Beyond the limit point : the Keller algorithm . . . . . . . . . . . . . . . . . 140

4 Discontinuous Galerkin methods 147


4.1 Linear first-order problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
4.1.1 The stationary transport equation . . . . . . . . . . . . . . . . . . . . . . . 147
4.1.2 [New] The time-dependent transport equation . . . . . . . . . . . . . . . . . 150
4.1.3 [New] Example: the Zalesak slotted disk . . . . . . . . . . . . . . . . . . . . 152
4.1.4 [New] Example: the Leveque vortex-in-box . . . . . . . . . . . . . . . . . . 154
4.2 Nonlinear first-order problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
4.2.1 Abstract setting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
4.2.2 Slope limiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
4.2.3 Example: the Burgers equation . . . . . . . . . . . . . . . . . . . . . . . . . 161
4.3 Scalar second-order problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
4.3.1 The Poisson problem with Dirichlet boundary conditions . . . . . . . . . . 167
4.3.2 The Helmholtz problem with Neumann boundary conditions . . . . . . . . 170
4.3.3 [New] Heterogeneous diffusion . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.3.4 Nonlinear scalar hyperbolic problems with diffusion . . . . . . . . . . . . . 175
4.3.5 Example: the Burgers equation with diffusion . . . . . . . . . . . . . . . . . 175
4.4 Fluids and solids computations revisited . . . . . . . . . . . . . . . . . . . . . . . . 181
4.4.1 The linear elasticity problem . . . . . . . . . . . . . . . . . . . . . . . . . . 181
4.4.2 The Stokes problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
4.5 The stationnary Navier-Stokes equations . . . . . . . . . . . . . . . . . . . . . . . . 185
Contents 7

4.5.1 Problem statemment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185


4.5.2 The discrete problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
4.5.3 A conservative variant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
4.5.4 Newton solver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
4.5.5 Application to the driven cavity benchmark . . . . . . . . . . . . . . . . . . 195
4.5.6 Upwinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

5 Complex fluids 203


5.1 Yield slip at the wall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
5.1.1 Problem statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
5.1.2 The augmented Lagrangian algorithm . . . . . . . . . . . . . . . . . . . . . 204
5.1.3 Newton algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.1.4 Error analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
5.2 Viscoplastic fluids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
5.2.1 Problem statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
5.2.2 The augmented Lagrangian algorithm . . . . . . . . . . . . . . . . . . . . . 217
5.2.3 Mesh adaptation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
5.2.4 Error analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
5.2.5 Error analysis for the yield surface . . . . . . . . . . . . . . . . . . . . . . . 225
5.3 Viscoelastic fluids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
5.3.1 A tensor transport equation . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
5.3.2 The Oldroyd model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
5.3.3 The θ-scheme algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
5.3.4 Flow in an abrupt ontraction . . . . . . . . . . . . . . . . . . . . . . . . . . 239
5.3.5 [New] Stress diffusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246

6 [New] Hybrid discontinuous methods 249


6.1 The Raviart-Thomas element . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
6.2 Hybrid discontinuous Galerkin (HDG) methods . . . . . . . . . . . . . . . . . . . . 252
6.2.1 The Poisson problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
6.2.2 Superconvergence of the Lagrange multiplier . . . . . . . . . . . . . . . . . 257
6.2.3 Superconvergence of the piecewise averaged solution . . . . . . . . . . . . . 258
6.2.4 Improving the solution by local postprocessing . . . . . . . . . . . . . . . . 259
6.2.5 Improving the gradient with the Raviart-Thomas element . . . . . . . . . . 262
6.3 Hybrid high order (HHO) methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
6.3.1 The diffusion problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
6.3.2 The reconstruction operator . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
6.3.3 The projections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
6.3.4 The discrete problem statement . . . . . . . . . . . . . . . . . . . . . . . . . 270

A Technical appendices 279


A.1 How to write a variational formulation ? . . . . . . . . . . . . . . . . . . . . . . . . 279
A.1.1 The Green formula . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
A.1.2 The vectorial Green formula . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
A.1.3 The Green formula on a surface . . . . . . . . . . . . . . . . . . . . . . . . . 280
8 Rheolef version 7.2

A.2 How to prepare a mesh ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280


A.2.1 Bidimensional mesh with bamg . . . . . . . . . . . . . . . . . . . . . . . . . 281
A.2.2 Unidimensional mesh with gmsh . . . . . . . . . . . . . . . . . . . . . . . . 282
A.2.3 Bidimensional mesh with gmsh . . . . . . . . . . . . . . . . . . . . . . . . . 282
A.2.4 Tridimensional mesh with gmsh . . . . . . . . . . . . . . . . . . . . . . . . . 283

B GNU Free Documentation License 285

List of example files 298

List of commands 301

Index 303
Contents 9

Notations

Rheolef mathematics description

d d ∈ {1, 2, 3} dimension of the physical space

interpolate(Vh,expr) πVh (expr) interpolation in the space Vh


Z
integrate(omega,expr) expr dx integration in Ω ⊂ Rd

integrate(omega, X Z
expr ds integration on the local element sides
on_local_sides(expr)) ∂K
K∈Th
d−1
X
dot(u,v) u.v = ui vi vector scalar product
i=0
d−1
X
ddot(sigma,tau) σ:τ = σi,j τi,j tensor scalar product
i,j=0
. d−1
A .. B =
X
dddot(A,B) Ai,j,k Bi,j,k tensor3 scalar product
i,j,k=0
d−1
X
tr(sigma) tr(σ) = σi,i trace of a tensor
i=0

trans(sigma) σT tensor transposition

sqr(phi)
ϕ2 square of a scalar
norm2(phi)
d−1
X
norm2(u) |u|2 = u2i square of the vector norm
i=0
d−1
X
norm2(sigma) |σ|2 = 2
σi,j square of the tensor norm
i,j=0

abs(phi)
|ϕ| absolute value of a scalar
norm(phi)
d−1
!1/2
X
norm(u) |u| = u2i vector norm
i=0
 1/2
d−1
X
norm(sigma) |σ| =  2 
σi,j tensor norm
i,j=0
 
∂ϕ
grad(phi) ∇ϕ = gradient of a scalar field
∂x
 i 0⩽i<d
∂ui
grad(u) ∇u = gradient of a vector field
∂xj 0⩽i,j<d
d−1
X ∂ui
div(u) div(u) = tr(∇u) = divergence of a vector field
i=0
∂xi
 symmetric part of
D(u) D(u) = ∇u + ∇uT /2
the gradient of a vector field
10 Rheolef version 7.2

Rheolef mathematics description

curl(u) curl(u) = ∇ ∧ u curl of a vector field, when d = 3


 
∂ϕ ∂ϕ
curl(phi) curl(ϕ) = ,− curl of a scalar field, when d = 2
∂x1 ∂x0
∂u1 ∂u0
curl(u) curl(u) = − curl of a vector field, when d = 2
∂x0 ∂x1
∇s ϕ = P ∇ϕ
grad_s(phi) tangential gradient of a scalar
where P = I − n ⊗ n

grad_s(u) ∇s u = ∇uP tangential gradient of a vector

Ds(u) Ds (u) = P D(u)P symmetrized tangential gradient

div_s(u) divs (u) = tr(Ds (u)) tangential divergence

unit outward normal on Γ = ∂Ω


normal() n or on an oriented surface Ω
or on an internal oriented side S
(
ϕ|K0 − ϕ|K1
jump(phi) [[ϕ]] = jump accross side S = ∂K0 ∩ K1
ϕ on ∂Ω
see section 4.1.1 page 147
(
(ϕ|K0 + ϕ|K1 )/2
average(phi) {{ϕ}} = average across S = ∂K0 ∩ K1
ϕ on ∂Ω

(
ϕ|K0
inner(phi) = inner trace across S = ∂K0 ∩ K1
ϕ on ∂Ω

(
ϕ|K1
outer(phi) = outer trace across S = ∂K0 ∩ K1
ϕ on ∂Ω

h_local() hK = meas(K)1/d length scale on an element K


 
meas(∂K0 ) meas(∂K1 )
penalty() ϖs = max , penalty coefficient on S
meas(K0 ) meas(K1 )

grad_h(phi) (∇h ϕ)|K = ∇(ϕ|K ), ∀K ∈ Th broken gradient

div_h(u) (divh u)|K = div(u|K ), ∀K ∈ Th broken divergence of a vector field

Dh(u) (Dh (u))|K = D(u|K ), ∀K ∈ Th broken symmetric part of


Contents 11

Rheolef mathematics description


the gradient of a vector field

sin(phi) sin(ϕ) standard mathematical functions


cos(phi) cos(ϕ) extended to scalar fields
tan(phi) tan(ϕ)
acos(phi) cos−1 (ϕ)
asin(phi) sin−1 (ϕ)
atan(phi) tan−1 (ϕ)
cosh(phi) cosh(ϕ)
sinh(phi) sinh(ϕ)
tanh(phi) tanh(ϕ)
exp(phi) exp(ϕ)
log(phi) log(ϕ)
log10(phi) log 10(ϕ)
floor(phi) ⌊ϕ⌋ largest integral not greater than ϕ
ceil(phi) ⌈ϕ⌉ smallest integral not less than ϕ
min(phi,psi) min(ϕ, ψ)
max(phi,psi) max(ϕ, ψ)
pow(phi,psi) ϕψ
atan2(phi,psi) tan−1 (ψ/ϕ)
fmod(phi,psi) ϕ − ⌊ϕ/ψ + 1/2⌋ ψ floating point remainder

compose(f,phi) f ◦ ϕ = f (ϕ) applies an unary function f

compose(f,phi1,...,phin) f (ϕ1 , . . . , ϕn ) applies a n-ary function f , n ⩾ 1

compose(phi,X) ϕ ◦ X, X(x) = x + d(x) composition with a characteristic


12 Rheolef version 7.2
Chapter 1

Getting started

The first chapter 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 the model problem. The last two examples
presents some special difficulties that appears in most problems: the first one introduce to problems
with non-constant coefficients and the second one, a ill-posed problem where the solution is defined
up to a constant.
This first chapter can be viewed as a pedagogic preparation for more advanced applications, such
as Stokes and elasticity, that are treated in the second chapter of this book. Problem with non-
constant coefficients are common as suproblems generated by various algorithms for non-linear
problem.

1.1 The model problem


For obtaining and installing Rheolef, see the installation instructions on the Rheolef home page:

http://www-ljk.imag.fr/membres/Pierre.Saramito/rheolef

Before to run examples, please check your Rheolef installation with:

rheolef-config --check

The present book is available in the documentation directory of the Rheolef distribution. This
documentation directory is given by the following unix command:

rheolef-config --docdir

All examples presented along the present book are also available in the example/ directory of the
Rheolef distribution. This directory is given by the following unix command:

rheolef-config --exampledir

This command returns you a path, something like /usr/share/doc/rheolef-doc/examples and


you should make a copy of these files:

cp -a /usr/share/doc/rheolef-doc/examples .
cd examples

13
14 Rheolef version 7.2

1.1.1 Problem statement


Let us consider the classical Poisson problem with homogeneous Dirichlet boundary conditions in
a domain bounded Ω ⊂ Rd , d = 1, 2, 3:
(P): find u, defined in Ω, such that:

−∆u = 1 in Ω (1.1)
u = 0 on ∂Ω (1.2)

where ∆ denotes the Laplace operator. The variational formulation of this problem expresses (see
appendix A.1.1 for details):
(VF): find u ∈ H01 (Ω) such that:

a(u, v) = l(v), ∀v ∈ H01 (Ω) (1.3)

where the bilinear form a(., .) and the linear form l(.) are defined by
Z
a(u, v) = ∇u.∇v dx, ∀u, v ∈ H01 (Ω)

Z
l(v) = v dx, ∀v ∈ L2 (Ω)

The bilinear form a(., .) defines a scalar product in H01 (Ω) and is related to the energy form. This
form is associated to the −∆ operator.

1.1.2 Approximation
Let us introduce a mesh Th of Ω and the finite dimensional space Xh of continuous piecewise
polynomial functions.
Xh = {v ∈ H 1 (Ω); v/K ∈ Pk , ∀K ∈ Th }
where k = 1 or 2. Let Vh = Xh ∩ H01 (Ω) be the functions of Xh that vanishes on the boundary of
Ω. The approximate problem expresses:
(V F )h : find uh ∈ Vh such that:

a(uh , vh ) = l(vh ), ∀vh ∈ Vh

By developing uh on a basis of Vh , this problem reduces to a linear system. The following C++
code implement this problem in the Rheolef environment.

File 1.1: dirichlet.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 geo omega ( argv [1]);
7 space Xh ( omega , argv [2]);
8 Xh . block ( " boundary " );
9 trial u ( Xh ); test v ( Xh );
10 form a = lazy_integrate ( dot ( grad ( u ) , grad ( v )));
11 field lh = lazy_integrate ( v );
12 field uh ( Xh );
13 uh [ " boundary " ] = 0;
14 problem p ( a );
15 p . solve ( lh , uh );
16 dout << uh ;
17 }
Chapter 1. Getting started 15

1.1.3 Comments
This code applies for both one, two or three dimensional meshes and for both piecewise linear or
quadratic finite element approximations. Four major classes are involved, namely: geo, space,
form and field.
Let us now comment the code, line by line.
# include " rheolef . h "

The first line includes the Rheolef header file ‘rheolef.h’.


using namespace rheolef ;
using namespace std ;

By default, in order to avoid possible name conflicts when using another library, all class and
function names are prefixed by rheolef::, as in rheolef::space. This feature is called the name
space. Here, since there is no possible conflict, and in order to simplify the syntax, we drop all the
rheolef:: prefixes, and do the same with the standard c++ library classes and variables, that are
also prefixed by std::.
int main ( int argc , char ** argv ) {

The entry function of the program is always called main and accepts arguments from the unix
command line: argc is the counter of command line arguments and argv is the table of values.
The character string argv[0] is the program name and argv[i], for i = 1 to argc-1, are the
additional command line arguments.
environment rheolef ( argc , argv );

These two command line parameters are immediately furnished to the distributed environment
initializer of the boost::mpi library, that is a c++ library based on the usual message passing
interface (mpi) library. Note that this initialization is required, even when you run with only one
processor.
geo omega ( argv [1]);

This command get the first unix command-line argument argv[1] as a mesh file name and store
the corresponding mesh in the variable omega.
space Xh ( omega , argv [2]);

Build the finite 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, P2 or
any Pk, where k ⩾ 1.
Xh . block ( " boundary " );

The homogeneous Dirichlet conditions are declared on the boundary.


trial u ( Xh ); test v ( Xh );
form a = integrate ( dot ( grad ( u ) , grad ( v )));

The bilinear form a(., .) is the energy form: it is defined for all functions u and v in Xh .
field lh = integrate ( v );

The linear form lh(.) is associated to the constant right-hand side f = 1 of the problem. It is
defined for all v in Xh .
field uh ( Xh );

The field uh contains the the degrees of freedom.


uh [ " boundary " ] = 0;

Some degrees of freedom are prescribed as zero on the boundary.


16 Rheolef version 7.2

problem p ( a );
p . solve ( lh , uh );

Finally, the problem related to the bilinear form a and the right-hand-side lh is solved and uh
contains the solution. The field is printed to standard output:
dout << uh ;

The dout stream is a specific variable defined in the Rheolef library: it is a distributed and
parallel extension of the usual cout stream in C++
Let us study with more details the linear system. Let (φi )0⩽i<dim(Xh ) be the basis of Xh associated
to the Lagrange nodes, e.g. the vertices of the mesh for the P1 approximation and the vertices
and the middle of the edges for the P2 approximation. The approximate solution uh expresses as
a linear combination of the continuous piecewise polynomial functions (φi ):
X
uh = ui φi
i

Thus, the field uh is completely represented by its coefficients (ui ). The coefficients (ui ) of this
combination are grouped into to sets: some have zero values, from the boundary condition and
are related to blocked coefficients, and some others are unknown. Blocked coefficients are stored
into the uh.b array while unknown one are stored into uh.u. Thus, the restriction of the bilinear
form a(., .) to Xh × Xh can be conveniently represented by a block-matrix structure:
  
 a.uu a.ub uh.u
a(uh , vh ) = vh.u vh.b
a.bu a.bb uh.b
This representation also applies for the linear form l(.):
 
 lh.u
l(vh ) = vh.u vh.b
lh.b
Thus, the problem (V F )h writes now:
    
 a.uu a.ub uh.u  lh.u
vh.u vh.b = vh.u vh.b
a.bu a.bb uh.b lh.b
for any vh.u and where vh.b = 0. After expansion, the problem reduces to find uh.u such that:
a.uu ∗ uh.u = l.u − a.ub ∗ uh.b
The resolution of this linear system for the a.uu matrix is then performed via the solver class:
this call is performed by the problem class. For more details, see the Rheolef reference manual
related to the problem and solver classes, available on the web site and via the unix command:
man problem
man solver

1.1.4 How to compile the code


First, create a file ‘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.
Chapter 1. Getting started 17

1.1.5 How to run the program

Figure 1.1: Solution of the model problem for d = 2 with the P1 element: visualization (left) with
paraview as filled isocontours ; (right) with gnuplot as unfilled isocontours.

Enter the commands:

mkgeo_grid -t 10 > square.geo


geo square.geo

The first command generates a simple 10x10 bidimensional mesh of Ω =]0, 1[2 and stores it in the
file square.geo. The second command shows the mesh. It uses paraview visualization program
by default.
The next commands perform the computation and visualization:

./dirichlet square.geo P1 > square.field


field square.field

The result is hown on Fig. 1.1. By default, the visualization appears in a paraview window. If
you are in trouble with this software, you can switch to the simpler gnuplot visualization mode:

field square.field -gnuplot

1.1.6 Advanced and stereo visualization


We could explore some graphic rendering modes (see Fig. 1.2):

field square.field -bw


field square.field -gray
field square.field -elevation
field square.field -elevation -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. In the book, stereo figures are indicated by the logo in the
right margin. See http://en.wikipedia.org/wiki/Anaglyph_image for more and https://
18 Rheolef version 7.2

Figure 1.2: Alternative representations of the solution of the model problem (d = 2 and the P1
element): (left) in black-and-white; (right) in elevation and stereoscopic anaglyph mode.

Figure 1.3: Red-cyan anaglyph glasses for the stereoscopic visualization.

www.ecolofrance.com/56-lunettes-anaglyphe-rouge-cyan-carton-pas-cher for how to find


anaglyph red-cyan glasses. For simplicity, it would perhaps prefer to switch to the gnuplot render:

field square.field -gnuplot


field square.field -gnuplot -bw
field square.field -gnuplot -gray

Please, consult the Rheolef reference manual page for more on the unix commands field, geo
and mkgeo_grid. The manual is available both on the web site:

firefox https://www-ljk.imag.fr/membres/Pierre.Saramito/rheolef

and as individual unix man pages:

man mkgeo_grid
man geo
man field
Chapter 1. Getting started 19

See also

man rheolef

The complete list of Rheolef man page is obtained by:

man -k rheolef

1.1.7 High-order finite element methods


Turning to the P2 or P3 approximations simply writes:

./dirichlet square.geo P2 > square-P2.field


field square-P2.field

You can replace the P2 command-line argument by any Pk, where k ⩾ 1. Now, let us consider a
mono-dimensional problem Ω =]0, 1[:

mkgeo_grid -e 10 > line.geo


geo line.geo
./dirichlet line.geo P1 | field -

The first 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 -

1.1.8 Tridimensional computations


Let us consider a three-dimensional problem Ω =]0, 1[3 . First, let us generate a mesh:

mkgeo_grid -T 10 > cube.geo


geo cube.geo
geo cube.geo -fill
geo cube.geo -cut
geo cube.geo -shrink
geo cube.geo -shrink -cut

The 3D visualization bases on the paraview render. These commands present some cuts (-cut)
inside the internal mesh structure: a simple click on the central arrow draws the cut plane normal
vector or its origin, while the red square allows a translation. The following command draws the
mesh with all internal edges (-full), together with the stereoscopic anaglyph (-stereo):

geo cube.geo -stereo -full

Then, we perform the computation and the visualization:

./dirichlet cube.geo P1 > cube.field


field cube.field

The visualization presents an isosurface. Also here, you can interact with the cutting plane. On
the Properties of the paraview window, select Contour, change the value of the isosurface and
click on the green Apply button. Finally exit from the visualization and explore the stereoscopic
anaglyph mode (see Fig. 1.4, right):
20 Rheolef version 7.2

Figure 1.4: Solution of the model problem for d = 3 and the P1 element : (left) mesh; (right)
isovalue, cut planes and stereo anaglyph renderings.

field cube.field -stereo

It is also possible to add a second isosurface (Contour) or a cutting plane (Slice) to this scene
by using the corresponding Properties menu. Finally, the following command, with the -volume
option, allows a 3D color light volume graphical rendering:

field cube.field -volume

After this exploration of the 3D visualization capacities of our environment, let us go back to the
Dirichlet problem and perform the P2 approximation:

./dirichlet cube.geo P2 | field -

1.1.9 Quadrangles, prisms and hexahedra


Quadrangles and hexahedra are also supported in meshes:

mkgeo_grid -q 10 > square.geo


geo square.geo
mkgeo_grid -H 10 > cube.geo
geo cube.geo

Note also that the one-dimensional exact solution expresses explicitly:


x(1 − x)
u(x) =
2
In the two-and three dimensional cases, an explicit expression of the solution, based on Fourier
expansion, is also known (see e.g. Saramito and Roquet, 2001, annex B, page 5411).

1.1.10 Direct versus iterative solvers


In order to measure the performances of the solver, the dirichlet.cc (page 14) has been modified
as:
Chapter 1. Getting started 21

103 103
T (n) T (n)
2
d=2 d=3

1.5

100 100
1 1.33

direct/factorize direct/factorize
direct/solve direct/solve
iterative/solve iterative/solve
10−3 10−3
103 104 105 106 107 103 104 105 106
n n

Figure 1.5: Compared performance between direct and iterative solvers: (left) d = 2; (right) d = 3.

double t0 = dis_wall_time ();


solver_option sopt ;
sopt . iterative = false ; // or true
sopt . tol = 1 -5; // when iterative
problem p (a , sopt );
double t_factorize = dis_wall_time () - t0 ;
p . solve ( lh , uh );
double t_solve = dis_wall_time () - t0 - t_factorize ;
derr << " time " << t_factorize << " " << t_solve << endl ;

The dis_wall_time function returns the wall-clock time in seconds, while the solver_option
sopt enables to choose between a direct or an iterative solver method: by default Rheolef selects
a direct method when d ⩽ 2 and an iterative one when d = 3. For a 3D mesh, the compilation
and run writes:

make dirichlet
mkgeo_grid -T 60 > cube-60.geo
./dirichlet cube-60.geo P1 > cube-60.field

Fig. 1.5 plots the performances of the direct and iterative solvers used in Rheolef. The com-
puting time T (n) is represented versus size n of the linear system, says Ax = b. Note that for a
square-k.geo or cube-k.geo mesh, the size of the linear system is n = (k − 1)d . For the direct
method, two times are represented: first, the time spend to factorize A = LDLT , where L is lower
triangular and D is diagonal, and second, the time used to solve LDLT = x (in three steps: solving
Lz = b, then Dy = z and finally LT x = y). For the iterative method, the conjugate gradient
algorithm is considered, without building any preconditioner, so there is nothing to initialize, and
only one time is represented. The tolerance on the residual term is set to 10−5 .
In the bidimensional case, the iterative solver presents asymptotically, for large n, a computing
time similar to the factorization time of the direct solver, roughly O(n3/2 ) while the time to solve
by the direct method is dramatically lower, roughly O(n). As the factorization can be done one
time for all, the direct method is advantageous most of the time.
In the three dimensional case, the situation is different. The factorization time is very time
consuming roughly O(n2 ), while the time to solve for both direct and iterative methods behave
as O(n4/3 ). Thus, the iterative method is clearly advantageous for three-dimensionnal problems.
Future works will improve the iterative approach by building preconditioners.
The asymptotic behaviors of direct methods strongly depends upon the ordering strategy used
for the factorization. For the direct solver, Rheolef was configured with the mumps [Amestoy
et al., 2001, 2006] library and mumps was configured with the parallel scotch [Pellegrini, 2010]
22 Rheolef version 7.2

ordering library. For a regular grid and in the bidimensional case, there exists a specific ordering
called nested disection [Hoffman et al., 1973, George, 1973] that minimize the fillin of the sparse
matrix during the factorization. For three-dimensional case this ordering is called nested multi-
section [Ashcraft and Liu, 1998]. Asymptotic computing time for these regular grid are then
explicity known versus the grid size n:

d direct/factorize direct/solve iterative


1 n n n2

2 n3/2 n log n n3/2

3 n2 n4/3 n4/3

The last column gives the asymptotic computing time for the conjugate gradient on a general
mesh [Saramito, 2013a]. Remark that these theoretical results are consistent with numerical
experiments presented on Fig. 1.5. In conclusion, the best strategy is to choose a direct method
when d ⩽ 2 and an iterative one when d = 3: this is the default behavior with Rheolef.

1.1.11 Distributed and parallel runs


For large meshes, a computation in a distributed and parallel environment is profitable:

mpirun -np 8 ./dirichlet cube-60.geo P1 > cube-60.field


mpirun -np 16 ./dirichlet cube-60.geo P1 > cube-60.field

The computing time T (n, p) depends now upon the linear system size n and the number of
processes p. For a fixed size n, the speedup S(p) when using p processors is defined by the
ratio of the time required by a sequential computation with the time used by a parallel one:
S(p) = T (n, 1)/T (n, p). The speedup is presented on Fig 1.6 for the two phases of the com-
putation: the assembly phase and the solve one, and for d = 2 (direct solver) and 3 (iterative
solver). The ideal speedup S(p) = p and the null speedup S(p) = 1 are represented by dotted
lines. Observe on Fig 1.6 that for too small meshes, using too much processes is not profitable,
as more time is spend by communications rather by computations, especially for the solve phase.
Conversely, when the mesh size increases, using more processes leads to a remarkable speedup for
both d = 2 and 3. The largest mesh used here contains about three millions of elements. The
speedup behavior is roughly linear up to a critical number of processor denotes as pc . Then, there
is a transition to a plateau (the Amdahl’s law), where communications dominate. Note that pc
increases with the mesh size: larger problems lead to a higher speedup. Also pc increases also with
the efficiency of communications.
Present computation times are measured on a BullX DLC supercomputer (Bull Newsca) composed
of nodes having two intel sandy-bridge processors and connected to a FDR infiniband non-blocking
low latency network. The assembly phase corresponds to dirichlet.cc (page 14) line 7 to 13
and the solve phase to lines 14 and 15.
Chapter 1. Getting started 23

32 32
square-40 cube-10
S(p) square-80 S(p) cube-20
square-160 cube-40
24 square-320 24 cube-60
square-640
square-1280
assembly d = 2 assembly d = 3
16 16

8 8

1
0 10
0 8 16 24 32 0 8 16 24 32
p p
32 32
square-40 cube-10
S(p) square-80 S(p) cube-20
square-160 cube-40
24 square-320 24 cube-60
square-640
square-1280
direct solve d = 2 direct solve d = 3
16 16

8 8

1
0 10
0 8 16 24 32 0 8 16 24 32
p p
32 32
square-40 cube-10
S(p) square-80 S(p) cube-20
square-160 cube-40
24 square-320 24 cube-60
square-640
square-1280
iterative solve d = 2 iterative solve d = 3
16 16

8 8

1
0 10
0 8 16 24 32 0 8 16 24 32
p p

Figure 1.6: Distributed and massively parallel resolution of the model problem with P1 element:
speedup S(p) versus the number of processors p during : (left-right) for d = 2 and 3, respec-
tively ; (top) the assembly phase ; (center-bottom) the solve phase, direct and iterative solvers,
respectively.
24 Rheolef version 7.2

1.1.12 Non-homogeneous Dirichlet conditions

Formulation

We turn now to the case of a non-homogeneous Dirichlet boundary conditions. Let f ∈ H −1 (Ω)
1
and g ∈ H 2 (∂Ω). The problem writes:
(P2 ) find u, defined in Ω such that:

−∆u = f in Ω
u = g on ∂Ω

The variational formulation of this problem expresses:


(V F2 ) find u ∈ V such that:

a(u, v) = l(v), ∀v ∈ V0

where

Z
a(u, v) = ∇u.∇v dx
ZΩ
l(v) = f v dx

V = {v ∈ H 1 (Ω); v|∂Ω = g}
V0 = H01 (Ω)

Approximation

As usual, we introduce a mesh Th of Ω and the finite dimensional space Xh :

Xh = {v ∈ H 1 (Ω); v/K ∈ Pk , ∀K ∈ Th }

Then, we introduce:

Vh = {v ∈ Xh ; v|∂Ω = πh (g)}
V0,h = {v ∈ Xh ; v|∂Ω = 0}

where πh denotes the Lagrange interpolation operator. The approximate problem writes:
(V F2 )h : find uh ∈ Vh such that:

a(uh , vh ) = l(vh ), ∀vh ∈ V0,h

The following C++ code implement this problem in the Rheolef environment.
Chapter 1. Getting started 25

File 1.2: dirichlet-nh.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cosinusprod_laplace . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 size_t d = omega . dimension ();
9 space Xh ( omega , argv [2]);
10 Xh . block ( " boundary " );
11 trial u ( Xh ); test v ( Xh );
12 form a = integrate ( dot ( grad ( u ) , grad ( v )));
13 field lh = integrate ( f ( d )* v );
14 field uh ( Xh );
15 space Wh ( omega [ " boundary " ] , argv [2]);
16 uh [ " boundary " ] = lazy_interpolate ( Wh , g ( d ));
17 problem p ( a );
18 p . solve ( lh , uh );
19 dout << uh ;
20 }

Let us choose Ω ⊂ Rd , d = 1, 2, 3 with


d−1
Y d−1
Y
f (x) = d π 2 cos(πxi ) and g(x) = cos(πxi ) (1.4)
i=0 i=0

Remarks the notation x = (x0 , . . . , xd−1 ) for the Cartesian coordinates in Rd : since all arrays
start at index zero in C++ codes, and in order to avoid any mistakes between the code and the
mathematical formulation, we also adopt this convention here. This choice of f and g is convenient,
since the exact solution is known:
d−1
Y
u(x) = cos(πxi )
i=0
The following C++ code implement this problem by using the concept of function object, also
called class-function (see e.g. Musser and Saini, 1996a). A convenient feature is the ability for
function objects to store auxiliary parameters, such as the space dimension d for f here, or some
constants, as π for f and g.

File 1.3: cosinusprod_laplace.h


1 struct f {
2 Float operator () ( const point & x ) const {
3 return d * pi * pi * cos ( pi * x [0])* cos ( pi * x [1])* cos ( pi * x [2]); }
4 f ( size_t d1 ) : d ( d1 ) , pi ( acos ( Float ( -1))) {}
5 size_t d ; const Float pi ;
6 };
7 struct g {
8 Float operator () ( const point & x ) const {
9 return cos ( pi * x [0])* cos ( pi * x [1])* cos ( pi * x [2]); }
10 g ( size_t d1 ) : pi ( acos ( Float ( -1))) {}
11 const Float pi ;
12 };

Comments

The class point describes the coordinates of a point (x0 , . . . , xd−1 ) ∈ Rd as a d-uplet of Float. The
Float type is usually a double. This type depends upon the Rheolef configuration (see Saramito,
2012a, installation instructions), and could also represent some high precision floating point class.
The dirichlet-nh.cc code looks like the previous one dirichlet.cc related to homogeneous
boundary conditions. Let us comments the changes. The dimension d comes from the geometry
Ω:
26 Rheolef version 7.2

size_t d = omega . dimension ();

The linear form l(.) is associated to the right-hand side f and writes:
field lh = integrate ( f ( d )* v );

Note that the function f that depends upon the dimension d parameter, is implemented by a
functor, i.e. a C++ class that possesses the operator() member function. The space Wh of
piecewise Pk functions defined on the boundary ∂Ω is defined by:
space Wh ( omega [ " boundary " ] , argv [2]);

where Pk is defined via the second command line argument argv[2]. This space is suitable for
the Lagrange interpolation of g on the boundary:
uh [ " boundary " ] = interpolate ( Wh , g ( d ));

The values of the degrees of freedom related to the boundary are stored into the field uh.b, where
non-homogeneous Dirichlet conditions applies. The rest of the code is similar to the homogeneous
Dirichlet case.

How to run the program

First, compile the program:

make dirichlet-nh

Running the program is obtained from the homogeneous Dirichlet case, by replacing dirichlet
by dirichlet-nh:

mkgeo_grid -e 10 > line.geo


./dirichlet-nh line.geo P1 > line.field
field line.field

for the bidimensional case:

mkgeo_grid -t 10 > square.geo


./dirichlet-nh square.geo P1 > square.field
field square.field

and for the tridimensional case:

mkgeo_grid -T 10 > box.geo


./dirichlet-nh box.geo P1 > box.field
field box.field -volume

The optional -volume allows a 3D color light volume graphical rendering. Here, the P1 approxi-
mation can be replaced by P2, P3, etc, by modifying the command-line argument.

Error analysis

Since the solution u is regular, the following error estimates holds:

∥u − uh ∥0,2,Ω = O(hk+1 )
∥u − uh ∥0,∞,Ω = O(hk+1 )
∥u − uh ∥1,2,Ω = O(hk )

providing the approximate solution uh uses Pk continuous finite element method, k ⩾ 1. Here,
∥.∥0,2,Ω , ∥.∥0,∞,Ω and ∥.∥1,2,Ω denotes as usual the L2 (Ω), L∞ (Ω) and H 1 (Ω) norms.
Chapter 1. Getting started 27

The following code implement the computation of the error.

File 1.4: cosinusprod_error.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cosinusprod . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 Float err_u_linf_expected = ( argc > 1) ? atof ( argv [1]) : 1 e +38;
8 field uh ; din >> uh ;
9 geo omega = uh . get_geo ();
10 space Xh = uh . get_space ();
11 size_t k = Xh . degree ();
12 size_t d = Xh . get_geo (). dimension ();
13 integrate_option iopt ;
14 iopt . set_order ( min (3*( k +1)+4 , size_t (17)));
15 Float err_u_l2 = sqrt ( integrate ( omega , sqr ( uh - u_exact ( d )) , iopt ));
16 string opts = Xh . get_basis (). option (). stamp ();
17 space Xh1 ( omega , " P " + to_string ( k +1)+ " d " + opts );
18 field euh = lazy_interpolate ( Xh1 , uh - u_exact ( d ));
19 Float err_u_linf = euh . max_abs ();
20 Float err_u_h1 = sqrt ( integrate ( omega , norm2 ( grad_h ( euh )) , iopt ));
21 dout << " err_u_l2 " << err_u_l2 << endl
22 << " err_u_linf " << err_u_linf << endl
23 << " err_u_h1 " << err_u_h1 << endl ;
24 return ( err_u_linf <= err_u_linf_expected ) ? 0 : 1;
25 }

File 1.5: cosinusprod.h


1 struct u_exact {
2 Float operator () ( const point & x ) const {
3 return cos ( pi * x [0])* cos ( pi * x [1])* cos ( pi * x [2]); }
4 u_exact ( size_t d1 ) : d ( d1 ) , pi ( acos ( Float ( -1.0))) {}
5 size_t d ; Float pi ;
6 };

Running the program


make dirichlet-nh cosinusprod_error

After compilation, run the code by using the command:

mkgeo_grid -t 10 > square.geo


./dirichlet-nh square.geo P1 | ./cosinusprod_error

The three L2 , L∞ and H 1 errors are printed for a h = 1/10 uniform mesh. Note that an unstruc-
tured quasi-uniform mesh can be simply generated by using the mkgeo_ugrid command:

mkgeo_ugrid -t 10 > square.geo


geo square.geo

Let nel denotes the number of elements in the mesh. Since the mesh is quasi-uniform, we have
1
h ≈ neld where d is the physical space dimension. Here d = 2 for our bidimensional mesh. Figure 1.7
1
plots in logarithmic scale the error versus nel2 for both Pk approximations, k = 1, 2, 3 and the
various norms. Observe that the error behaves as predicted by the theory.

Curved domains
The error analysis applies also for curved boundaries and high order approximations.
28 Rheolef version 7.2

10−2
∥uh − πh (u)∥0,2,Ω

10−4
2=k+1

10−6
3

10−8
k=1
4 k=2
k=3
10−10
10−2 10−1
h
10−2 100
∥uh − πh (u)∥0,∞,Ω |uh − πh (u)|1,2,Ω
2=k+1
10−4 10−2
1=k

10−6 3 10−4
2

10−8 4 10−6 3
k=1 k=1
k=2 k=2
k=3 k=3
10−10 10−8
10−2 10−1 10−2 10−1
h h

Figure 1.7: Strait geometry: error analysis in L2 , L∞ and H 1 norms.

File 1.6: cosinusrad_laplace.h


1 struct f {
2 Float operator () ( const point & x ) const {
3 Float r = sqrt ( sqr ( x [0])+ sqr ( x [1])+ sqr ( x [2]));
4 Float sin_over_ar = ( r == 0) ? 1 : sin ( a * r )/( a * r );
5 return sqr ( a )*(( d -1)* sin_over_ar + cos ( a * r )); }
6 f ( size_t d1 ) : d ( d1 ) , a ( acos ( Float ( -1.0))) {}
7 size_t d ; Float a ;
8 };
9 struct g {
10 Float operator () ( const point & x ) const {
11 return cos ( a * sqrt ( sqr ( x [0])+ sqr ( x [1])+ sqr ( x [2]))); }
12 g ( size_t =0) : a ( acos ( Float ( -1.0))) {}
13 Float a ;
14 };

File 1.7: cosinusrad.h


1 struct u_exact {
2 Float operator () ( const point & x ) const {
3 Float r = sqrt ( sqr ( x [0])+ sqr ( x [1])+ sqr ( x [2]));
4 return cos ( a * r ); }
5 u_exact ( size_t =0) : a ( acos ( Float ( -1.0))) {}
6 Float a ;
Chapter 1. Getting started 29

First, generate the test source file and compile it:

sed -e ’s/sinusprod/sinusrad/’ < dirichlet-nh.cc > dirichlet_nh_ball.cc


sed -e ’s/sinusprod/sinusrad/’ < cosinusprod_error.cc > cosinusrad_error.cc
make dirichlet_nh_ball cosinusrad_error

Then, generates the mesh of a circle and run the test case:

mkgeo_ball -order 1 -t 10 > circle-P1.geo


geo circle-P1 -gnuplot
./dirichlet_nh_ball circle-P1.geo P1 | ./cosinusrad_error

For a high order k = 3 isoparametric approximation:

mkgeo_ball -order 3 -t 10 > circle-P3.geo


geo circle-P3 -gnuplot
./dirichlet_nh_ball circle-P3.geo P3 | ./cosinusrad_error

Observe Fig. 1.8: for meshes based on triangles: the error behave as expected when k = 1, 2, 3, 4.
These features are available for arbitrarily Pk high order approximations and three-dimensional
geometries. In practice, the current implementation is efficient up to k = 5: for higher polynomial
degrees, a more specific implementation is in development.
30 Rheolef version 7.2

100 100
∥uh − πh (u)∥0,2,Ω ∥uh − πh (u)∥0,∞,Ω
10−2 10−2

2=k+1
10−4 10−4 2=k+1

10−6 10−6 3
3

k =1 4 k =1
10−8 k =2 10−8 k =2
4
k =3 k =3
k =4 k =4
10−10 10−10
10−2 10−1 10−2 10−1
h h
100
|uh − πh (u)|1,2,Ω

10−2 1=k

10−4 2

3
10−6 k =1
k =2
k =3
k =4
10−8
10−2 10−1
h

Figure 1.8: Curved domains (triangles): error analysis in L2 , L∞ and H 1 norms.


Chapter 1. Getting started 31

100
10−2 ∥uh − πh (u)∥0,2,Ω ∥uh − πh (u)∥0,∞,Ω
10−2
10−4 2=k+1
10−4 2=k+1

10−6
10−6 3
3

10−8 k =1
10−8
k =1
k =2 4 k =2
4
k =3 k =3
k =4 k =4
10−10 10−10
10−2 10−1 10−2 10−1
h h

|uh − πh (u)|1,2,Ω

10−2 1=k

10−4
2

10−6 k =1
3 k =2
k =3
k =4
10−8
10−2 10−1
h

Figure 1.9: Curved domains (quadrangles): error analysis in L2 , L∞ and H 1 norms.


32 Rheolef version 7.2

1.2 Non-homogeneous Neumann boundary conditions for


the Helmholtz operator
Formulation
1
Let us show how to insert Neumann boundary conditions. Let f ∈ H −1 (Ω) and g ∈ H − 2 (∂Ω).
The problem writes:
(P3 ): find u, defined in Ω such that:

u − ∆u = f in Ω
∂u
= g on ∂Ω
∂n

The variational formulation of this problem expresses:


(V F3 ): find u ∈ H 1 (Ω) such that:

a(u, v) = l(v), ∀v ∈ H 1 (Ω)

where
Z
a(u, v) = (u v + ∇u.∇v) dx
ZΩ Z
l(v) = f v dx + g v ds
Ω ∂Ω

Approximation

As usual, we introduce a mesh Th of Ω and the finite dimensional space Xh :

Xh = {v ∈ H 1 (Ω); v/K ∈ Pk , ∀K ∈ Th }

The approximate problem writes:


(V F3 )h : find uh ∈ Xh such that:

a(uh , vh ) = l(vh ), ∀vh ∈ Xh

File 1.8: neumann-nh.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sinusprod_helmholtz . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 size_t d = omega . dimension ();
9 space Xh ( omega , argv [2]);
10 trial u ( Xh ); test v ( Xh );
11 form a = integrate ( u * v + dot ( grad ( u ) , grad ( v )));
12 field lh = integrate ( f ( d )* v ) + integrate ( " boundary " , g ( d )* v );
13 field uh ( Xh );
14 problem p ( a );
15 p . solve ( lh , uh );
16 dout << uh ;
17 }
Chapter 1. Getting started 33

Let us choose Ω ⊂ Rd , d = 1, 2, 3 and


d−1
Y
f (x) = (1 + dπ 2 ) sin(πxi )
i=0
when d = 1


 −π !

 d−1
X
when d = 2

 −π

sin(πxi )
g(x) = i=0 !


 d−1
X
 −π sin(πxi ) sin(x(i+1)mod d when d = 3



i=0

This example is convenient, since the exact solution is known:


d−1
Y
u(x) = sin(πxi ) (1.5)
i=0

File 1.9: sinusprod_helmholtz.h


1 struct f {
2 Float operator () ( const point & x ) const {
3 switch ( d ) {
4 case 1: return (1+ d * pi * pi )* sin ( pi * x [0]);
5 case 2: return (1+ d * pi * pi )* sin ( pi * x [0])* sin ( pi * x [1]);
6 default : return (1+ d * pi * pi )* sin ( pi * x [0])* sin ( pi * x [1])* sin ( pi * x [2]);
7 }}
8 f ( size_t d1 ) : d ( d1 ) , pi ( acos ( Float ( -1.0))) {}
9 size_t d ; const Float pi ;
10 };
11 struct g {
12 Float operator () ( const point & x ) const {
13 switch ( d ) {
14 case 1: return - pi ;
15 case 2: return - pi *( sin ( pi * x [0]) + sin ( pi * x [1]));
16 default : return - pi *( sin ( pi * x [0])* sin ( pi * x [1])
17 + sin ( pi * x [1])* sin ( pi * x [2])
18 + sin ( pi * x [2])* sin ( pi * x [0]));
19 }}
20 g ( size_t d1 ) : d ( d1 ) , pi ( acos ( Float ( -1.0))) {}
21 size_t d ; const Float pi ;
22 };

Comments

The neumann-nh.cc code looks like the previous one dirichlet-nh.cc. Let us comments only
the changes.
form a = integrate ( u * v + dot ( grad ( u ) , grad ( v )));

The bilinear form a(., .) is defined. Notes the flexibility of the integrate function that takes as
argument an expression involving the trial and test functions. The right-hand side is computed
as:
field lh = integrate ( f ( d )* v ) + integrate ( " boundary " , g ( d )* v );

The second integration is performed on ∂Ω. The additional first argument of the integrate
function is here the name of the integration domain.

How to run the program

First, compile the program:


34 Rheolef version 7.2

make neumann-nh

Running the program is obtained from the homogeneous Dirichlet case, by replacing dirichlet
by neumann-nh.

1.3 The Robin boundary conditions


Formulation
1
Let f ∈ H −1 (Ω) and Let g ∈ H 2 (∂Ω). The problem writes:
(P4 ) find u, defined in Ω such that:

−∆u = f in Ω
∂u
+u = g on ∂Ω
∂n
The variational formulation of this problem expresses:
(V F4 ): find u ∈ H 1 (Ω) such that:

a(u, v) = l(v), ∀v ∈ H 1 (Ω)

where
Z Z
a(u, v) = ∇u.∇v dx + uv ds
ZΩ Z ∂Ω

l(v) = f v dx + gv ds
Ω ∂Ω

Approximation

As usual, let
Xh = {v ∈ H 1 (Ω); v/K ∈ Pk , ∀K ∈ Th }
The approximate problem writes:
(V F4 )h : find uh ∈ Xh such that:

a(uh , vh ) = l(vh ), ∀vh ∈ Xh

File 1.10: robin.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cosinusprod_laplace . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 size_t d = omega . dimension ();
9 space Xh ( omega , argv [2]);
10 trial u ( Xh ); test v ( Xh );
11 form a = integrate ( dot ( grad ( u ) , grad ( v ))) + integrate ( " boundary " , u * v );
12 field lh = integrate ( f ( d )* v ) + integrate ( " boundary " , g ( d )* v );
13 field uh ( Xh );
14 problem p ( a );
15 p . solve ( lh , uh );
16 dout << uh ;
17 }
Chapter 1. Getting started 35

Comments

The code robin.cc looks like the previous one neumann-nh.cc. Let us comments the changes.
form a = integrate ( dot ( grad ( u ) , grad ( v ))) + integrate ( " boundary " , u * v );

This statement reflects directly the definition of the bilinear form a(., .), as the sum of two integrals,
the first one over Ω and the second one over its boundary.

How to run the program

First, compile the program:


make robin
Running the program is obtained from the homogeneous Dirichlet case, by replacing dirichlet
by robin.

1.4 Neumann boundary conditions for the Laplace operator


In this chapter we study how to solve a ill-posed problem with a solution defined up to a constant.

Formulation

Let Ω be a bounded open and simply connected subset of Rd , d = 1, 2 or 3. Let f ∈ L2 (Ω) and
1
g ∈ H 2 (∂Ω) satisfying the following compatibility condition:
Z Z
f dx + g ds = 0
Ω ∂Ω
The problem writes:
(P5 )h : find u, defined in Ω such that:
−∆u = f in Ω
∂u
= g on ∂Ω
∂n
Since this problem only involves the derivatives of u, it is defined up to a constant: it is then
clear that its solution is never unique [Girault and Raviart, 1986, p. 11]. A discrete version of this
problem could be solved iteratively by the conjugate gradient or the MINRES algorithm [Paige
and Saunders, 1975]. In order to solve it by a direct method, we get round this difficulty by
seeking u in the following space
V = {v ∈ H 1 (Ω); b(v, 1) = 0}
where Z
b(v, µ) = µ v dx, ∀v ∈ L2 (Ω), ∀µ ∈ R

The variational formulation of this problem writes:
(V F5 ): find u ∈ V such that:
a(u, v) = ℓ(v), ∀v ∈ V
where
Z
a(u, v) = ∇u.∇v dx
ZΩ
ℓ(v) = f v dx

Z
+ gv ds
∂Ω
36 Rheolef version 7.2

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, defined for all
v ∈ H 1 (Ω) and µ ∈ R by:
1
L(v, µ) = a(v, v) + b(v, µ) − ℓ(v)
2
The saddle point (u, λ) ∈ H 1 (Ω) × R of this Lagrangian is characterized as the unique solution of:
a(u, v) + b(v, λ) = ℓ(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 F5 ).
Conversely, let u ∈ V the solution of (V F5 ). Choosing v = v0 where v0 (x) = 1, ∀x ∈ Ω leads to
λ meas(Ω) = ℓ(v0 ). From the definition of ℓ(.) and the compatibility condition between the data
f and g, we get λ = 0. Note that the saddle point problem extends to the case when f and g does
not satisfies the compatibility condition, and in that case λ = ℓ(v0 )/meas(Ω).

Approximation

As usual, we introduce a mesh Th of Ω and the finite dimensional space Xh :


Xh = {v ∈ H 1 (Ω); v/K ∈ Pk , ∀K ∈ Th }
The approximate problem writes:
(V F5 )h : find (uh , λh ) ∈ Xh × R such that:
a(uh , v) + b(v, λh ) = ℓ(v), ∀v ∈ Xh
b(uh , µ) = 0, ∀µ ∈ R

File 1.11: neumann-laplace.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 size_t d ;
5 Float f ( const point & x ) { return 1; }
6 Float g ( const point & x ) { return -0.5/ d ; }
7 int main ( int argc , char ** argv ) {
8 environment rheolef ( argc , argv );
9 geo omega ( argv [1]);
10 d = omega . dimension ();
11 space Xh ( omega , argv [2]);
12 trial u ( Xh ); test v ( Xh );
13 form a = integrate ( dot ( grad ( u ) , grad ( v )));
14 field b = integrate ( v );
15 field lh = integrate ( f * v ) + integrate ( " boundary " , g * v );
16 form A = {{ a, b},
17 { trans ( b ) , 0}};
18 field Bh = { lh , 0};
19 field Uh ( Bh . get_space () , 0);
20 A . set_symmetry ( true );
21 problem p ( A );
22 p . solve ( Bh , Uh );
23 dout << Uh [0];
24 }

Comments

Let Ω ⊂ Rd , d = 1, 2, 3. We choose f (x) = 1 and g(x) = −1/(2d). This example is convenient,


since the exact solution is known:
d
1 1 X
u(x) = − + xi (1 − xi )
12 2d i=1
Chapter 1. Getting started 37

The code looks like the previous ones. Let us comment the changes. The discrete bilinear form
b is computed as b ∈ Xh that interprets as a linear application from Xh to R: b(vh ) = m(vh , 1).
Thus b is computed as
field b = integrate ( v );

Let      
a trans(b) uh lh
A= , U= , B=
b 0 lambda 0

The problem admits the following matrix form:

AU =B

The matrix that represents the bilinear fo and its right-hand side are assembled as:
form A = {{ a, b},
{ trans ( b ) , 0}};
field Bh = { lh , 0};

Both the pairs U = (uh , λ) and B = (b, 0) belong to the vectorial space Xh × R. and B = (b, 0)
belong to the Then, the variable Uh could be declared as:
field Uh ( Bh . get_space () , 0);

Note that the matrix A is symmetric and non-singular, but indefinite : it admits eigenvalues that
are either strictly positive or strictly negative. While the Choleski factorization is not possible, its
variant the LDLT one is performed, thanks to the ldlt function:
A . set_symmetry ( true );
problem p ( A );
p . solve ( Bh , Uh );

Then, the uh field is extracted as the first component of the the Uh one:
dout << Uh [0];

How to run the program

As usual, enter:

make neumann-laplace
mkgeo_grid -t 10 > square.geo
./neumann-laplace square P1 | field -

1.5 Non-constant coefficients and multi-regions


This section is related to the so-called transmission problem. We introduce some new concepts:
problems with non-constant coefficients, regions in the mesh, weighted forms and discontinuous
approximations.

Formulation
Let us consider a diffusion problem with a non-constant diffusion coefficient η in a domain bounded
Ω ⊂ Rd , d = 1, 2, 3:
38 Rheolef version 7.2

(P ): find u defined in Ω such that:


−div(η∇u) = f in Ω (1.6)
u = 0 on Γleft ∪ Γright (1.7)
∂u
= 0 on Γtop ∪ Γbottom when d ⩾ 2 (1.8)
∂n
∂u
= 0 on Γfront ∪ Γback when d = 3 (1.9)
∂n
where f is a given source term. We consider here the important special case when η is piecewise
west
east
bottom
right
top
left

Figure 1.10: Transmission problem: the domain Ω partition: (Ωwest and Ωeast ).

constant:
when x ∈ Ωwest

ε
η(x) =
1 when x ∈ Ωeast
where (Ωwest , Ωeast ) is a partition of Ω in two parts (see Fig. 1.10). This is the so-called trans-
mission problem: the solution and the flux are continuous on the interface Γ0 = ∂Ωeast ∩ ∂Ωwest
between the two domains where the problem reduce to a constant diffusion one:
uΩwest = uΩeast on Γ0
∂u/Ωwest ∂uΩeast
ε = on Γ0
∂n ∂n

It expresses the transmission of the quantity u and its flux across the interface Γ0 between two
regions that have different diffusion properties: Note that the more classical problem, with constant
diffusion η on Ω is obtained by simply choosing when ε = 1.
The variational formulation of this problem expresses:
(V F ): find u ∈ V such that:
a(u, v) = l(v), ∀v ∈ V
where the bilinear forms a(., .) and the linear one l(.) are defined by
Z
a(u, v) = η ∇u.∇v dx, ∀u, v ∈ H 1 (Ω)

Z
l(v) = f v dx, ∀v ∈ L2 (Ω)

V = {v ∈ H 1 (Ω); v = 0 on Γleft ∪ Γright }
Chapter 1. Getting started 39

The bilinear form a(., .) defines a scalar product in V and is related to the energy form. This form
is associated to the −div η∇ operator.
The approximation of this problem could performed by a standard Lagrange Pk continuous ap-
proximation.

File 1.12: transmission.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 geo omega ( argv [1]);
7 string approx = ( argc > 2) ? argv [2] : " P1d " ;
8 Float epsilon = ( argc > 3) ? atof ( argv [3]) : 1e -2;
9 space Xh ( omega , approx );
10 Xh . block ( " left " );
11 Xh . block ( " right " );
12 string eta_approx = " P " + to_string ( Xh . degree () -1) + " d " ;
13 space Qh ( omega , eta_approx );
14 field eta_h ( Qh );
15 eta_h [ " east " ] = 1;
16 eta_h [ " west " ] = epsilon ;
17 trial u ( Xh ); test v ( Xh );
18 form a = integrate ( eta_h * dot ( grad ( u ) , grad ( v )));
19 field lh = integrate ( v );
20 field uh ( Xh );
21 uh [ " left " ] = 0; uh [ " right " ] = 0;
22 problem p ( a );
23 p . solve ( lh , uh );
24 dout << catchmark ( " epsilon " ) << epsilon << endl
25 << catchmark ( " u " ) << uh ;
26 }

Comments
This file is quite similar to those studied in the first sections of this book. Let us comment only
the changes. The Dirichlet boundary condition applies no more on the whole boundary ∂Ω but
on two parts Γleft and Γright . On the other boundary parts, an homogeneous Neumann boundary
condition is used: since these conditions does not produce any additional terms in the variational
formulation, there are also nothing to write in the C++ code for these boundaries. We choose
f = 1: this leads to a convenient test-problem, since the exact solution is known when Ω =]0, 1[d :
  
x0 1 + 3ε
− x when x0 < 1/2

0

 2ε 2(1 + ε)


u(x) =  
 1 − x0 1−ε
otherwise

x +

0

2 2(1 + ε)

The field η belongs to a discontinuous finite element Pk−1 space denoted by Qh :


string eta_approx = " P " + to_string ( Xh . degree () -1) + " d " ;
space Qh ( omega , eta_approx );
field eta ( Qh );

For instance, when argv[2] contains "P2", i.e. k = 2, then the string eta_approx takes value
"P1d". Then η is initialized by:
eta [ " east " ] = 1;
eta [ " weast " ] = epsilon ;

The energy form a is then constructed with η as additional parameter that acts as a integration
weight:
form a = integrate ( eta_h * dot ( grad ( u ) , grad ( v )));
40 Rheolef version 7.2

Such forms with a additional weight function are called weighted forms in Rheolef.

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.geo


geo line.geo

The trivial mesh generator mkgeo_grid, defines two regions east and west when used with the
-region option. This correspond to the situation:

Ω = [0, 1]d , Ωwest = Ω ∩ {x0 < 1/2} and Ωeast = Ω ∩ {x0 > 1/2}.

In order to avoid mistakes with the C++ style indexes, we denote by (x0 , . . . , xd−1 ) the Cartesian
coordinate system in Rd .
Finally, run the program and look at the solution:

make transmission
./transmission line.geo P1 > line.field
field line.field

Since the exact solution is a piecewise second order polynomial and the change in the diffusion
coefficient value fits the element boundaries, we obtain the exact solution for all the degrees of
freedom of any Pk approximation, k ⩾ 1, as shown on Fig. 1.11 when k = 1. Moreover, when
k ⩾ 2 then uh = u since Xh contains the exact solution u. The two dimensional case corresponds

exact
h = 1/6
3 h = 1/10
h = 1/14

0
0 0.25 0.5 0.75 1

Figure 1.11: Transmission problem with ε = 10−2 , d = 1, P1 approximation.

to the commands:

mkgeo_grid -t 10 -region > square.geo


geo square.geo
./transmission square.geo P1 > square.field
field square.field -elevation
Chapter 1. Getting started 41

while the tridimensional to

mkgeo_grid -T 10 -region > cube.geo


./transmission cube.geo P1 > cube.field
field cube.field

As for all the others examples, you can replace P1 by higher-order approximations, change elements
shapes, such as q, H or P, and run distributed computations computations with mpirun.
42 Rheolef version 7.2
Chapter 2

Fluids and solids computations

2.1 The linear elasticity and the Stokes problems


2.1.1 The linear elasticity problem
Formulation

The total Cauchy stress tensor expresses:

σ(u) = λ div(u).I + 2µD(u) (2.1)

where λ and µ are the Lamé coefficients. Here, D(u) denotes the symmetric part of the gradi-
ent operator and div is the divergence operator. Let us consider the elasticity problem for the
embankment, in Ω =]0, 1[d , d = 2, 3. The problem writes:
(P ): find u = (u0 , . . . , ud−1 ), defined in Ω, such that:

− div σ(u) = f in Ω,
∂u
= 0 on Γtop ∪ Γright
∂n (2.2)
u = 0 on Γleft ∪ Γbottom ,
u = 0 on Γfront ∪ Γback , when d = 3

where f = (0, −1) when d = 2 and f = (0, 0, −1) when d = 3. The Lamé coefficients are assumed
to satisfy µ > 0 and λ + µ > 0. Since the problem is linear, we can suppose that µ = 1 without
any loss of generality. recall that, in order to avoid mistakes with the C++ style indexes, we denote
by (x0 , . . . , xd−1 ) the Cartesian coordinate system in Rd .
For d = 2 we define the boundaries:
Γleft = {0}×]0, 1[, Γright = {1}×]0, 1[
Γbottom = ]0, 1[×{0}, Γtop = ]0, 1[×{1}

and for d = 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. 2.1.


The variational formulation of this problem expresses:
(V F ): find u ∈ V such that:
a(u, v) = l(v), ∀v ∈ V, (2.3)

43
44 Rheolef version 7.2

x1 x2
top
top
back
left right
left right
front
bottom x0 x1
bottom
x0
Figure 2.1: The boundary domains for the square and the cube.

where

Z
a(u, v) = (λdiv u div v + 2D(u) : D(v)) dx,

Z
l(v) = f .v dx,

V = {v ∈ (H 1 (Ω))2 ; v = 0 on Γleft ∪ Γbottom }, when d = 2
V = {v ∈ (H 1 (Ω))3 ; v = 0 on Γleft ∪ Γbottom ∪ Γright ∪ Γback }, when d = 3

Approximation

We introduce a mesh Th of Ω and for k ⩾ 1, the following finite dimensional spaces:

Xh = {vh ∈ (H 1 (Ω))d ; vh/K ∈ (Pk )d , ∀K ∈ Th },


Vh = Xh ∩ V

The approximate problem writes:


(V F )h : find uh ∈ Vh such that:

a(uh , vh ) = l(vh ), ∀vh ∈ Vh


Chapter 2. Fluids and solids computations 45

File 2.1: embankment.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " embankment . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh = embankment_space ( omega , argv [2]);
9 Float lambda = ( argc > 3) ? atof ( argv [3]) : 1;
10 size_t d = omega . dimension ();
11 point f (0 ,0 ,0);
12 f [d -1] = -1;
13 trial u ( Xh ); test v ( Xh );
14 form a = integrate ( lambda * div ( u )* div ( v ) + 2* ddot ( D ( u ) , D ( v )));
15 field lh = integrate ( dot (f , v ));
16 field uh ( Xh , 0);
17 problem p ( a );
18 p . solve ( lh , uh );
19 dout << catchmark ( " inv_lambda " ) << 1/ lambda << endl
20 << catchmark ( " u " ) << uh ;
21 }

File 2.2: embankment.icc


1 space embankment_space ( const geo & omega , string approx ) {
2 space Xh ( omega , approx , " vector " );
3 Xh . block ( " left " );
4 if ( omega . dimension () >= 2)
5 Xh . block ( " bottom " );
6 if ( omega . dimension () == 3) {
7 Xh . block ( " right " );
8 Xh . block ( " back " );
9 }
10 return Xh ;
11 }

Comments

The space is defined in a separate file ‘embankment.icc’, since it will be reused in others examples
along this chapter:
space Vh ( omega , " P2 " , " vector " );

Note here the multi-component specification "vector" as a supplementary argument to the space
constructor. The boundary condition contain a special cases for bi- and tridimensional cases. The
right-hand-side fh represents the dimensionless gravity forces, oriented on the vertical axis: the
last component of fh is set to −1 as:
fh [d -1] = -1;

The code for the bilinear form a(., .) and the linear one l(.) are closed to their mathematical
definitions:
form a = integrate ( lambda * div ( u )* div ( v ) + 2* ddot ( D ( u ) , D ( v )));
field lh = integrate ( dot (f , v ));

Finally, the 1/λ parameter and the multi-field 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.

How to run the program

Compile the program as usual (see page 16):


46 Rheolef version 7.2

Figure 2.2: The linear elasticity for λ = 1 and d = 2 and d = 3: both wireframe and filled surfaces
; stereoscopic anaglyph mode for 3D solutions.

make embankment

and enter the commands:

mkgeo_grid -t 10 > square.geo


geo square.geo

The triangular mesh has four boundary domains, named left, right, top and bottom. Then,
enter:

./embankment square.geo P1 > square-P1.field

The previous command solves the problem for the corresponding mesh and writes the multi-
component solution in the ‘.field’ file format. Run the deformation vector field visualization:
Chapter 2. Fluids and solids computations 47

field square-P1.field
field square-P1.field -nofill

It bases on the default paraview render for 2D and 3D geometries. The view is shown on Fig. 2.2.
If you are in trouble with paraview, you can switch to the simpler gnuplot render:

field square-P1.field -nofill -gnuplot

The unix manual for the field command is available as:

man field

A specific field component can be also selected for a scalar visualization:

field -comp 0 square-P1.field


field -comp 1 square-P1.field

Next, perform a P2 approximation of the solution:

./embankment square.geo P2 > square-P2.field


field square-P2.field -nofill

Finally, let us consider the three dimensional case

mkgeo_grid -T 10 > cube.geo


./embankment cube.geo P1 > cube-P1.field
field cube-P1.field -stereo
field cube-P1.field -stereo -fill

The two last commands show the solution in 3D stereoscopic anaglyph mode. The graphic is
represented on Fig. 2.2. The P2 approximation writes:

./embankment cube.geo P2 > cube-P2.field


field cube-P2.field

2.1.2 Computing the stress tensor


Formulation and approximation

The following code computes the total Cauchy stress tensor, reading the Lamé coefficient λ and
the deformation field uh from a ‘.field’ file. Let us introduce:

Th = {τh ∈ (L2 (Ω))d×d ; τh = τhT and τh;ij/K ∈ Pk−1 , ∀K ∈ Th , 1 ⩽ i, j ⩽ d}

This computation expresses:


find σh such that:
m(σh , τ ) = b(τ, uh ), ∀τ ∈ Th
where
Z
m(σ, τ ) = σ : τ dx,
ZΩ
b(τ, u) = (2D(u) : τ dx + λdiv(u) tr(τ )) dx,

Pd
where tr(τ ) = i=1 τii is the trace of the tensor τ .
48 Rheolef version 7.2

File 2.3: stress.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 Float inv_lambda ;
7 field uh ;
8 din >> catchmark ( " inv_lambda " ) >> inv_lambda
9 >> catchmark ( " u " ) >> uh ;
10 const geo & omega = uh . get_geo ();
11 const space & Xh = uh . get_space ();
12 string grad_approx = " P " + to_string ( Xh . degree () -1) + " d " ;
13 space Th ( omega , grad_approx , " tensor " );
14 size_t d = omega . dimension ();
15 tensor I = tensor :: eye ( d );
16 field sigma_h ;
17 if ( inv_lambda == 0)
18 sigma_h = lazy_interpolate ( Th , 2* D ( uh ));
19 else sigma_h = lazy_interpolate ( Th , 2* D ( uh ) + (1/ inv_lambda )* div ( uh )* I );
20 dout << catchmark ( " s " ) << sigma_h ;
21 }

Comments

In order to our code stress.cc to apply also for the forthcoming incompressible case λ = +∞,
the Lamé coefficient is introduced as 1/λ. Its value is zero in the incompressible case. By this
way, the previous code applies for any deformation field, and is not restricted to our embankment
problem. The stress tensor is obtained by a direct interpolation of the uh first derivatives. As
uh is continuous and piecewise polynomial Pk , its derivatives are also piecewise polynomials with
degree k − 1, but discontinuous at inter-element boundaries : this approximation is denoted as
Pk−1,d . Thus, the stress tensor belongs to the space Th with the Pk−1,d element.

How to run the program

Figure 2.3: The stress tensor visualization (linear elasticity λ = 1).

First, compile the program:

make stress
Chapter 2. Fluids and solids computations 49

The visualization for the stress tensor as ellipses writes:

./stress < square-P1.field > square-stress-P1.field


./stress < square-P2.field > square-stress-P2.field
field square-stress-P1.field -proj
field square-stress-P2.field -proj

This visualization based on paraview requires the TensorGlyph feature, available since
paraview-5.0. 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
tensor field is projected as a continuous piecewise linear space, using the -proj option. Conversely,
the 3D visualization bases on ellipsoids:

./stress < cube-P1.field > cube-stress-P1.field


field cube-stress-P1.field -proj -stereo

Figure 2.4: The σ01 stress component (linear elasticity λ = 1): d = 2 (top) and d = 3 (bottom) ;
P0 (left) and P1 discontinuous approximation (right).

You can observe a discontinuous constant or piecewise linear representation of the approximate
stress component σ01 (see Fig. 2.4):
50 Rheolef version 7.2

field square-stress-P1.field -comp 01


field square-stress-P2.field -comp 01 -elevation
field square-stress-P2.field -comp 01 -elevation -stereo

Note that the -stereo implies the paraview render: this feature available with paraview. The
approximate stress field can be also projected on a continuous piecewise space:

field square-stress-P2.field -comp 01 -elevation -proj

The tridimensional case writes simply (see Fig. 2.4):

./stress < cube-P1.field > cube-stress-P1.field


./stress < cube-P2.field > cube-stress-P2.field
field cube-stress-P1.field -comp 01 -stereo
field cube-stress-P2.field -comp 01 -stereo

and also the P1-projected versions write:

field cube-stress-P1.field -comp 01 -stereo -proj


field cube-stress-P2.field -comp 01 -stereo -proj

These operations can be repeated for each σij components and for both P1 and P2 approximation
of the deformation field.

2.1.3 Mesh adaptation

The main principle of the auto-adaptive mesh writes [Hecht, 2006, Borouchaki et al., 1995, Castro-
Diaz et al., 1997, Vallet, 1990, Roquet et al., 2000]:

din >> omega;


uh = solve(omega);
for (unsigned int i = 0; i < n; i++) {
ch = criterion(uh);
omega = adapt(ch);
uh = solve(omega);
}

The initial mesh is used to compute a first solution. The adaptive loop compute an adaptive
criterion, denoted by ch, that depends upon the problem under consideration and the polynomial
approximation used. Then, a new mesh is generated, based on this criterion. A second solution
on an adapted mesh can be constructed. The adaptation loop converges generally in roughly 5 to
20 iterations.
Let us apply this principle to the elasticity problem.
Chapter 2. Fluids and solids computations 51

File 2.4: embankment_adapt.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " elasticity_solve . icc "
5 # include " elasticity_criterion . icc "
6 # include " embankment . icc "
7 int main ( int argc , char ** argv ) {
8 environment rheolef ( argc , argv );
9 const Float lambda = 1;
10 geo omega ( argv [1]);
11 adapt_option options ;
12 string approx = ( argc > 2) ? argv [2] : " P1 " ;
13 options . err = ( argc > 3) ? atof ( argv [3]) : 5e -3;
14 size_t n_adapt = ( argc > 4) ? atoi ( argv [4]) : 5;
15 options . hmin = 0.004;
16 for ( size_t i = 0; true ; i ++) {
17 space Xh = embankment_space ( omega , approx );
18 field uh = elasticity_solve ( Xh , lambda );
19 odiststream of ( omega . name () , " field " );
20 of << catchmark ( " lambda " ) << lambda << endl
21 << catchmark ( " u " ) << uh ;
22 if ( i == n_adapt ) break ;
23 field ch = elasticity_criterion ( lambda , uh );
24 omega = adapt ( ch , options );
25 odiststream og ( omega . name () , " geo " );
26 og << omega ;
27 }
28 }

File 2.5: elasticity_solve.icc


1 field elasticity_solve ( const space & Xh , Float lambda ) {
2 size_t d = Xh . get_geo (). dimension ();
3 point f (0 ,0 ,0);
4 f [d -1] = -1;
5 trial u ( Xh ); test v ( Xh );
6 field lh = integrate ( dot (f , v ));
7 form a = integrate ( lambda * div ( u )* div ( v ) + 2* ddot ( D ( u ) , D ( v )));
8 field uh ( Xh , 0);
9 problem p ( a );
10 p . solve ( lh , uh );
11 return uh ;
12 }

File 2.6: elasticity_criterion.icc


1 field elasticity_criterion ( Float lambda , const field & uh ) {
2 string grad_approx = " P " + to_string ( uh . get_space (). degree () -1) + " d " ;
3 space Xh ( uh . get_geo () , grad_approx );
4 if ( grad_approx == " P0d " ) return lazy_interpolate ( Xh , norm ( uh ));
5 space T0h ( uh . get_geo () , grad_approx );
6 size_t d = uh . get_geo (). dimension ();
7 tensor I = tensor :: eye ( d );
8 return lazy_interpolate ( T0h , sqrt (2* norm2 ( D ( uh )) + lambda * sqr ( div ( uh ))));
9 }

Comments

The criterion is here:


when using P1

|uh |
ch =
(σ(uh ) : D(uh ))1/2 when using P2
The elasticity_criterion function compute it as
return interpolate ( Xh , norm ( uh ));

when using P1 , and as


52 Rheolef version 7.2

return interpolate ( T0h , sqrt (2* norm2 ( D ( uh )) + lambda * sqr ( div ( uh ))));

when using P2 . The sqr function returns the square of a scalar. Conversely, the norm2 function
returns the square of the norm. In the min program, the result of the elasticity_criterion
function is send to the adapt function:
field ch = elasticity_criterion ( lambda , uh );
omega = adapt ( ch , options );

The adapt_option declaration is used by Rheolef to send options to the mesh generator. The
err parameter controls the error via 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.

How to run the program

P1 : 6771 elements, 3663 vertices P2 : 1636 elements, 920 vertices

Figure 2.5: Adapted meshes: the deformation visualization for P1 and P2 approximations.

The compilation command writes:

make embankment_adapt

The mesh loop adaptation is initiated from a bamg mesh (see also appendix A.2.1).

bamg -g square.bamgcad -o square.bamg


bamg2geo square.bamg square.dmn > square.geo
./embankment_adapt square P1 2e-2

The last command line argument is the target error. The code performs a loop of five
mesh adaptations: the corresponding meshes are stored in files, from square-001.geo.gz
to square-005.geo.gz, and the associated solutions in files, from square-001.field.gz to
square-005.field.gz. The additional ‘.gz’ suffix expresses that the files are compressed us-
ing gzip.

geo square-005.geo
field square-005.field -nofill
Chapter 2. Fluids and solids computations 53

Note that the ‘.gz’ suffix is automatically assumed by the geo and the field commands.
For a piecewise quadratic approximation:

./embankment_adapt square P2 5e-3

Then, the visualization writes:

geo square-005.geo
field square-005.field -nofill

A one-dimensional mesh adaptive procedure is also possible:

gmsh -1 line.mshcad -format msh2 -o line.msh


msh2geo line.msh > line.geo
geo line.geo
./embankment_adapt line P2 1e-3
geo line-005.geo
field line-005.field -comp 0 -elevation

The three-dimensional extension of this mesh adaptive procedure is in development.

2.1.4 The Stokes problem


Formulation

Let us consider the Stokes problem for the driven cavity in Ω =]0, 1[d , d = 2, 3. The problem
writes:
(S) find u = (u0 , . . . , ud−1 ) and p defined in Ω such that:

− div(2D(u)) + ∇p = 0 in Ω,
− div u = 0 in Ω,
u = (1, 0) on Γtop ,
u = 0 on Γleft ∪ Γright ∪ Γbottom ,
∂u0 ∂u1
= = u2 = 0 on Γback ∪ Γfront when d = 3,
∂n ∂n
where D(u) = (∇u + ∇uT )/2. The boundaries are represented on Fig. 2.1, page 44.
The variational formulation of this problem expresses:
(V F S) find u ∈ V(1) and p ∈ L20 (Ω) such that:

a(u, v) + b(v, p) = 0, ∀v ∈ V(0),


b(u, q) = 0, ∀q ∈ L20 (Ω),

where
Z
a(u, v) = 2D(u) : D(v) dx,

Z
b(v, q) = − div(v) q dx.

V(α) = {v ∈ (H 1 (Ω))2 ; v = 0 on Γleft ∪ Γright ∪ Γbottom and v = (α, 0) on Γtop }, when d = 2,
V(α) = {v ∈ (H 1 (Ω))3 ; v = 0 on Γleft ∪ Γright ∪ Γbottom ,
v = (α, 0, 0) on Γtop and v2 = 0 on Γback ∪ Γfront }, when d = 3,
Z
L20 (Ω) = {q ∈ L2 (Ω); q dx = 0}.

54 Rheolef version 7.2

Approximation

The Hood and Taylor [1973] finite element approximation of the Stokes problem is considered. We
introduce a mesh Th of Ω and the following finite dimensional spaces:

Xh = {v ∈ (H 1 (Ω))d ; v/K ∈ (P2 )d , ∀K ∈ Th },


Vh (α) = Xh ∩ V(α),
Qh = {q ∈ L2 (Ω)) ∩ C 0 (Ω̄); q/K ∈ P1 , ∀K ∈ Th },

The approximate problem writes:


(V F S)h find uh ∈ Vh (1) and p ∈ Qh such that:

a(uh , v) + b(v, ph ) = 0, ∀v ∈ Vh (0),


(2.4)
b(uh , q) = 0, ∀q ∈ Qh .

File 2.7: cavity.h


1 struct cavity {
2 static space velocity_space ( const geo & omega , string approx ) {
3 space Xh ( omega , approx , " vector " );
4 Xh . block ( " top " ); Xh . block ( " bottom " );
5 if ( omega . dimension () == 3) {
6 Xh . block ( " back " ); Xh . block ( " front " );
7 Xh [1]. block ( " left " ); Xh [1]. block ( " right " );
8 } else {
9 Xh . block ( " left " ); Xh . block ( " right " );
10 }
11 return Xh ;
12 }
13 static field velocity_field ( const space & Xh , Float alpha =1) {
14 field uh ( Xh , 0.);
15 uh [0][ " top " ] = alpha ;
16 return uh ;
17 }
18 static space streamf_space ( geo omega , string approx ) {
19 string valued = ( omega . dimension () == 3) ? " vector " : " scalar " ;
20 space Ph ( omega , approx , valued );
21 Ph . block ( " top " ); Ph . block ( " bottom " );
22 if ( omega . dimension () == 3) {
23 Ph . block ( " back " ); Ph . block ( " front " );
24 } else {
25 Ph . block ( " left " ); Ph . block ( " right " );
26 }
27 return Ph ;
28 }
29 static field streamf_field ( space Ph ) {
30 return field ( Ph , 0);
31 }
32 };
Chapter 2. Fluids and solids computations 55

File 2.8: stokes_cavity.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cavity . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh = cavity :: velocity_space ( omega , " P2 " );
9 space Qh ( omega , " P1 " );
10 trial u ( Xh ); test v ( Xh ) , q ( Qh );
11 form a = integrate (2* ddot ( D ( u ) , D ( v )));
12 form b = integrate ( - div ( u )* q );
13 field uh = cavity :: velocity_field ( Xh , 1);
14 field ph ( Qh , 0);
15 problem_mixed stokes (a , b );
16 stokes . solve ( field ( Xh ,0) , field ( Qh ,0) , uh , ph );
17 dout << catchmark ( " u " ) << uh
18 << catchmark ( " p " ) << ph ;
19 }

Comments

The spaces and boundary conditions and grouped in specific functions, defined in file ‘cavity.h’.
This file is suitable for a future re-usage. Next, forms are defined as usual, in file
‘stokes_cavity.cc’.
The problem admits the following matrix form:
    
a trans(b) uh 0
=
b 0 ph 0

An initial value for the pressure field is provided:


field ph ( Qh , 0);

The solve call to the Stokes problem writes:


problem_mixed stokes (a , b );
stokes . solve ( field ( Xh ,0) , field ( Qh ,0) , uh , ph );

The two first arguments of the solve member function represents the zero right-hand-side of
the problem. For two-dimensional geometries (d = 2), this system is solved by a direct method
(see Saramito, 2016b, p. 41). Conversely, for tridimensional geometries (d = 3), it is solved by
the preconditioned conjugate gradient algorithm (see Saramito, 2016b, p. 56). In that case, the
preconditioner is by default the mass matrix mp for the pressure space: as showed by Klawonn
[1998], 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 on the web site and via the unix command:

man problem_mixed

How to run the program

We assume that the previous code is contained in the file ‘stokes_cavity.cc’. Then, compile the
program as usual (see page 16):

make stokes_cavity

and enter the commands:


56 Rheolef version 7.2

Figure 2.6: The velocity visualization for d = 2 and d = 3 with stereo anaglyph.

mkgeo_grid -t 10 > square.geo


./stokes_cavity square > square.field

The previous command solves the problem for the corresponding mesh and writes the solution in
a ‘.field’ file. Run the velocity vector visualization :

field square.field -velocity

Run also some scalar visualizations:

field square.field -comp 0


field square.field -comp 1
field square.field -mark p

Note the -mark option to the field command: the file reader jumps to the label and then starts
to read the selected field. Next, perform another computation on a finer mesh:

mkgeo_grid -t 20 > square-20.geo


./stokes_cavity square-20.geo > square-20.field

and observe the convergence.


Finally, let us consider the three dimensional case:

mkgeo_grid -T 5 > cube.geo


./stokes_cavity cube.geo > cube.field

and the corresponding visualization:

field cube.field -velocity -scale 3


field cube.field -comp 0
field cube.field -comp 1
field cube.field -comp 2
field cube.field -mark p
Chapter 2. Fluids and solids computations 57

2.1.5 Computing the vorticity


Formulation and approximation

When d = 2, we define [Girault and Raviart, 1986, page 30] for any distributions ϕ and v:
 
∂ϕ ∂ϕ
curl ϕ = ,− ,
∂x1 ∂x0
∂v1 ∂v0
curl v = − ,
∂x0 ∂x1
and when d = 3:  
∂v2 ∂v1 ∂v0 ∂v2 ∂v1 ∂v0
curl v = − , − , −
∂x1 ∂x2 ∂x2 ∂x0 ∂x0 ∂x1
Let u be the solution of the Stokes problem (S). The vorticity is defined by:

ω = curl u when d = 2,
ω = curl u when d = 3.

Since the approximation of the velocity is piecewise quadratic, we are looking for a discontinuous
piecewise linear vorticity field that belongs to:

Yh = {ξ ∈ L2 (Ω); ξ/K ∈ P1 , ∀K ∈ Th }, when d = 2


Yh = {ξ ∈ (L2 (Ω))3 ; ξi/K ∈ P1 , ∀K ∈ Th }, when d = 3

The approximate variational formulation writes:

when d = 2,
R R
ωh ∈ Yh , Ω ωh ξ dx = Ω
curl uh ξ dx, ∀ξ ∈ Yh

when d = 3.
R R
ω ∈ Yh , Ω
ω h .ξ dx = Ω
curl uh .ξ dx, ∀ξ ∈ Yh

File 2.9: vorticity.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 field uh ;
7 din >> uh ;
8 const space & Xh = uh . get_space ();
9 string grad_approx = " P " + to_string ( Xh . degree () -1) + " d " ;
10 string valued = ( uh . get_geo (). dimension () == 3) ? " vector " : " scalar " ;
11 space Lh ( uh . get_geo () , grad_approx , valued );
12 field curl_uh = lazy_interpolate ( Lh , curl ( uh ));
13 dout << catchmark ( " w " ) << curl_uh ;
14 }

Comments

As for the stress tensor (see stress.cc, page 48), the vorticity is obtained by a direct interpolation
of the uh first derivatives and its approximation is discontinuous at inter-element boundaries.

How to run the program

For d = 2, just enter:

make vorticity
./vorticity < square.field | field -elevation -stereo -
58 Rheolef version 7.2

Figure 2.7: The vorticity: elevation view for d = 2 and vector representation for d = 3 (with
anaglyph).

and you observe a discontinuous piecewise linear representation of the approximate vorticity. Also,
the vorticity presents two sharp peaks at the upper corners of the driven cavity: the vorticity is
unbounded and the peaks will increase with mesh refinements. This singularity of the solution is
due to the boundary condition for the first component of the velocity u0 that jumps from zero to
one at the corners. The approximate vorticity field can also be projected on a continuous piecewise
linear space, using the -proj option (See Fig. 2.7 left):
./vorticity < square.field | field -elevation -stereo -nofill -
./vorticity < square.field | field -elevation -stereo -proj -
For d = 3, the whole vorticity vector can also be visualized (See Fig. 2.7 right):
./vorticity < cube.field | field -proj -velocity -stereo -
In the previous command, the -proj option has been used: since the 3D render has no support for
discontinuous piecewise linear fields, the P1-discontinuous field is transformed into a P1-continuous
one, thanks to a L2 projection. P1 The following command shows the second component of the
vorticity vector, roughly similar to the bidimensional case.
./vorticity < cube.field | field -comp 1 -
./vorticity < cube.field | field -comp 1 -proj -

2.1.6 Computing the stream function


Formulation and approximation

When d = 3, the stream function is a vector-valued field ψ that satisfies [Girault and Raviart,
1986, page 90]: curl ψ = u and div ψ = 0. From the identity:
curl curl ψ = −∆ψ + ∇(div ψ)
we obtain the following characterization of ψ :
−∆ ψ = curl u in Ω,
ψ = 0 on Γback ∪ Γfront ∪ Γtop ∪ Γbottom ,
∂ψ
= 0 on Γleft ∪ Γright .
∂n
Chapter 2. Fluids and solids computations 59

When d = 2, the stream function ψ is a scalar-valued field the solution of the following prob-
lem [Girault and Raviart, 1986, page 88]:
−∆ ψ = curl u in Ω,
ψ = 0 on ∂Ω.

File 2.10: streamf_cavity.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cavity . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 field uh ;
8 din >> uh ;
9 string approx = " P " + to_string ( uh . get_space (). degree ());
10 space Ph = cavity :: streamf_space ( uh . get_geo () , approx );
11 space Xh = uh . get_space ();
12 size_t d = uh . get_geo (). dimension ();
13 trial u ( Xh ) , psi ( Ph ); test phi ( Ph );
14 form a = ( d == 3) ? integrate ( ddot ( grad ( psi ) , grad ( phi )))
15 : integrate ( dot ( grad ( psi ) , grad ( phi )));
16 form b = ( d ==3) ? integrate ( dot ( curl ( u ) , phi ))
17 : integrate ( curl ( u )* phi );
18 field psi_h = cavity :: streamf_field ( Ph );
19 field lh = b * uh ;
20 problem p ( a );
21 p . solve ( lh , psi_h );
22 dout << catchmark ( " psi " ) << psi_h ;
23 }

How to run the program

Figure 2.8: The stream function visualization: isolines for d = 2, and combined vectors and
isonorm surface for d = 3.

For d = 2, just enter (see Fig. 2.8 left):

make streamf_cavity
./streamf_cavity < square.field | field -bw -
60 Rheolef version 7.2

For d = 3, the whole stream function vector can be visualized:


./streamf_cavity < cube.field | field -velocity -scale 10 -
The second component of the stream function is showed by:
./streamf_cavity < cube.field | field -comp 1 -
The combined representation of Fig. 2.8.right has been obtained in two steps. First, enter:
./streamf_cavity < cube.field | field -comp 1 -noclean -noexecute -name psi1 -
./streamf_cavity < cube.field | field -velocity -scale 10 -
The -noclean -noexecute options cause the creation of the ‘.vtk’ file for the second compo-
nent, without running the paraview visualization. Next, in the paraview window associated to
the whole stream function, select the File->Open menu and load ‘psi1.vtk’ and click on the
green button Apply. Finally, select the Filters/Common/Contours menu: the isosurface appears.
Observe that the 3D stream function is mainly represented by its second component.

2.2 Nearly incompressible elasticity and the stabilized Stokes


problems
2.2.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 difficulty, the pressure is introduced :
p = −λdiv u
and the problem becomes:
(E) find u and p defined in Ω such that:
− div(2D(u)) + ∇p = f in Ω,
1
− div u − p = 0 in Ω,
λ
+B.C.
The variational formulation of this problem expresses:
(V F E) find u ∈ V (1) and p ∈ L2 (Ω) such that:
a(u, v) + b(v, p) = m(f , v), ∀v ∈ V (0),
b(u, q) − c(p, q) = 0, ∀q ∈ L20 (Ω),
where
Z
m(u, v) = u.v dx,
ZΩ
a(u, v) = D(u) : D(v) dx,

Z
b(v, q) = − div(v) q dx.
ZΩ
1
c(p, q) = p q dx.
λ Ω
V = {v ∈ (H 1 (Ω))2 ; v = 0 on Γlef t ∪ Γbottom }
Chapter 2. Fluids and solids computations 61

When λ becomes large, we obtain the incompressible elasticity problem, that coincides with the
Stokes problem.

Approximation

As for the Stokes problem, the Hood and Taylor [1973] finite element approximation is considered.
We introduce a mesh Th of Ω and the following finite dimensional spaces:
Xh = {v ∈ (H 1 (Ω)); v/K ∈ (P2 )2 , ∀K ∈ Th },
Vh (α) = Xh ∩ V,
Qh = {q ∈ L2 (Ω)) ∩ C 0 (Ω̄); q/K ∈ P1 , ∀K ∈ Th },
The approximate problem writes:
(V F E)h find uh ∈ Vh (1) and p ∈ Qh such that:
a(uh , v) + b(v, ph ) = 0, ∀v ∈ Vh (0),
b(uh , q) − c(p, q) = 0, ∀q ∈ Qh .

File 2.11: incompressible-elasticity.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " embankment . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 Float inv_lambda = ( argc > 2 ? atof ( argv [2]) : 0);
9 size_t d = omega . dimension ();
10 space Xh = embankment_space ( omega , " P2 " );
11 space Qh ( omega , " P1 " );
12 point f (0 ,0 ,0);
13 f [d -1] = -1;
14 trial u ( Xh ) , p ( Qh ); test v ( Xh ) , q ( Qh );
15 field lh = integrate ( dot (f , v ));
16 form a = integrate (2* ddot ( D ( u ) , D ( v )));
17 form b = integrate ( - div ( u )* q );
18 form c = integrate ( inv_lambda * p * q );
19 field uh ( Xh , 0) , ph ( Qh , 0);
20 problem_mixed elasticity (a , b , c );
21 elasticity . solve ( lh , field ( Qh ,0) , uh , ph );
22 dout << catchmark ( " inv_lambda " ) << inv_lambda << endl
23 << catchmark ( " u " ) << uh
24 << catchmark ( " p " ) << ph ;
25 }

Comments

The problem admits the following matrix form:


    
a trans(b) uh lh
=
b −c ph 0
The problem is similar to the Stokes one (see page 55). This system is solved by:
problem_mixed elasticity (a , b , c );
elasticity . solve ( lh , field ( Qh ,0) , uh , ph );

For two-dimensional problems, a direct solver is used by default. In the three-dimensional case,
an iterative algorithm is the default: the preconditioned conjugate gradient. The preconditioner is
here the mass matrix mp for the pressure. As showed by Klawonn [1998], 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.
62 Rheolef version 7.2

How to run the program

Figure 2.9: The incompressible linear elasticity (λ = +∞) for N = 2 and N = 3.

We assume that the previous code is contained in the file ‘incompressible-elasticity.cc’.


Compile the program as usual (see page 16):

make incompressible-elasticity

and enter the commands:

mkgeo_grid -t 10 > square.geo


./incompressible-elasticity square.geo 0 > square.field
field square.field -nofill

mkgeo_grid -T 10 > cube.geo


./incompressible-elasticity cube.geo 0 > cube.field
field cube.field -fill -scale 2

The visualization is performed as usual: see section 2.1.1, page 45. Compare the results on Fig. 2.9,
obtained for λ = +∞ with those of Fig. 2.2, page 46, obtained for λ = 1.
Finally, the stress computation and the mesh adaptation loop is left as an exercise to the reader.

2.2.2 The P1 b − P1 element for the Stokes problem


Formulation and approximation

Let us go back to the Stokes problem. In section 2.1.4, page 53, the Taylor-Hood finite element was
considered. Here, we turn to the mini-element proposed by Arnold, Brezzi, and Fortin [1984], also
well-known as the P1-bubble element. This element is generally 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 P1 approximation of the velocity field. Moreover, this problem develops some links
with stabilization technique and will presents some new Rheolef features.
Chapter 2. Fluids and solids computations 63

We consider a mesh Th of Ω ⊂ Rd , d = 2, 3 composed only of simplicial elements: triangles when


d = 2 and tetrahedra when d = 3. The following finite dimensional spaces are introduced:
(1)
Xh = {v ∈ (H 1 (Ω))d ; v/K ∈ (P1 )d , ∀K ∈ Th },
Bh = {β ∈ (C 0 (Ω̄))d ; β /K ∈ B(K)d , ∀K ∈ Th }
(1)
Xh = Xh ⊕ Bh
Vh (α) = Xh ∩ V(α),
Qh = {q ∈ L2 (Ω)) ∩ C 0 (Ω̄); q/K ∈ P1 , ∀K ∈ Th },

where B(K) = vect(λ1 × . . . × λd+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 (2.4),
page 54, up to the choice of finite dimensional spaces.
(1) (b) (1) (1)
Remark that the velocity field splits in two terms: uh = uh +uh , where uh ∈ Xh is continuous
(b)
and piecewise linear, and uh ∈ Bh is the bubble term.
We consider the abrupt contraction geometry:

Ω =]−Lu , 0[×]0, c[ ∪ [0, Ld [×]0, 1[

where c ⩾ 1 stands for the contraction ratio, and Lu , Ld > 0, are the upstream and downstream
tube lengths. The boundary conditions on u = (u0 , u1 ) for this test case are:

u0 = upoiseuille and u1 = 0 on Γupstream


u = 0 on Γwall
∂u0
= 0 and u1 = 0 on Γaxis
∂x1
∂u
= 0 on Γdownstream
∂n
where

Γupstream = {−Lu }×]0, c[


Γdownstream = {Ld }×]0, 1[
Γaxis = ]−Lu , Ld [×{0}
Γwall = ]−Lu , 0[×{c} ∪ {0}×]1, c[ ∪ ]0, Ld [×{1}

The matrix structure is similar to those of the Taylor-Hood element (see section 2.1.4, page 53).
(1)
Since Xh = Xh ⊕ Bh , any element uh ∈ Xh can be written as a sum uh = u1,h + ub,h where
(1)
u1,h ∈ Xh and ub,h ∈ Bh . Remark that
(1)
a(u1,h , vb,h ) = 0, ∀u1,h ∈ Xh , ∀vb,h ∈ Bh .

Thus, the form a(., .) defined over Xh × Xh writes simply as the sum of the forms a1 (., .) and
(1) (1)
ab (., .), defined over Xh × Xh and Bh × Bh respectively. Finally, the form b(., .) defined over
(1)
Xh × Qh writes as the sum of the forms b1 (., .) and bb (., .), defined over Xh × Qh and Bh × Qh
respectively. Then, the linear system admits the following block structure :

A1 0 B1T
    
U1 L1
 0 Ab BbT   Ub  =  Lb 
B1 Bb 0 P Lp

An alternative and popular implementation of this element eliminates the unknowns related to
the bubble components (see e.g. Abdalass, 1987, page 24). Remark that, on any element K ∈ Th ,
64 Rheolef version 7.2

any bubble function vK that belongs to B(K) vanishes on the boundary of K and have a compact
support in K. Thus, the Ab matrix is block-diagonal. Moreover, Ab is invertible and Ub writes :

Ub = A−1 T
b (Bb p − Lb )

As Ab is block-diagonal, its inverse can be efficiently inverted at the element level during the
assembly process. Then, Ub can be easily eliminated from the system that reduces to:

B1T
    
A1 U1 L1
=
B1 −C P L̃p

where L̃p = Lp −A−1 −1 T


b Lp and C = Bb Ab Bb . Remarks that the matrix structure is similar to those
of the nearly incompressible elasticity (see 2.2.1, page 2.2.1). This reduced matrix formulation of
the P1 b − P 1 element is similar to the direct P1 − P1 stabilized element, proposed by Brezzi and
Pitkäranta [1984].

File 2.12: stokes_contraction_bubble.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " contraction . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space X1h = contraction :: velocity_space ( omega , " P1 " );
9 space Bh ( omega , " bubble " , " vector " );
10 space Qh ( omega , " P1 " );
11 trial u1 ( X1h ) , ub ( Bh ) , p ( Qh );
12 test v1 ( X1h ) , vb ( Bh ) , q ( Qh );
13 form b1 = integrate ( - div ( u1 )* q );
14 form bb = integrate ( - div ( ub )* q );
15 form a1 = integrate (2* ddot ( D ( u1 ) , D ( v1 )));
16 integrate_option iopt ;
17 iopt . invert = true ;
18 form inv_ab = integrate (2* ddot ( D ( ub ) , D ( vb )) , iopt );
19 form c = bb * inv_ab * trans ( bb );
20 field u1h = contraction :: velocity_field ( X1h );
21 field ph ( Qh , 0);
22 problem_mixed stokes ( a1 , b1 , c );
23 stokes . solve ( field ( X1h ,0) , field ( Qh ,0) , u1h , ph );
24 dout << catchmark ( " inv_lambda " ) << 0 << endl
25 << catchmark ( " u " ) << u1h
26 << catchmark ( " p " ) << ph ;
27 }

Comments

First, A−1
b is computed as:
integrate_option iopt ;
iopt . invert = true ;
form inv_ab = integrate (2* ddot ( D ( ub ) , D ( vb )) , iopt );

Note the usage of the optional parameter iopt to the integrate function. As the form is bloc-
diagonal, its inverse is computed element-by-element during the assembly process. Next, the
C = Bb A−1
b Bb form is simply computed as:
T

form c = bb * inv_ab * trans ( bb );

The file ‘contraction.h’ contains code for the velocity and stream function boundary conditions.
Chapter 2. Fluids and solids computations 65

File 2.13: contraction.h


1 struct contraction {
2 struct base {
3 base ( geo omega ) : c (0) , umax (0) , cartesian ( true ) {
4 c = omega . xmax ()[1];
5 string sys_coord = omega . co ord in ate _s yst em _na me ();
6 cartesian = ( sys_coord == " cartesian " );
7 umax = cartesian ? 3/(2* c ) : 4/ sqr ( c );
8 }
9 Float c , umax ;
10 bool cartesian ;
11 };
12 struct u_upstream : base {
13 u_upstream ( geo omega ) : base ( omega ) {}
14 Float operator () ( const point & x ) const {
15 return base :: umax *(1 - sqr ( x [1]/ base :: c )); }
16 };
17 static space velocity_space ( geo omega , string approx ) {
18 space Xh ( omega , approx , " vector " );
19 Xh . block ( " wall " );
20 Xh . block ( " upstream " );
21 Xh [1]. block ( " axis " );
22 Xh [1]. block ( " downstream " );
23 return Xh ;
24 }
25 static field velocity_field ( space Xh ) {
26 geo omega = Xh . get_geo ();
27 string approx = " P " + to_string ( Xh . degree ());
28 space Wh ( omega [ " upstream " ] , approx );
29 field uh ( Xh , 0.);
30 uh [0][ " upstream " ] = lazy_interpolate ( Wh , u_upstream ( omega ));
31 return uh ;
32 }
33 static space streamf_space ( geo omega , string approx ) {
34 space Ph ( omega , approx );
35 Ph . block ( " upstream " );
36 Ph . block ( " wall " );
37 Ph . block ( " axis " );
38 return Ph ;
39 }
40 struct psi_upstream : base {
41 psi_upstream ( geo omega ) : base ( omega ) {}
42 Float operator () ( const point & x ) const {
43 Float y = ( x [1]/ base :: c );
44 if ( base :: cartesian ) {
45 return ( base :: umax * base :: c )*( y *(1 - sqr ( y )/3) - 2./3);
46 } else {
47 return 0.5*( base :: umax * sqr ( base :: c ))*( sqr ( y )*(1 - sqr ( y )/2) - 0.5);
48 }
49 }
50 };
51 static field streamf_field ( space Ph ) {
52 geo omega = Ph . get_geo ();
53 space Wh ( omega [ " upstream " ] , Ph . get_approx ());
54 field psi_h ( Ph , 0);
55 psi_upstream psi_up ( omega );
56 psi_h [ " upstream " ] = lazy_interpolate ( Wh , psi_up );
57 psi_h [ " wall " ] = 0;
58 psi_h [ " axis " ] = -1;
59 return psi_h ;
60 }
61 };

Without loss of generality, we assume that the half width of the downstream channel is assumed
to be equal to one. The Poiseuille velocity upstream boundary condition u_upsteam is then scaled
such that the downstream average velocity is equal to one. By this way, the flow rate in the half
upstream and downstream channel are also equal to one. The stream function is defined up to a
constant: we assume that it is equal to −1 on the axis of symmetry: by this way, it is equal to
66 Rheolef version 7.2

zero on the wall.


The file ‘contraction.h’ also contains a treatment of the axisymmetric variant of the geometry:
this case will be presented in the next paragraph. Note also the automatic computation of the
geometric coordinate system and contraction ratio c from the input mesh, as:
c = omega . xmax ()[1];
string sys_coord = omega . co ord in ate _s yst em_ na me ();

These parameters are transmitted via a base class to the class-function that computes the Poiseuille
upstream flow boundary condition.

How to run the program

ψmax = 1.109 × 10−3

ψmax = 1.118 × 10−3

Figure 2.10: Solution of the Stokes problem in the abrupt contraction: (top) the mesh; (center) the
P1 stream function associated to the P1 b−P1 element; (bottom) the P2 stream function associated
to the P2 − P1 Taylor-Hood element.

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 build from the geometry
description file ‘contraction.mshcad’, which is provided in the example directory of the Rheolef
Chapter 2. Fluids and solids computations 67

distribution. The building mesh procedure is presented with details in appendix A.2, page A.2.

gmsh -2 contraction.mshcad -format msh2 -o contraction.msh


msh2geo contraction.msh > contraction.geo
geo contraction.geo

The mesh is represented on Fig. 2.10.top. Then, the computation and the visualization writes:

make stokes_contraction_bubble
./stokes_contraction_bubble contraction.geo > contraction-P1.field
field contraction-P1.field -velocity

The visualization of the velocity field brings few informations about the properties of the flow.
The stream function is more relevant for stationary flow visualization.
68 Rheolef version 7.2

File 2.14: streamf_contraction.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " contraction . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 field uh ;
8 din >> uh ;
9 const geo & omega = uh . get_geo ();
10 size_t d = omega . dimension ();
11 Float c = omega . xmax ()[1];
12 string approx = " P " + to_string ( uh . get_space (). degree ());
13 space Ph = contraction :: streamf_space ( omega , approx );
14 field psi_h = contraction :: streamf_field ( Ph );
15 integrate_option iopt ;
16 iopt . ignore_sys_coord = true ;
17 const space & Xh = uh . get_space ();
18 trial psi ( Ph ) , u ( Xh );
19 test xi ( Ph ) , v ( Xh );
20 form a = ( d == 3) ? integrate ( ddot ( grad ( psi ) , grad ( xi )))
21 : integrate ( dot ( grad ( psi ) , grad ( xi )) , iopt );
22 field lh = integrate ( dot ( uh , bcurl ( xi )));
23 problem p ( a );
24 p . solve ( lh , psi_h );
25 dout << catchmark ( " psi " ) << psi_h ;
26 }

Note the usage of the optional parameter iopt to the integrate function.
iopt . ignore_sys_coord = true ;

In the axisymmetric coordinate system, there is a specific definition of the stream function, together
with the use of a variant of the curl operator, denoted as bcurl in Rheolef.
field lh = integrate ( dot ( uh , bcurl ( xi )));

The axisymmetric case will be presented in the next section. By this way, our code is able to deal
with both Cartesian and axisymmetric geometries.
The stream function ψ (see also section 2.1.6) is computed and visualized as:

make streamf_contraction
./streamf_contraction < contraction-P1.field > contraction-P1-psi.field
field contraction-P1-psi.field
field contraction-P1-psi.field -n-iso 15 -n-iso-negative 10 -bw

The P1 stream function is represented on Fig. 2.10.center. The stream function is zero along
the wall and the line separating the main flow and the vortex located in the outer corner of the
contraction. Thus, the isoline associated to the zero value separates the main flow from the vortex.
In order to observe this vortex, an extra -n-iso-negative 10 option is added: ten isolines are
drawn for negatives values of ψ, associated to the main flow, and n_iso-10 for the positives values,
associated to the vortex.
A similar computation based on the Taylor-Hood P2 − P1 element is implemented in
stokes_contraction.cc. The code is similar, up to the boundary conditions, to
stokes_cavity.cc (see page 54): thus it is not listed here but is available in the Rheolef example
directory.

make stokes_contraction
./stokes_contraction contraction.geo > contraction-P2.field
field contraction-P2.field -velocity
./streamf_contraction < contraction-P2.field > contraction-P2-psi.field
field contraction-P2-psi.field -n-iso-negative 10 -bw
Chapter 2. Fluids and solids computations 69

The associated P2 stream function is represented on Fig. 2.10.bottom. Observe that the two
solutions are similar and that the vortex activity, defined as ψmax , is accurately computed with
the two methods (see also Saramito, 1990, Fig. 5.11.a, page 143).

field contraction-P1-psi.field -max


field contraction-P2-psi.field -max

Recall that the stream function is negative in the main flow and positive in the vortex located
in the outer corner of the contraction. Nevertheless, the Taylor-Hood based solution is more
accurate : this is perceptible on the graphic, in the region where the upstream vortex reaches the
boundary.

2.2.3 Axisymmetric geometries


Axisymmetric geometries are fully supported in Rheolef: the coordinate system is associated
to the geometry description, stored together with the mesh in the ‘.geo’ and this information is
propagated in spaces, forms and fields without any change in the code. Thus, a code that works in
plane a 2D plane geometry is able to support a 3D axisymmetric one without changes. A simple
axisymmetric geometry writes:

mkgeo_grid -t 10 -zr > square-zr.geo


more square-zr.geo

Remark the additional line in the header:

coordinate_system zr

The axis of symmetry is denoted as z while the polar coordinates are (r, θ). By symmetry, the
problem is supposed to be independent upon θ and the computational domain is described by
(x0 , x1 ) = (z, r). Conversely, in some cases, it could be convenient to swap the order of the
coordinates and use (r, z): this feature is obtained by the -rz option:

mkgeo_grid -t 10 -rz > square-rz.geo


more square-rz.geo

Axisymmetric problems uses L2 functional space equipped with the following weighted scalar
product Z
(f, g) = f (z, r) g(z, r) r drdz

and all usual bilinear forms support this weight. Thus, the coordinate system can be chosen
at run time and we can expect an efficient source code reduction.

2.2.4 The axisymmetric stream function and stress tensor


In the axisymmetric case, the velocity field u = (uz , ur ) can be expressed in terms of the Stokes
stream function ψ by (see Batchelor Batchelor, 1967, p.453 and Wikipedia, 2012):
 
1 ∂ψ 1 ∂ψ
u = (uz , ur ) = , − (2.5)
r ∂r r ∂z

Recall that in the axisymmetric case:


 
1 ∂(rψ) ∂ψ
curl ψ = , −
r ∂r ∂z
70 Rheolef version 7.2

Thus, from this definition, in axisymmetric geometries u ̸= curl ψ and the definition of ψ differs
from the 2D plane or 3D cases (see section 2.1.6, page 58).
Let us turn to a variational formulation in order to compute ψ from u. For any ξ ∈ H 1 (Ω), let
us multiply (2.5) by v = (∂r ξ, −∂z ξ) and then integrate over Ω with the r dr dz weight. For any
known u velocity field, the problem writes:
(P): find ψ ∈ Ψ(ψΓ ) such that

a(ψ, ξ) = l(ξ), ∀ξ ∈ Ψ(0)

where we have introduced the following bilinear forms:


Z  
∂ψ ∂ξ ∂ψ ∂ξ
a(ψ, ξ) = + dr dz
Ω ∂r ∂r ∂z ∂z
Z  
∂ξ ∂ξ
l(ξ) = uz − ur r dr dz
Ω ∂r ∂z

These forms are defined in ‘streamf_contraction.cc’ as:


integrate_option iopt ;
iopt . ignore_sys_coord = true ;
form a = integrate ( dot ( grad ( psi ) , grad ( xi )) , iopt );

and
field lh = integrate ( dot ( uh , bcurl ( xi )));

The iopt.ignore_sys_coord alows us to drops the r integration weight, i.e. replace r dr dz by


dr dz when computing the a(., .) form. Conversely, l involves the bcurl operator defined as:
 
∂ξ ∂ξ
bcurl ξ = , −
∂r ∂z

It is is closely related but differs from the standard curl operator:


 
1 ∂(rξ) ∂ξ
curl ξ = , −
r ∂r ∂z

The bcurl operator is a specific notation introduced in Rheolef: it coincides with the usual curl
operator except for axisymmetric geometries. In tht case, it refers to the Batchelor trick, suitable
for the computation of the stream function.
As an example, let us reconsider the contraction geometry (see section 2.2.2, page 62), extended
in the axisymmetric case. In that case, the functional space is defined by:

Ψ(ψΓ ) = {φ ∈ H 1 (Ω); φ = ψΓ on Γupstream ∪ Γwall ∪ Γaxis }

with
on Γupstream

 ψpoiseuile
ψΓ = 0 on Γwall
−1 on Γaxis

This space corresponds to the imposition of Dirichlet boundary conditions on Γupstream , Γwall and
Γaxis and a Neumann boundary condition on Γdownstream .
The following unix commands generate the axisymmetric geometry:

gmsh -2 contraction.mshcad -format msh2 -o contraction.msh


msh2geo -zr contraction.msh > contraction-zr.geo
more contraction-zr.geo
geo contraction-zr.geo
Chapter 2. Fluids and solids computations 71

ψmax = 1.84 × 10−3

Figure 2.11: Solution of the axisymmetric Stokes problem in the abrupt contraction: (top) the P2
stream function associated to the P 2 − P1 element; (bottom) comparison with the 2D Cartesian
solution (in red).

The previous code stokes_contraction.cc and streamf_contraction.cc are both reused as:

./stokes_contraction contraction-zr.geo > contraction-zr-P2.field


./streamf_contraction < contraction-zr-P2.field > contraction-zr-P2-psi.field
field contraction-zr-P2-psi.field -n-iso-negative 10 -bw

The solution is represented on Fig. 2.11: it slightly differs from the 2D Cartesian solution, as
computed in the previous section (see Fig. 2.10). The vortex size is smaller but its intensity
ψmax = 1.84 × 10−3 is higher. Despite the stream functions looks like similar, the plane solutions
are really different, as we can observe from a cut of the first component of the velocity along the
axis (see Fig. 2.12):

field contraction-P2.field -comp 0 -cut -normal 0 1 -origin 0 1e-15 -gnuplot


field contraction-zr-P2.field -comp 0 -cut -normal 0 1 -origin 0 1e-15 -gnuplot

The 1e-15 argument replace the zero value, as the mesh intersection cannot yet be done exactly
on the boundary. Note that the stokes_contraction_bubble.cc can be also reused in a similar
way:

./stokes_contraction_bubble contraction-zr.geo > contraction-zr-P1.field


./streamf_contraction < contraction-zr-P1.field > contraction-zr-P1-psi.field
field contraction-zr-P1-psi.field -n-iso-negative 10 -bw

There is another major difference with axisymmetric problems: the rate of deformation tensor
writes:  
τzz τrz 0
τ = 2D(u) =  τrz τrr 0 
0 0 τθθ
72 Rheolef version 7.2

0
u0 (z, 0) axisymetric τθθ (z, 0)
cartesian
4

3
−1

1
−2

0
−8 −4 0 2 −8 −4 0 2
z z

Figure 2.12: Solution of the plane and axisymmetric Stokes problem in the abrupt contraction:
cut along the axis of symmetry: (left): u0 ; (right) τθθ .

Thus, there is an additional non-zero component τθθ that is automatically integrated into the
computations in Rheolef. The incompressibility relation leads to tr(τ ) = τzz + τrr + τθθ = 0.
Here σtot = −p.I + τ is the total Cauchy stress tensor (by a dimensionless procedure, the viscosity
can be taken as one). By reusing the stress.cc code (see page 48) we are able to compute the
tensor components:

make stress
./stress < contraction-zr-P1.field > contraction-zr-P1-tau.field

The visualization along the axis of symmetry for the τθθ component is obtained by (see Fig. 2.12):

field contraction-zr-P1-tau.field -comp 22 -proj -cut -normal 0 1 -origin 0 1e-15 -gnuplot

Recall that the τzz and τrr components are obtained by the -comp 00 and -comp 11 options,
respectively. The non-zero values along the axis of symmetry expresses the elongational effects in
the entry region of the abrupt contraction.

2.3 [New] Slip boundary conditions


Formulation

We consider an approximation of the Stokes problem with slip boundary conditions. As a test
case, we consider the flow around a circular obstacle, as represented on Fig. 2.13. We assume a
permanent flow and: due to the symmetries versus upstream/downstream and with respect to the
horizontal axis, the computational domain reduces to the quarter of the geometry.
This problem writes [Verfürth, 1987]:
Chapter 2. Fluids and solids computations 73

x1 Γwall
c

Γvaxis Ω
1 Γdownstream

Γobstacle

0 1 Γaxis L x0
Γwall ∪ Γaxis ∪ Γobstacle : u.n = 0 and σ nt = 0
Γvaxis : ut = 0 and σnn = 0
Γdownstream : u = (1, 0)

Figure 2.13: Slip boundary conditions for the flow around an obstacle.

(P ): find u and p, defined in Ω, such that

σ = −pI + 2D(u) in Ω (2.6a)





−div σ = 0 in Ω (2.6b)





in Ω (2.6c)

 −div u = 0


 u.n = 0 and σ nt = 0 on Γwall ∪ Γaxis ∪ Γobstacle (2.6d)
ut = 0 and σnn = 0 on Γvaxis (2.6e)





on Γdownstream (2.6f)

u = e0

with the notation v t = v − (v.n)n, τnn = (τ n).n and τ nt = τ n − τnn n for any vector v and ten-
sor τ . Observe that Γwall , Γaxis and Γvaxis are parallel to the coordinate axis: the corresponding
slip boundary condition writes also

ud−1 = 0 and σi,d−1 = 0 on Γwall ∪ Γaxis , 1 ⩽ i ⩽ d − 2


ui = 0 and σ0,0 = 0 on Γvaxis , 1 ⩽ i ⩽ d − 1

It remains one slip boundary condition on the curved boundary domain Γobstacle : imposing such
a boundary condition is the main difficulty of this problem. There are mainly three approaches
for the imposition of this boundary condition: (i) the regularization ; (ii) the Lagrange multiplier
weak imposition ; (iii) the strong imposition. The main drawback of the Lagrange multiplier weak
imposition approach is the discretization of the Lagrange multiplier λ, that should satisfy the inf-
sup condition [Verfürth, 1987, 1991, Liakos, 2001, Caglar and Liakos, 2009]. An alternative is to
introduce stabilization terms (Verfürth, 1991, p. 621, eqn (4.1)), but the resulting problem is no
more symmetric. The strong imposition [Verfürth, 1985] requires some modifications of the finite
element basis along the slip boundary: this promising feature is in development in the Rheolef
library. The rest of this section focuses on the regularization approach.
The main idea of the regularization approach is to replace the slip boundary condition (2.6d) on
the curved boundary domain Γobstacle by Robin one:

σ nt + ε−1 u.n = 0 on Γobstacle

where ε > 0 is the regularization parameter. It leads to the following variational formulation:
74 Rheolef version 7.2

(F V )ε : find u ∈ V (1) and p ∈ L2 (Ω) such that


(
a(u, v) + b(v, p) = 0, ∀v ∈ V (0)
b(u, q) = 0, ∀q ∈ L2 (Ω)
with Z Z
a(u, v) = 2D(u) : 2D(v) dx + ε−1 (u.n)(v.n) ds
Ω ∂Ω
Z
b(v, q) = − q div v dx
n Ω d
V (α) = v ∈ H 1 (Ω) / v = αe0 on Γdownstream
and ui = 0 on Γvaxis , 1 ⩽ i ⩽ d − 1
and ud−1 = 0 on Γwall ∪ Γaxis }

Approximation

The curved boundary ∂Ω is replaced polynomial approximation ∂Th . Then, a natural procedure
would be to replace the the normal n on ∂Ω by the normal nh on ∂Th in the previous expression
of the bilinear form a. This approach do not converge in general and this counter-intuitive feature
is called the Babuška paradox, see e.g. Verfürth [1985, p. 473]. We have to deal with either the
exact normal n or a more accurate approximation n e h of n. In the present case, as the exact
normal n is known, we use it.
Otherwise, the space V (α) is completely standard, for any α ∈ R and can be approxi-
mated by classical Lagrange finite elements. Thus, the Hood and Taylor [1973] finite el-
ement approximation of the Stokes problem is considered. The present implementation
‘stokes_obstacle_slip_regul.cc’ supports both the 2D Cartesian and the 3D axisymmetric
cases, so the 3D Cartesian case is not implemented here. Note that the 2D Cartesian case cor-
responds to a 3D cylindrical obstacle while the 3D axisymmetric case corresponds to a spherical
obstacle. The ‘streamf_obstacle_slip_move.cc’ computes the stream function the modified
velocity field u
e h = e0 − uh relative to the moving obstacle.

File 2.15: stokes_obstacle_slip_regul.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 point n_exact ( const point & x ) { return -x ; }
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 Float eps = ( argc > 2) ? atof ( argv [2]) : 1e -7;
9 space Xh ( omega , " P2 " , " vector " );
10 Xh . block ( " downstream " );
11 Xh [1]. block ( " wall " );
12 Xh [1]. block ( " axis " );
13 Xh [1]. block ( " vaxis " );
14 space Qh ( omega , " P1 " );
15 trial u ( Xh ) , p ( Qh );
16 test v ( Xh ) , q ( Qh );
17 form a = integrate (2* ddot ( D ( u ) , D ( v )))
18 + (1/ eps )* integrate ( " obstacle " , dot (u , n_exact )* dot (v , n_exact ));
19 form b = integrate ( - div ( u )* q );
20 field uh ( Xh ,0);
21 uh [0][ " downstream " ] = 1;
22 field ph ( Qh , 0);
23 problem_mixed stokes (a , b );
24 stokes . solve ( field ( Xh ,0) , field ( Qh ,0) , uh , ph );
25 dout << catchmark ( " u " ) << uh
26 << catchmark ( " p " ) << ph ;
27 }
Chapter 2. Fluids and solids computations 75

File 2.16: streamf_obstacle_slip_move.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 field uh ;
7 din >> uh ;
8 point e0 (1 ,0);
9 uh = lazy_interpolate ( uh . get_space () , e0 - uh );
10 string approx = " P " + to_string ( uh . get_space (). degree ());
11 const geo & omega = uh . get_geo ();
12 space Ph ( omega , approx );
13 Ph . block ( " wall " );
14 Ph . block ( " axis " );
15 Ph . block ( " downstream " );
16 field psi_h ( Ph ,0.);
17 trial psi ( Ph );
18 test xi ( Ph );
19 integrate_option iopt ;
20 iopt . ignore_sys_coord = true ;
21 form a = integrate ( dot ( grad ( psi ) , grad ( xi )) , iopt );
22 field lh = integrate ( dot ( uh , bcurl ( xi )));
23 problem p ( a );
24 p . solve ( lh , psi_h );
25 dout << catchmark ( " psi " ) << psi_h ;
26 }

How to run the program

Figure 2.14: Slip boundary conditions for the flow around a cylinder (left) and sphere (right):
isovalues of the stream function.

The run is detailed in the axisymmetric case. The mkgeo_obstacle script generates the mesh of
the geometry:
mkgeo_obstacle -zr -name obstacle-zr
geo obstacle-zr.geo
Then the compilation and run writes:
make stokes_obstacle_slip_regul ./streamf_obstacle_slip_move
./stokes_obstacle_slip_regul obstacle-zr > obstacle-zr.field
field -velocity obstacle-zr.field
./streamf_obstacle_slip_move < obstacle-zr.field | field -bw -n-iso 25 -
Observe on Fig. 2.14 that the trajectories, as represented by the stream function, differ in the
Cartesian an axisymmetric cases.
76 Rheolef version 7.2

2 1
cylinder
sphere

1−u0 (x1 )

x1

cylinder
sphere
1 0
−2 −1 0 0 1 2 3 4
1 − u0 (x1 ) x0

Figure 2.15: Slip boundary conditions for the flow around an obstacle: horizontal relative velocity
along the two axis: (left) along Ox1 on the top of the obstacle ; (right) along Ox0 on the front.

Let us turn to the cut of the relative velocity along the horizontal and vertical axis, named
respectively axis and vaxis by the mesh:

field obstacle-zr.field -domain vaxis -comp 0 -gnuplot -elevation


field obstacle-zr.field -domain axis -comp 0 -gnuplot -elevation

Observe on Fig. 2.15.left that the relative velocity is negative on the top of the obstacle. Indeed,
when the obstacle moves right, the fluid moves from the front of the obstacle, rotates around the
obstacle and goes to the back. Thus, the fluid moves in the negatives direction when it is on the
top of the obstacle. Also, observe that the fluid is more accelerated when it flows around a cylinder
than around a sphere. Fig. 2.15.right shows that the perturbation caused by the fluid decreases
faster for a sphere than for a cylinder.

2.4 Time-dependent problems


2.4.1 The heat equation
Formulation

Let T > 0, Ω ⊂ Rd , d = 1, 2, 3 and f defined in Ω. The heat problem writes:

(P ): find u, defined in Ω×]0, T [, such that

∂u
− ∆u = f in Ω×]0, T [,
∂t
u(0) = 0 in Ω,
u(t) = 0 on ∂Ω×]0, T [.
where f is a known function. In the present example, we consider f = 1.

Approximation

Let ∆t > 0 and tn = n∆t, n ⩾ 0. The problem is approximated with respect to time by the
following first-order implicit Euler scheme:
un+1 − un
− ∆un+1 = f (tn+1 ) in Ω
∆t
Chapter 2. Fluids and solids computations 77

where un ≈ u(n∆t) and u(0) = 0. The variational formulation of the time-discretized problem
writes:
(V F )n : Let un being known, find un+1 ∈ H01 (Ω) such that

a (un+1 , v) = l(n) (v), ∀v ∈ H01 (Ω).

where
Z
a(u, v) = (uv + ∆t ∇u.∇v) v dx
ZΩ
l(n) (v) = (un + ∆t f (tn+1 )) v dx

This is a Poisson-like problem. The discretization with respect to space of this problem is similar
to those presented in section 1.1.1, page 14.

File 2.17: heat.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 geo omega ( argv [1]);
7 size_t n_max = ( argc > 2) ? atoi ( argv [2]) : 100;
8 Float delta_t = 0.5/ n_max ;
9 space Xh ( omega , " P1 " );
10 Xh . block ( " boundary " );
11 trial u ( Xh ); test v ( Xh );
12 form a = integrate ( u * v + delta_t * dot ( grad ( u ) , grad ( v )));
13 problem p ( a );
14 field uh ( Xh , 0);
15 branch event ( " t " ," u " );
16 dout << event (0 , uh );
17 for ( size_t n = 1; n <= n_max ; n ++) {
18 field rhs = uh + delta_t ;
19 field lh = integrate ( rhs * v );
20 p . solve ( lh , uh );
21 dout << event ( n * delta_t , uh );
22 }
23 }

Comments

Note the use of the branch class:


branch event ( " t " ," u " );

this is a wrapper class that is used here to print the branch of solution (tn , un )n⩾0 , on the standard
output in the ‘.branch’ file format. An instruction as:
dout << event (t , uh );

is equivalent to the formatted output


dout << catchmark ( " t " ) << t << endl
<< catchmark ( " u " ) << uh ;

How to run the program

We assume that the previous code is contained in the file ‘heat.cc’. Then, compile the program
as usual (see page 16):

make heat
78 Rheolef version 7.2

Figure 2.16: Animation of the solution of the heat problem.

For a one dimensional problem, enter the commands:


mkgeo_grid -e 100 > line.geo
./heat line.geo > line.branch
The previous commands solve the problem for the corresponding mesh and write the solution in
the field-family file format ‘.branch’. For a bidimensional one:
mkgeo_grid -t 10 > square.geo
./heat square.geo > square.branch
For a tridimensional one:
mkgeo_grid -T 10 > box.geo
./heat box.geo > box.branch

How to run the animation

branch line.branch

A gnuplot window appears. Enter q to exit the window. For a bidimensional case, simply enter:
branch square.branch -elevation

A window appears, that looks like a video player. Then, click on the video play button, at the
top of the window.
To generate an animation file, go to the File->save animation menu and enter as file name
square and as file type avi. The animation file square.avi can now be started from any video
player, such as vlc:
Chapter 2. Fluids and solids computations 79

vlc square.avi

For the tridimensional case, the animation feature is similar:

branch box.branch
branch box.branch -volume

2.4.2 The convection-diffusion problem


Formulation

Let T > 0 and ν > 0. The convection-diffusion problem writes:

(P ): find ϕ, defined in Ω×]0, T [, such that

∂ϕ
+ u.∇ϕ − ν∆ϕ + σϕ = 0 in Ω×]0, T [
∂t
ϕ(0) = ϕ0 in Ω
ϕ(t) = ϕΓ (t) on ∂Ω×]0, T [

where u, σ ⩾ 0, ϕ0 and ϕΓ being known. Note the additional u.∇ operator.

Time approximation

This problem is approximated by the following first-order implicit Euler scheme:

ϕn+1 − ϕn ◦ X n
− ν∆ϕn+1 + σϕn+1 = 0 in Ω
∆t

where ∆t > 0, ϕn ≈ ϕ(n∆t) and ϕ(0) = ϕ0 .


Let tn = n∆t, n ⩾ 0. The term X n (x) is the position at tn of the particle that is in x at tn+1
and is transported by un . Thus, X n (x) = X(tn , x) where X(t, x) is the solution of the differential
equation
dX
(
= u(X(t, x), t) p.p. t ∈ ]tn , tn+1 [,
dt
X(tn+1 , x) = x.

Then X n (x) is approximated by the first-order Euler approximation

X n (x) ≈ x − ∆t nn (x).

This algorithm has been introduced by O. Pironneau (see e.g. Pironneau, 1988), and is known
as the method of characteristic in the finite difference context and as the Lagrange-Galerkin in
the finite element one. The efficient evaluation of ϕh ◦ X n (x) in an unstructured mesh involves a
hierarchical d-tree (quadtree, octree) data structure for the localization of the element K of the
mesh that contains x. When d = 3 requires also sophisticated geometric predicates to test whether
x ∈ K without rounding errors, and avoid to conclude that no elements contains a point x close
to ∂K up to rounding errors. This problems is addressed in Rheolef based on the cgal library.
The following code implements the classical rotating Gaussian hill test case (see e.g. Rui and
Tabata, 2001).
80 Rheolef version 7.2

File 2.18: convect.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " rotating - hill . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 string approx = ( argc > 2) ? argv [2] : " P1 " ;
9 Float nu = ( argc > 3) ? atof ( argv [3]) : 1e -2;
10 size_t n_max = ( argc > 4) ? atoi ( argv [4]) : 50;
11 size_t d = omega . dimension ();
12 Float delta_t = 2* acos ( -1.)/ n_max ;
13 space Vh ( omega , approx , " vector " );
14 field uh = lazy_interpolate ( Vh , u ( d ));
15 space Xh ( omega , approx );
16 Xh . block ( " boundary " );
17 field phi_h = lazy_interpolate ( Xh , phi (d , nu ,0));
18 characteristic X ( - delta_t * uh );
19 integrate_option iopt ;
20 iopt . set_family ( integrate_option :: gauss_lobatto );
21 iopt . set_order ( Xh . degree ());
22 trial phi ( Xh ); test psi ( Xh );
23 branch event ( " t " ," phi " );
24 dout << catchmark ( " nu " ) << nu << endl
25 << event (0 , phi_h );
26 for ( size_t n = 1; n <= n_max ; n ++) {
27 Float t = n * delta_t ;
28 Float c1 = 1 + delta_t * phi :: sigma (d , nu , t );
29 Float c2 = delta_t * nu ;
30 form a = integrate ( c1 * phi * psi + c2 * dot ( grad ( phi ) , grad ( psi )) , iopt );
31 field lh = integrate ( compose ( phi_h , X )* psi , iopt );
32 problem p ( a );
33 p . solve ( lh , phi_h );
34 dout << event (t , phi_h );
35 }
36 }

Comments

The characteristic variable X implements the localizer X n (x):


characteristic X ( - delta_t * uh );

Combined with the compose function, it perform the composition ϕh ◦ X n . The right-hand side
is then computed by using the integrate function:
field lh = integrate ( compose ( phi_h , X )* psi , iopt );

Note the additional iopt argument to the integrate function. By default, when this argument is
omitted, a Gauss quadrature formulae is assumed, and the number of point is computed such that
it integrate exactly 2k + 1 polynomials, where k is the degree of polynomials in Xh . The Gauss-
Lobatto quadrature formule is recommended for Lagrange-Galerkin methods. Recall that this
choice of quadrature formulae guaranties inconditionnal stability at any polynomial order. Here,
we specifies a Gauss-Lobatto quadrature formulae that should be exact for k order polynomials.
The bilinear form is computed via the same quadrature formulae:
form a = integrate ( c1 * phi * psi + c2 * dot ( grad ( phi ) , grad ( psi )) , iopt );

A test case is described by Pironneau and Tabata [2010]: we take Ω =] − 2, 2[d and T = 2π. This
problem provides an example for a convection-diffusion equation and a known analytical solution:
ϕ(t, x) = exp −λt − r(t)|x − x0 (t)|2


where λ = 4ν/t0 ⩾ 0 with t0 > 0 and ν ⩾ 0, x0 (t) is the moving center of the hill and r(t) =
1/(t0 + 4νt). The source term is time-dependent: σ(t) = λ − 2dνr(t) and has been adjusted such
Chapter 2. Fluids and solids computations 81

that the right-hand side is zero. The moving center of the hill x0 (t) is associated to the velocity
field u(t, x) as:

d u(t, x) x0 (t)
1 1/(2π) t/(2π) − 1/2
2 (y, −x) (− cos(t)/2, sin(t)/2)
3 (y, −x, 0) (− cos(t)/2, sin(t)/2, 0)

File 2.19: rotating-hill.h


1 struct u {
2 point operator () ( const point & x ) const {
3 return ( d == 1) ? point ( u0 ) : point ( x [1] , -x [0]); }
4 u ( size_t d1 ) : d ( d1 ) , u0 (0.5/ acos ( Float ( -1))) {}
5 protected : size_t d ; Float u0 ;
6 };
7 struct phi {
8 Float operator () ( const point & x ) const {
9 return exp ( -4* nu *( t / t0 ) - dist2 (x , x0t ())/( t0 +4* nu * t )); }
10 phi ( size_t d1 , Float nu1 , Float t1 =0) : d ( d1 ) , nu ( nu1 ) , t ( t1 ) ,
11 u0 (0.5/ acos ( Float ( -1))) , x0 ( -0.5 ,0) {}
12 static Float sigma ( size_t d , Float nu1 , Float t =0) {
13 return 4* nu1 / t0 - 2* d * nu1 /( t0 + 4* nu1 * t ); }
14 point x0t () const {
15 if ( d == 1) return point ( x0 [0] + u0 * t );
16 return point ( x0 [0]* cos ( t ) + x0 [1]* sin ( t ) ,
17 - x0 [0]* sin ( t ) + x0 [1]* cos ( t )); }
18 point d_x0t_dt () const {
19 if ( d == 1) return point ( u0 );
20 return point ( - x0 [0]* sin ( t ) + x0 [1]* cos ( t ) ,
21 - x0 [0]* cos ( t ) - x0 [1]* sin ( t )); }
22 protected : size_t d ; Float nu , t , u0 ; point x0 ;
23 static constexpr Float t0 = 0.2;
24 };

Note the use of a class-function phi for the implementation of ϕ(t) as a function of x. Such
programming style has been introduced in the standard template library [Musser and Saini, 1996b],
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.

How to run the program

We assume that the previous code is contained in the file ‘convect.cc’. Then, compile the program
as usual (see page 16):

make convect

and enter the commands: Running the one-dimensional test case:

mkgeo_grid -e 500 -a -2 -b 2 > line2.geo


./convect line2.geo P1 > line2.branch
branch line2.branch -gnuplot

Note the hill that moves from x = −1/2 to x = 1/2. Since the exact solution is known, it is
possible to analyze the error:
82 Rheolef version 7.2

Figure 2.17: Animation of the solution of the rotating hill problem.

File 2.20: convect_error.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " rotating - hill . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 Float tol = ( argc > 1) ? atof ( argv [1]) : 1e -10;
8 Float nu ;
9 din >> catchmark ( " nu " ) >> nu ;
10 branch get ( " t " ," phi " );
11 branch put ( " t " ," phi_h " ," pi_h_phi " );
12 derr << " # t \ terror_l2 \ terror_linf " << endl ;
13 field phi_h ;
14 Float err_l2_l2 = 0;
15 Float err_linf_linf = 0;
16 for ( Float t = 0 , t_prec = 0; din >> get (t , phi_h ); t_prec = t ) {
17 const space & Xh = phi_h . get_space ();
18 size_t d = Xh . get_geo (). dimension ();
19 field pi_h_phi = lazy_interpolate ( Xh , phi (d , nu , t ));
20 trial phi ( Xh ); test psi ( Xh );
21 form m = integrate ( phi * psi );
22 field eh = phi_h - pi_h_phi ;
23 Float err_l2 = sqrt ( m ( eh , eh ));
24 Float err_linf = eh . max_abs ();
25 err_l2_l2 += sqr ( err_l2 )*( t - t_prec );
26 err_linf_linf = max ( err_linf_linf , err_linf );
27 dout << put (t , phi_h , pi_h_phi );
28 derr << t << " \ t " << err_l2 << " \ t " << err_linf << endl ;
29 }
30 derr << " # error_l2_l2 = " << sqrt ( err_l2_l2 ) << endl ;
31 derr << " # error_linf_linf = " << err_linf_linf << endl ;
32 return ( err_linf_linf <= tol ) ? 0 : 1;
33 }
The numerical error ϕh − πh (ϕ) is computed as:
field pi_h_phi = interpolate ( Xh , phi (d , nu , t ));
field eh = phih - pi_h_phi ;
Chapter 2. Fluids and solids computations 83

and its L2 norm is printed on the standard error. Observe the use of the branch class as both
input and output field stream.
make convect_error
./convect_error < line2.branch > line2-cmp.branch
branch line2-cmp.branch -gnuplot
The instantaneous L2 (Ω) norm is printed at each time step and the total error in L2 (]0, T [; L2 (Ω))
is finally printed at the end of the stream. A P2 approximation can be used as well:

∥ϕh − πh (ϕ)∥L2 (L2 ) ∥ϕh − πh (ϕ)∥L∞ (L∞ )


P1 P1
P2 P2
1 1

0.1 0.1

2
2
0.01 0.01
∆t = 2π/50
∆t = 2π/50
∆t = 2π/100
∆t = 2π/100
0.001 ∆t = 2π/200 0.001 ∆t = 2π/200

0.001 0.01 0.1 1 0.001 0.01 0.1 1


h h

Figure 2.18: Diffusion-convection when d = 1 and ν = 10−2 : convergence versus h and ∆t for P1
and P2 elements: (left) in L2 (L2 ) norm; (right) in L∞ (L∞ ) norm.

./convect line2.geo P2 > line2.branch


branch line2.branch -gnuplot
./convect_error < line2.branch > line2-cmp.branch
On Fig. 2.18.left we observe the L2 (L2 ) convergence versus h for the P1 and P2 elements when
d = 1: the errors reaches a plateau that decreases versus ∆t. On Fig. 2.18.right the L∞ (L∞ )
norm of the error presents a similar behavior. Since the plateau are equispaced, the convergence
versus ∆t is of first order.
These computation was performed for a convection-diffusion problem with ν = 10−2 . The pure
transport problem (ν = 0, without diffusion) computation is obtained by:
./convect line2.geo P1 0 > line2.branch
branch line2.branch -gnuplot
Let us turn to the two-dimensional test case:
mkgeo_grid -t 80 -a -2 -b 2 -c -2 -d 2 > square2.geo
./convect square2.geo P1 > square2.branch
branch square2.branch
The visualization and animation are similar to those of the head problem previously presented
in paragraph 2.4.1. Go to the WrapByScalar entry in pipeline brower and adjust eventually the
scale factor, e.g. to 3. Then, play the animation and observe the rotating hill. The result is
shown on Fig. 2.17. The error analysis writes:
84 Rheolef version 7.2

./convect_error < square2.branch > square2-cmp.branch


branch square2-cmp.branch -nofill -bw -elevation

From the paraview menu, you can visualize simultaneously both the approximate solution and the
Lagrange interpolate of the exact one. First, in the pipeline brower go to the WrapByScalar entry
and adjust the scale factor, e.g. to 3 and click on Apply. Next, go first to the File->Open menu
and select in the /tmp the square2-cmp-..vtk and click on Apply. In the Filter->Alphabetical
menu, select WrapByScalar. In the Properties panel, go to the Scalars entry and select
pi_h_phi, to the coloring entry and select also pi_h_phi, adjust the scale factor to 3. and
click on Apply. Next, in the same panel, in the Representation entry, choose wireframe. You
are ready to click on the video play button, at the top of the window. Observe the difference
between the solution and its approximation. See also the paraview documentation for more. For
serious problem, the characteristic method has been superseded by the discontinuous Galerkin
one, that will be presented in chapter 4, page 147. You are strongly encouraged to definitively
turn to discontinuous Galerkin method for convection dominant and pure transport problems.
Finally, the three-dimensional case:

mkgeo_grid -T 15 -a -2 -b 2 -c -2 -d 2 -f -2 -g 2 > cube2.geo


./convect cube2.geo P1 > cube2.branch

The visualization is similar to the two-dimensional case:

branch cube2.branch
branch cube2.branch -volume

2.5 The Navier-Stokes equations


Formulation

This longer example combines most functionalities presented in the previous examples.
Let us consider the Navier-Stokes problem for the driven cavity in Ω =]0, 1[d , d = 2, 3. Let Re > 0
be the Reynolds number, and T > 0 a final time. The problem writes:
(N S): find u = (u0 , . . . , ud−1 ) and p defined in Ω×]0, T [ such that:
 
∂u
Re + u.∇u − div(2D(u)) + ∇p = 0 in Ω×]0, T [,
∂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 [,
∂u0 ∂u1
= = u2 = 0 on (Γback ∪ Γfront )×]0, T [ when d = 3,
∂n ∂n
where D(u) = (∇u + ∇uT )/2. This nonlinear problem is the natural extension of the linear
Stokes problem, as presented in paragraph 2.5, page 84. The boundaries are represented on
Fig. 2.1, page 44.

Time approximation

Let ∆t > 0. Let us consider the following backward second order scheme, for all ϕ ∈ C 2 ([0, T ]) :

dϕ 3ϕ(t) − 4ϕ(t − ∆t) + ϕ(t − 2∆t)


(t) = + O(∆t2 )
dt 2∆t
Chapter 2. Fluids and solids computations 85

The problem is approximated by the following second-order implicit scheme (BDF2):

3un+1 − 4un ◦ X n + un−1 ◦ X n−1


Re − div(2D(un+1 )) + ∇pn+1 = 0 in Ω,
2∆t n+1
− div u = 0 in Ω,
un+1 = (1, 0) on Γtop ,
un+1 = 0 on Γleft ∪ Γright ∪ Γbottom ,
n+1
∂u0 ∂un+1
= 1
= un+1
2 = 0 on Γback ∪ Γfront when d = 3,
∂n ∂n
where, following [Boukir et al., 1997, Fourestey and Piperno, 2004]:

X n (x) = x − ∆t u∗ (x)
X n−1 (x) = x − 2∆t u∗ (x)

u = 2un − un−1

It is a second order extension of the method previously introduced in paragraph 2.4.2 page 79. The
scheme defines a second order recurrence for the sequence (un )n⩾−1 , that starts with u−1 = u0 = 0.

Variational formulation

The variational formulation of this problem expresses:


(N S)∆t : find un+1 ∈ V(1) and pn+1 ∈ L20 (Ω) such that:

a(un+1 , v) + b(v, pn+1 ) = m(f n , v), ∀v ∈ V(0),

b(un+1 , q) = 0, ∀q ∈ L20 (Ω),

where
Re
fn = 4 un ◦ X n − un−1 ◦ X n

2∆t
and
Z Z
3Re
a(u, v) = u.v dx + 2D(u) : D(v) dx
2∆t Ω Ω

and b(., .) and V(α) was already introduced in paragraph 2.1.4, page 53, while studying the Stokes
problem.

Space approximation

The Hood and Taylor [1973] finite element approximation of this generalized Stokes problem was
also considered in paragraph 2.1.4, page 53. We introduce a mesh Th of Ω and the finite dimensional
spaces Xh , Vh (α) and Qh . The approximate problem writes:
(N S)∆t,h : find un+1
h ∈ Vh (1) and pn+1 ∈ Qh such that:

a(un+1 , v) + b(v, pn+1 ) = m(fhn , v), ∀v ∈ Vh (0),


h h (2.7)
b(un+1
h , q) = 0, ∀q ∈ Qh .

where
Re
fhn = 4 unh ◦ X n − un−1 ◦ Xn

h
2∆t
The problem reduces to a sequence resolution of a generalized Stokes problems.
86 Rheolef version 7.2

File 2.21: navier_stokes_solve.icc


1 using namespace std ;
2 int na vier_stokes_solve (
3 Float Re , Float delta_t , field l0h , field & uh , field & ph ,
4 size_t & max_iter , Float & tol , odiststream * p_derr =0) {
5 const space & Xh = uh . get_space ();
6 const space & Qh = ph . get_space ();
7 string label = " navier - stokes - " + Xh . get_geo (). name ();
8 integrate_option iopt ;
9 iopt . set_family ( integrate_option :: gauss_lobatto );
10 iopt . set_order ( Xh . degree ());
11 trial u ( Xh ) , p ( Qh );
12 test v ( Xh ) , q ( Qh );
13 form m = integrate ( dot (u , v ) , iopt );
14 form a = integrate (2* ddot ( D ( u ) , D ( v )) + 1.5*( Re / delta_t )* dot (u , v ) , iopt );
15 form b = integrate ( - div ( u )* q , iopt );
16 problem_mixed stokes (a , b );
17 if ( p_derr != 0) * p_derr << " [ " << label << " ] # n | du / dt | " << endl ;
18 field uh1 = uh ;
19 for ( size_t n = 0; true ; n ++) {
20 field uh2 = uh1 ;
21 uh1 = uh ;
22 field uh_star = 2.0* uh1 - uh2 ;
23 characteristic X1 ( - delta_t * uh_star );
24 characteristic X2 ( -2.0* delta_t * uh_star );
25 field l1h = integrate ( dot ( compose ( uh1 , X1 ) , v ) , iopt );
26 field l2h = integrate ( dot ( compose ( uh2 , X2 ) , v ) , iopt );
27 field lh = l0h + ( Re / delta_t )*(2* l1h - 0.5* l2h );
28 stokes . solve ( lh , field ( Qh ,0) , uh , ph );
29 field duh_dt = (3* uh - 4* uh1 + uh2 )/(2* delta_t );
30 Float residual = sqrt ( m ( duh_dt , duh_dt ));
31 if ( p_derr != 0) * p_derr << " [ " << label << " ] " << n << " " << residual << endl ;
32 if ( residual < tol ) {
33 tol = residual ;
34 max_iter = n ;
35 return 0;
36 }
37 if ( n == max_iter -1) {
38 tol = residual ;
39 return 1;
40 }
41 }
42 }

Comments

The navier_stokes_solve function is similar to the ‘stokes_cavity.cc’. It solves here a gen-


eralized Stokes problem and manages a right-hand side fh :

characteristic X1 ( - delta_t * uh_star );


characteristic X2 ( -2.0* delta_t * uh_star );
field l1h = integrate ( compose ( uh1 , X1 )* v , iopt );
field l2h = integrate ( compose ( uh2 , X2 )* v , iopt );
field lh = l0h + ( Re / delta_t )*(2* l1h - 0.5* l2h );

This last computation is similar to those done in the ‘convect.cc’ example. The generalized
Stokes problem is solved by the solver_abtb class. The stopping criterion is related to the
stationary solution or the maximal iteration number.
Chapter 2. Fluids and solids computations 87

File 2.22: navier_stokes_cavity.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " navier_stokes_solve . icc "
5 # include " n a vi er _ st ok es _ cr it er i on . icc "
6 # include " cavity . h "
7 int main ( int argc , char ** argv ) {
8 environment rheolef ( argc , argv );
9 if ( argc < 2) {
10 cerr << " usage : " << argv [0] << " <geo > <Re > <err > < n_adapt > " << endl ;
11 exit (1);
12 }
13 geo omega ( argv [1]);
14 adapt_option options ;
15 Float Re = ( argc > 2) ? atof ( argv [2]) : 100;
16 options . err = ( argc > 3) ? atof ( argv [3]) : 1e -2;
17 size_t n_adapt = ( argc > 4) ? atoi ( argv [4]) : 5;
18 Float delta_t = 0.05;
19 options . hmin = 0.004;
20 options . hmax = 0.1;
21 space Xh = cavity :: velocity_space ( omega , " P2 " );
22 space Qh ( omega , " P1 " );
23 field uh = cavity :: velocity_field ( Xh , 1.0);
24 field ph ( Qh , 0);
25 field fh ( Xh , 0);
26 for ( size_t i = 0; true ; i ++) {
27 size_t max_iter = 1000;
28 Float tol = 1e -5;
29 n avier_stokes_solve ( Re , delta_t , fh , uh , ph , max_iter , tol , & derr );
30 odiststream o ( omega . name () , " field " );
31 o << catchmark ( " Re " ) << Re << endl
32 << catchmark ( " delta_t " ) << delta_t << endl
33 << catchmark ( " u " ) << uh
34 << catchmark ( " p " ) << ph ;
35 o . close ();
36 if ( i >= n_adapt ) break ;
37 field ch = n av i er _s t ok es _c r it er io n ( Re , uh );
38 omega = adapt ( ch , options );
39 o . open ( omega . name () , " geo " );
40 o << omega ;
41 o . close ();
42 Xh = cavity :: velocity_space ( omega , " P2 " );
43 Qh = space ( omega , " P1 " );
44 uh = cavity :: velocity_field ( Xh , 1.0);
45 ph = field ( Qh , 0);
46 fh = field ( Xh , 0);
47 }
48 }

File 2.23: navier_stokes_criterion.icc


1 field na v ie r_ st o ke s_ cr i te ri on ( Float Re , const field & uh ) {
2 space T0h ( uh . get_geo () , " P1d " );
3 return lazy_interpolate ( T0h , sqrt ( Re * norm2 ( uh ) + 4* norm2 ( D ( uh ))));
4 }

Comments

The code performs a computation by using adaptive mesh refinement, in order to capture recir-
culation zones. The adapt_option declaration is used by rheolef to send options to the mesh
generator. The code reuse the file ‘cavity.h’ introduced page 54. This file contains two functions
that defines boundary conditions associated to the cavity driven problem.
The criteria function computes the adaptive mesh refinement criteria:

ch = (Re|uh |2 + 2|D(uh )|2 )1/2


88 Rheolef version 7.2

The criteria function is similar to those presented in the ‘embankment_adapt.cc’ example.

How to run the program

Re = 100: 4804 elements, 2552 vertices ψmax = 9.5 × 10−6 , ψmin = −0.103

Re = 400: 5233 elements, 2768 vertices ψmax = 6.4 × 10−4 , ψmin = −0.111

Figure 2.19: Meshes and stream functions associated to the solution of the Navier-Stokes equations
for Re = 100 (top) and Re = 400 (bottom).

The mesh loop adaptation is initiated from a bamg mesh (see also appendix A.2.1).

bamg -g square.bamgcad -o square.bamg


bamg2geo square.bamg square.dmn > square.geo

Then, compile and run the Navier-Stokes solver for the driven cavity for Re = 100:
Chapter 2. Fluids and solids computations 89

Re = 1000: 5873 elements, 3106 vertices ψmax = 1.64 × 10−3 , ψmin = −0.117

Figure 2.20: Meshes and stream functions associated to the solution of the Navier-Stokes equations
for Re = 1000.

make navier_stokes_cavity
time mpirun -np 8 ./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 five mesh adaptations. At each time step, the program prints
an approximation of the time derivative, and stops when a stationary solution is reached. The
mpirun -np 8 prefix allows a parallel and distributed run while the time one returns the real
and the user times used by the computation. The speedup could be estimated here by the ratio
user/real: it is ideally close to the number of processors. These prefixes are optional and you
can omit the mpirun one if you are running with a sequential installation of Rheolef. Then, we
visualize the ‘square-005.geo’ adapted mesh and its associated solution:

geo square-005.geo
field square-005.field.gz -velocity

The representation of the stream function writes:

make streamf_cavity
zcat square-005.field.gz | ./streamf_cavity | field -bw -n-iso-negative 10 -

The programs ‘streamf_cavity.cc’, already introduced page 59, is here reused. The last options
of the field program draws isocontours of the stream function using lines, as shown on Fig. 2.19.
The zero isovalue separates the main flow 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

The visualization of the cut of the horizontal velocity along the vertical median line writes:

field square-005.field.gz -comp 0 -cut -normal -1 0 -origin 0.5 0 -gnuplot


field square-005.field.gz -comp 1 -cut -normal 0 1 -origin 0 0.5 -gnuplot
90 Rheolef version 7.2

0.5 u1 (x0 , 0.5)


x1

Re = 100
0.5 data: Re = 100 0
Re = 400
data: Re = 40 0
Re = 1000 Re = 100
data: Re = 1000 Re = 100, data
Re = 400
Re = 400, data
−0.5 Re = 1000
Re = 1000, data
0
−0.5 0 0.5 1 0 0.5 1
u0 (0.5, x1 ) x0
Figure 2.21: Navier-Stokes: velocity profiles along lines passing thought the center of the cavity,
compared with data from Ghia et al. [1982]: (a) u0 along the vertical line; (b) u1 along the
horizontal line line.

Fig. 2.21 compare the cuts with data from Ghia et al. [1982], table 1 and 2 (see also Gupta and
Kalita, 2005). Observe that the solution is in good agreement with these previous computations.

Re xc yc −ψmin ψmax
100 present 0.613 0.738 0.103 9.5 × 10−6
Labeur and Wells [2007] 0.608 0.737 0.104 -
Donea and Huerta [2003] 0.62 0.74 0.103 -
400 present 0.554 0.607 0.111 5.6 × 10−4
Labeur and Wells [2007] 0.557 0.611 0.115 -
Donea and Huerta [2003] 0.568 0.606 0.110 -
1000 present 0.532 0.569 0.117 1.6 × 10−3
Labeur and Wells [2007] 0.524 0.560 0.121 -
Donea and Huerta [2003] 0.540 0.573 0.110 -

Figure 2.22: Cavity flow: primary vortex position and stream function value.

Finally, table 2.22 compares the primary vortex position and its associated stream function value.
Note also the good agreement with previous simulations. The stream function extreme values are
obtained by:

zcat square-005.field.gz | ./streamf_cavity | field -min -


zcat square-005.field.gz | ./streamf_cavity | field -max -

The maximal value has not yet been communicated to our knowledge and is provided in table 2.22
for cross validation purpose. The small program that computes the primary vortex position is
showed below.

make vortex_position
zcat square-005.field.gz | ./streamf_cavity | ./vortex_position
Chapter 2. Fluids and solids computations 91

File 2.24: vortex_position.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 int main ( int argc , char ** argv ) {
4 environment rheolef ( argc , argv );
5 check_macro ( communicator (). size () == 1 , " please , use sequentially " );
6 field psi_h ;
7 din >> psi_h ;
8 size_t idof_min = 0;
9 Float psi_min = std :: numeric_limits < Float >:: max ();
10 for ( size_t idof = 0 , ndof = psi_h . ndof (); idof < ndof ; idof ++) {
11 if ( psi_h . dof ( idof ) >= psi_min ) continue ;
12 psi_min = psi_h . dof ( idof );
13 idof_min = idof ;
14 }
15 const disarray < point >& xdof = psi_h . get_space (). get_xdofs ();
16 point xmin = xdof [ idof_min ];
17 dout << " xc \ t \ tyc \ t \ tpsi " << std :: endl
18 << xmin [0] << " \ t " << xmin [1] << " \ t " << psi_min << std :: endl ;
19 }

For higher Reynolds number, Shen [1991] showed in 1991 that the flow converges to a station-
ary state for Reynolds numbers up to 10 000; for Reynolds numbers larger than a critical value
10 000 < Re1 < 10 500 and less than another critical value 15 000 < Re2 < 16 000, these authors
founded that the flow becomes periodic in time which indicates a Hopf bifurcation; the flow loses
time periodicity for Re ≥ Re2 . Ould Salihi [1998] founded a loss of stationnarity between 10 000
and 20 000. Auteri, Parolini, and Quartapelle [2002] estimated the critical value for the apparition
of the first instability to Re1 ≈ 8018. Erturk, Corke, and Gökçol [2005] computed steady driven
cavity solutions up to Re ⩽ 21 000. This result was infirmed by Gelhard, Lube, Olshanskii, and
Starcke [2005]: these authors estimated Re1 close to 8000, in agreement with Auteri, Parolini,
and Quartapelle [2002]. The 3D driven cavity has been investigated in Minev and Ethier [1998]
by the method of characteristic (see also Melchior, Legat, Van Dooren, and Wathen, 2012 for 3D
driven cavity computations). In conclusion, the exploration of the driven cavity at large Reynolds
number is a fundamental challenge in computational fluid dynamics.
Note that, instead of using a time-dependent scheme, that requires many time steps, it is possible
to directly compute the stationary solution of the Navier-Stokes problem, thanks to a nonlinear
solver. This alternative approach is presented in section 4.5, page 185, based on the discontinuous
Galerkin method. The discontinuous Galerkin method is much more robust and accurate than the
method of characteristics and is recomanded for serious problems.
92 Rheolef version 7.2
Chapter 3

Advanced and highly nonlinear


problems

3.1 Equation defined on a surface


This chapter deals with equations defined on a closed hypersurface. We present three different
numerical methods: the direct resolution of the problem on an explicit surface mesh generated
independently of Rheolef, the direct resolution on a surface mesh generated by Rheolef from a
volume mesh, and finally a level set type method based on a volume mesh in an h-narrow band
containing the surface. This last method allows one to define hybrid operators between surface
and volume-based finite element fields. These methods are demonstrated on two model problems
and two different surfaces.
Let us consider a closed surface Γ ∈ Rd , d = 2 or 3 and Γ is a connected C 2 surface of dimension
d − 1 with ∂Γ = 0. We first consider the following problem:
(P 1) find u, defined on Γ such that:

u − ∆s u = f on Γ (3.1)

where f ∈ L2 (Γ). For all function u defined on Γ, ∆s denotes the Laplace-Beltrami operator:

∆s u = divs (∇s u)

where ∇s and divs are the tangential derivative and the surface divergence along Γ, defined
respectively, for all scalar field φ and vector field v by:

∇s φ = (I − n ⊗ n) ∇φ
divs v = (I − n ⊗ n) : ∇v

Here, n denotes a unit normal on Γ.


We also consider the following variant of this problem:
(P 2) find u, defined on Γ such that:

−∆s u = f on Γ (3.2)

This second problem is similar to the first one: the Helmholtz operator I − ∆s has been replaced
by the Laplace-Beltrami one −∆s . In that case, the solution is defined up to a constant: if u is
a solution, then u + c is also a solution for any constant c ∈ R. Thus, we refers to (P 1) as the
Helmholtz-Beltrami problem and to (P 2) as the Laplace-Beltrami one.

93
94 Rheolef version 7.2

3.1.1 Approximation on an explicit surface mesh


The Helmholtz-Beltrami problem

Tanks to the surface Green formula (see appendix A.1.3), the variational formulation of problem
(P 1) writes:
(V F 1): find u ∈ H 1 (Γ) such that:

a(u, v) = l(v), ∀v ∈ H 1 (Γ)

where for all u, v ∈ H 1 (Γ),


Z
a(u, v) = (u v + ∇s u.∇s v) ds

l(v) = f v ds
Γ

Let k ⩾ 1 and consider a k-th order curved surface finite element mesh Γh of Γ. We define the
space Wh :
Wh = vh ∈ H 1 (Γh ); v|S ∈ Pk , ∀S ∈ Γh


The approximate problem writes:


(V F 1)h : find uh ∈ Wh such that:

a(uh , vh ) = l(vh ), ∀vh ∈ Wh

File 3.1: helmholtz_s.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sphere . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo gamma ( argv [1]);
8 size_t d = gamma . dimension ();
9 space Wh ( gamma , argv [2]);
10 trial u ( Wh ); test v ( Wh );
11 form a = integrate ( u * v + dot ( grad_s ( u ) , grad_s ( v )));
12 field lh = integrate ( f ( d )* v );
13 field uh ( Wh );
14 problem p ( a );
15 p . solve ( lh , uh );
16 dout << uh ;
17 }

Comments

The problem involves the Helmholtz operator and thus, the code is similar to ‘neumann-nh.cc’
presented page 32. Let us comments the only differences:
form a = integrate ( u * v + dot ( grad_s ( u ) , grad_s ( v )));

The form refers to the grad_s operator instead of the grad one, since only the coordinates related
to the surface are involved.
field lh = integrate ( f ( d )* v );

The right-hand-side does not involve any boundary term, since the surface Γ is closed: the bound-
ary domain ∂Γ = ∅. As test problem, the surface Γ is the unit circle when d = 2 and the unit
Chapter 3. Advanced and highly nonlinear problems 95

sphere when d = 3. The data f has been chosen as in Deckelnick et al. [2009, p. 17]. This choice
is convenient since the exact solution is known. Recall that the spherical coordinates (ρ, θ, ϕ) are
defined from the Cartesian ones (x0 , x1 , x2 ) by:
  
when x1 ⩾ 0
p
q  arccos x0 / x2 + x2
0 1
ρ = x20 + x21 + x22 , ϕ = arccos (x2 /ρ) , θ =  
otherwise
p
 2π − arccos x0 / x2 + x2
0 1

File 3.2: sphere.icc


1 struct p {
2 Float operator () ( const point & x ) const {
3 if ( d == 2) return 26*( pow ( x [0] ,5) - 10* pow ( x [0] ,3)* sqr ( x [1])
4 + 5* x [0]* pow ( x [1] ,4));
5 else return 3* sqr ( x [0])* x [1] - pow ( x [1] ,3);
6 }
7 p ( size_t d1 ) : d ( d1 ) {}
8 protected : size_t d ;
9 };
10 struct f {
11 Float operator () ( const point & x ) const {
12 if ( d == 2) return _p ( x )/ pow ( norm ( x ) ,5);
13 else return alpha * _p ( x );
14 }
15 f ( size_t d1 ) : d ( d1 ) , _p ( d1 ) , alpha (0) {
16 Float pi = acos ( Float ( -1));
17 alpha = -(13./8.)* sqrt (35./ pi );
18 }
19 protected : size_t d ; p _p ; Float alpha ;
20 };
21 struct u_exact {
22 Float operator () ( const point & x ) const {
23 if ( d == 2) return _f ( x )/(25+ sqr ( norm ( x )));
24 else return sqr ( norm ( x ))/(12+ sqr ( norm ( x )))* _f ( x );
25 }
26 u_exact ( size_t d1 ) : d ( d1 ) , _f ( d1 ) {}
27 protected : size_t d ; f _f ;
28 };
29 Float phi ( const point & x ) { return norm ( x ) - 1; }

How to run the program

The program compile as usual:

make helmholtz_s

A mesh of a circle is generated by:

mkgeo_ball -s -e 100 > circle.geo


geo circle -gnuplot

The mkgeo_ball is a convenient script that generates a mesh with the gmsh mesh generator. Then,
the problem resolution writes:

./helmholtz_s circle P1 > circle.field


field circle.field
field circle.field -elevation

The tridimensional case is similar:

mkgeo_ball -s -t 10 > sphere.geo


geo sphere.geo -stereo
96 Rheolef version 7.2

./helmholtz_s sphere.geo P1 > sphere.field


field sphere.field
field sphere.field -stereo
The solution is represented on Fig .3.1.left.

Figure 3.1: Helmholtz-Beltrami problem: high-order curved surface mesh and its corresponding
isoparametric solution: (top) order = 1; (bottom) order = 3.

Higher-order isoparametric finite elements can be considered for the curved geometry:
mkgeo_ball -s -e 30 -order 3 > circle-P3.geo
geo circle-P3.geo -subdivide 10
Observe the curved edges (see Fig .3.1). The -subdivide option allows a graphical representation
of the curved edges by subdividing each edge in ten linear parts, since graphical softwares are not
yet able to represent curved elements. The computation with the P3 isoparametric approximation
writes:
./helmholtz_s circle-P3 P3 > circle-P3.field
field circle-P3.field -elevation -gnuplot
Note that both the curved geometry and the finite element are second order. The tridimensional
counterpart writes simply:
Chapter 3. Advanced and highly nonlinear problems 97

mkgeo_ball -s -t 10 -order 3 > sphere-P3.geo


geo sphere-P3.geo -gnuplot
./helmholtz_s sphere-P3 P3 > sphere-P3.field
field sphere-P3.field
field sphere-P3.field -stereo

The solution is represented on Fig .3.1).right-bottom. The graphical representation is not yet able
to represent the high-order approximation: each elements is subdivided and a piecewise linear
representation is used in each sub-elements.

∥uh − πh (u)∥0,2,Ω ∥uh − πh (u)∥0,∞,Ω


10−2 10−2

2=k+1 2=k+1
10−4 10−4

10−6 10−6
3
3
k=1 k=1
10−8 4 k=2 10−8 4 k=2
k=3 k=3
10−2 10−1 10−2 10−1
h h
100
|uh − πh (u)|1,2,Ω

10−2 1=k

10−4
2

10−6 3

k=1
k=2
k=3
10−8
10−2 10−1
h

Figure 3.2: Curved non-polynomial surface: error analysis in L2 , L∞ and H 1 norms.

Since the exact solution is known, the error can be computed: this is done by the program
helmholtz_s_error.cc. This file is not presented here, as it is similar to some others examples,
but can be founded in the Rheolef example directory. Figure 3.2 plots the error in various norms
versus element size for different isoparametric approximations.
98 Rheolef version 7.2

The Laplace-Beltrami problem

This problem has been introduced in (3.2), page 93. While the treatment of the Helmholtz-
Beltrami problem was similar to the Helmholtz problem with Neumann boundary conditions, here,
the treatment of the Laplace-Beltrami problem is similar to the Laplace problem with Neumann
boundary conditions: see section 1.4, page 35. Note that for both problems, the solution is defined
up to a constant. Thus, the linear problem has a singular matrix. The ‘laplace_s.cc’ code is
similar to the ‘neumann-laplace.cc’ one, as presented in section 1.4. The only change lies one
the definition of the right-hand side.

File 3.3: laplace_s.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " torus . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo gamma ( argv [1]);
8 size_t d = gamma . dimension ();
9 space Wh ( gamma , argv [2]);
10 trial u ( Wh ); test v ( Wh );
11 form a = integrate ( dot ( grad_s ( u ) , grad_s ( v )));
12 field b = integrate ( v );
13 field lh = integrate ( f ( d )* v );
14 form A = {{ a, b },
15 { trans ( b ) , 0 }};
16 field Bh = { lh , 0 };
17 field Uh ( Bh . get_space () , 0);
18 A . set_symmetry ( true );
19 problem pa ( A );
20 pa . solve ( Bh , Uh );
21 dout << Uh [0];
22 }
Chapter 3. Advanced and highly nonlinear problems 99

File 3.4: torus.icc


1 static const Float R = 1;
2 static const Float r = 0.6;
3 Float phi ( const point & x ) {
4 return sqr ( sqrt ( sqr ( x [0])+ sqr ( x [1])) - sqr ( R )) + sqr ( x [2]) - sqr ( r );
5 }
6 void g et_ torus_ coord inates ( const point & x ,
7 Float & rho , Float & theta , Float & phi ) {
8 static const Float pi = acos ( Float ( -1));
9 rho = sqrt ( sqr ( x [2]) + sqr ( sqrt ( sqr ( x [0]) + sqr ( x [1])) - sqr ( R )));
10 phi = atan2 ( x [1] , x [0]);
11 theta = atan2 ( x [2] , sqrt ( sqr ( x [0]) + sqr ( x [1])) - R );
12 }
13 struct u_exact {
14 Float operator () ( const point & x ) const {
15 Float rho , theta , phi ;
16 g e t _ torus _coor dinate s (x , rho , theta , phi );
17 return sin (3* phi )* cos (3* theta + phi );
18 }
19 u_exact ( size_t d =3) {}
20 };
21 struct f {
22 Float operator () ( const point & x ) const {
23 Float rho , theta , phi ;
24 g e t _ torus _coor dinate s (x , rho , theta , phi );
25 Float fx = (9* sin (3* phi )* cos (3* theta + phi ))/ sqr ( r )
26 - ( -10* sin (3* phi )* cos (3* theta + phi ) - 6* cos (3* phi )* sin (3* theta + phi ))
27 / sqr ( R + r * cos ( theta ))
28 - (3* sin ( theta )* sin (3* phi )* sin (3* theta + phi ))
29 /( r *( R + r * cos ( theta )));
30 return fx ;
31 }
32 f ( size_t d =3) {}
33 };

As test problem, the surface Γ is the a torus when d = 3. The data f has been chosen as
in Olshanskii et al. [2009, p. 3355]. This choice is convenient since the exact solution is known.
Let R and r denotes the large and small torus radii, respectively. The torus coordinates (ρ, θ, ϕ)
are defined linked to the Cartesian ones by:
     
x0 cos(ϕ) cos(ϕ) cos(θ)
 x1  = R  sin(ϕ)  + ρ  sin(ϕ) cos(θ) 
x2 0 sin(θ)
Here ρ is the distance from the point to the circle in the x0 x1 plane around 0 with radius R,
θ is the angle from the positive (x0 , x1 , 0) to x0 and ϕ is the angle from the positive x0 axis to
(x0 , x1 , 0).

How to run the program ?

The surface mesh of the torus is generated by:

gmsh -2 torus.mshcad -format msh2 -o torus.msh


msh2geo torus.msh > torus.geo
geo torus.geo -stereo

The ‘torus.mshcad’ is not presented here: it can be founded in the Rheolef example directory.
Then, the computation and visualization writes:

make laplace_s
./laplace_s torus.geo P1 > torus.field
field torus.field
field torus.field -stereo
100 Rheolef version 7.2

Figure 3.3: Laplace-Beltrami problem on a torus: high-order curved surface mesh and its corre-
sponding isoparametric solution: (top) order = 1; (bottom) order = 2.

For a higher-order approximation:

gmsh -2 -order 2 torus.mshcad -format msh2 -o torus-P2.msh


msh2geo torus-P2.msh > torus-P2.geo
geo torus-P2.geo -gnuplot
./laplace_s torus-P2.geo P2 > torus-P2.field
field torus-P2.field -stereo

The solution is represented on Fig. 3.3. By editing ‘torus.mshcad’ and changing the density of
discretization, we can improve the approximate solution and converge to the exact solution. Due
to a bug [Saramito, 2012b] in the current gmsh version 2.5.1 the convergence is not optimal O(hk )
for higher values of k.
Chapter 3. Advanced and highly nonlinear problems 101

3.1.2 Building a surface mesh from a level set function


The previous method is limited to not-too-complex surface Γ, that can be described by a regular
finite element surface mesh Γh . When the surface change, as in a time-dependent process, complex
change of topology often occurs and the mesh Γh can degenerate or be too complex to be efficiently
meshed. In that case, the surface is described implicitly as the zero isosurface, or zero level set, of
a function:
Γ = {x ∈ Λ; ϕ(x) = 0}
where Λ ⊂ Rd is a bounding box of the surface Γ.
The following code automatically generates the mesh Γh of the surface described by the zero
isosurface of a discrete ϕh ∈ Xh level set function:
Γh = {x ∈ Λ; ϕh (x) = 0}
where Xh is a piecewise affine functional space over a mesh Th of Λ:
Xh = {φ ∈ L2 (Λ) ∩ C 0 (Λ); φ/K ∈ P1 , ∀K ∈ Th }
The polynomial approximation is actually limited here to first order: building higher order curved
finite element surface meshes from a level set function is planed for the future versions of Rheolef.
Finally, a computation, as performed in the previous paragraph can be done using Γh . We also
point out the limitations of this approach.

File 3.5: level_set_sphere.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sphere . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo lambda ( argv [1]);
8 level_set_option opts ;
9 opts . split_to_triangle
10 = ( argc > 2 && argv [2] == std :: string ( " - tq " )) ? false : true ;
11 space Xh ( lambda , " P1 " );
12 field phi_h = lazy_interpolate ( Xh , phi );
13 geo gamma = level_set ( phi_h , opts );
14 dout << gamma ;
15 }

Comments

All the difficult work of building the intersection mesh Γh , defined as the zero level set of the ϕh
function, is performed by the level_set function:
geo gamma = level_set ( phi_h , opts );

When d = 3, intersected tetrahedra leads to either triangular or quadrangular faces. By default,


quadrangular faces are split into two triangles. An optional -tq program flag allows one to conserve
quadrangles in the surface mesh: it set the split_to_triangle optional field to false.

How to run the program ?

After the compilation, generates the mesh of a bounding box Λ = [−2, 2]d of the surface and run
the program:
make level_set_sphere
mkgeo_grid -t 20 -a -2 -b 2 -c -2 -d 2 > square2.geo
./level_set_sphere square2.geo > circle.geo
geo circle.geo -gnuplot
102 Rheolef version 7.2

The computation of the previous paragraph can be reused:

./helmholtz_s circle.geo P1 | field -paraview -

Note that, while the bounding box mesh was uniform, the intersected mesh could present arbi-
trarily small edge length (see also Fig. 3.4):

geo -min-element-measure circle.geo


geo -max-element-measure circle.geo

Let us turn to the d = 3 case:

mkgeo_grid -T 20 -a -2 -b 2 -c -2 -d 2 -f -2 -g 2 > cube2.geo


./level_set_sphere cube2.geo | geo -upgrade - > sphere.geo
geo sphere.geo -stereo
./helmholtz_s sphere.geo P1 | field -

While the bounding box mesh was uniform, the triangular elements obtained by intersecting the
3D bounding box mesh with the level set function can present arbitrarily irregular sizes and shapes
(see also Fig. 3.4):

geo -min-element-measure -max-element-measure sphere.geo

Nevertheless, and surprisingly, Olshanskii et al. [2012] recently showed that the finite element
method converges on these irregular intersected families of meshes.
This approach can be extended to the Laplace-Beltrami problem on a torus:

sed -e ’s/sphere/torus/’ < level_set_sphere.cc > level_set_torus.cc


make level_set_torus
./level_set_torus cube2.geo | geo -upgrade - > torus.geo
geo torus.geo -stereo
./laplace_s torus.geo P1 | field -

Note that the intersected mesh is also irregular:

geo -min-element-measure -max-element-measure torus.geo


Chapter 3. Advanced and highly nonlinear problems 103

Figure 3.4: Building an explicit surface mesh from level set: (top) circle; (center) sphere; (bottom)
torus.
104 Rheolef version 7.2

3.1.3 The banded level set method


The banded level set method presents the advantages of the two previous methods without their
drawback: it applies to very general geometries, as described by a level set funtion, and stronger
convergence properties, as usual finite element methods. The previous intersection mesh can be
circumvented by enlarging the surface Γh to a band βh containing all the intersected elements of
Th (see Olshanskii et al., 2009, Abouorm, 2010, Dicko, 2011):
βh = {K ∈ Th ; K ∩ Γh ̸= ∅}
Then, we introduce Bh the piecewise affine functional space over βh :
Bh = {v ∈ L2 (βh ) ∩ C 0 (βh ); v/K ∈ P1 , ∀K ∈ Th }
The problem is extended from Γh to βh as:
(V F )h : find uh ∈ Bh such that:
a(uh , vh ) = l(vh ), ∀vh ∈ Bh
where, for all u, v ∈ Bh ,
Z
a(u, v) = (u v + ∇s u.∇s v) ds
Γh
Z
l(v) = f v ds
Γh

for all uh , vh ∈ Bh . Note that while uh and vh are defined over βh , the summations in the
variational formulations are restricted only to Γh ⊂ βh .

File 3.6: helmholtz_band_iterative.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 # include " sphere . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo lambda ( argv [1]);
8 size_t d = lambda . dimension ();
9 space Xh ( lambda , " P1 " );
10 field phi_h = lazy_interpolate ( Xh , phi );
11 band gamma_h ( phi_h );
12 space Bh ( gamma_h . band () , " P1 " );
13 trial u ( Bh ); test v ( Bh );
14 form a = lazy_integrate ( gamma_h , u * v + dot ( grad_s ( u ) , grad_s ( v )));
15 field lh = lazy_integrate ( gamma_h , f ( d )* v );
16 field uh ( Bh ,0);
17 solver_option sopt ;
18 sopt . max_iter = 10000;
19 sopt . tol = 1e -10;
20 minres ( a . uu () , uh . set_u () , lh . u () , eye () , sopt );
21 dout << catchmark ( " phi " ) << phi_h
22 << catchmark ( " u " ) << uh ;
23 }

Comments

The band is build directly from the level set function as:
band gamma_h ( phi_h );

The band structure is a small class that groups the surface mesh Γh , available as
gamma_h.level_set(), and the βh mesh, available as gamma_h.band(). It also manages some
correspondence between both meshes. Then, the space of piecewise affine functions over the band
is introduced:
Chapter 3. Advanced and highly nonlinear problems 105

space Bh ( gamma_h . band () , " P1 " );

Next, the bilinear form is computed by using the integrate function, with the band gamma_h as
a domain-like argument:
form a = integrate ( gamma_h , u * v + dot ( grad_s ( u ) , grad_s ( v )));

The right-hand side also admits the gamma_h argument:


field lh = integrate ( gamma_h , f ( d )* v );

Recall that summations for both forms and right-hand side will be performed on Γh , represented
by gamma_h.level_set(), while the approximate functional space is Bh . Due to this summation
on Γh instead of βh , the matrix of the system is singular [Olshanskii et al., 2009, Olshanskii and
Reusken, 2010, Abouorm, 2010] and the MINRES algorithm has been chosen to solve the linear
system:
minres ( a . uu () , uh . set_u () , lh . u () , eye () , sopt );

The eye() argument represents here the identity preconditioner, i.e. no preconditioner at all. It
has few influence of the convergence properties of the matrix and could be replaced by another
simple one: the diagonal of the matrix diag(a.uu()) without sensible gain of performance:
minres ( a . uu () , uh . set_u () , lh . u () , diag ( a . uu ()) , sopt );

See the reference manual for more about minres, e.g. on the Rheolef web site or as unix manual

man minres

How to run the program

The compilation and run writes:

make helmholtz_band_iterative
mkgeo_grid -T 20 -a -2 -b 2 -c -2 -d 2 -f -2 -g 2 > cube-20.geo
./helmholtz_band_iterative cube-20.geo > sphere-band.field

The run generates also two meshes (see Fig. 3.5): the intersection mesh and the band around it.
The solution is here defined on this band: this extension has no interpretation in terms of the
initial problem and can be restricted to the intersection mesh for visualization purpose:

make proj_band
./proj_band < sphere-band.field | field -

The ‘proj_band.cc’ is presented below. The run generates also the Γh mesh (see Fig. 3.5), required
for the visualization. The two-dimensional case is obtained simply by replacing the 3D bounding
box by a 2D one:

mkgeo_grid -t 20 -a -2 -b 2 -c -2 -d 2 > square-20.geo


./helmholtz_band_iterative square-20.geo > circle-band.field
./proj_band < circle-band.field | field -paraview -
./proj_band < circle-band.field | field -elevation -bw -stereo -
106 Rheolef version 7.2

File 3.7: proj_band.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 field phi_h ;
7 din >> catchmark ( " phi " ) >> phi_h ;
8 const space & Xh = phi_h . get_space ();
9 band gamma_h ( phi_h );
10 space Bh ( gamma_h . band () , " P1 " );
11 field uh ( Bh );
12 din >> catchmark ( " u " ) >> uh ;
13 space Wh ( gamma_h . level_set () , " P1 " );
14 gamma_h . level_set (). save ();
15 dout << lazy_interpolate ( Wh , uh );
16 }

3.1.4 Improving the banded level set method with a direct solver

The iterative algorithm previously used for solving the linear system is not optimal: for 3D
problems on a surface, the bidimensionnal connectivity of the sparse matrix suggests that a direct
sparse factorization would be much more efficient.
Recall that ϕh = 0 on Γh . Thus, if uh ∈ Bh is solution of the problem, then uh +αϕh|βh ∈ Bh is also
solution for any α ∈ R, where ϕh|βh ∈ Bh denotes the restriction of the level set function ϕh ∈ Xh
on the band βh . Thus there is multiplicity of solutions and the matrix of the problem is singular.
The direct resolution is still possible on a modified linear system with additional constraints in
order to recover the unicity of the solution. We impose the constraint that the solution uh should
be othogonal to ϕh|βh ∈ Bh . In some special cases, the band is composed of several connected
components (see Fig. 3.6): this appends when a vertex of the bounding box mesh belongs to Γh .
In that case, the constraint should be expressed on each connected component. Fig. 3.6 shows also
the case when a full side of an element is included in Γh : such an element of the band is called
isolated.
Chapter 3. Advanced and highly nonlinear problems 107

File 3.8: helmholtz_band.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 # include " sphere . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo lambda ( argv [1]);
8 size_t d = lambda . dimension ();
9 space Xh ( lambda , " P1 " );
10 field phi_h = lazy_interpolate ( Xh , phi );
11 band gamma_h ( phi_h );
12 field phi_h_band = phi_h [ gamma_h . band ()];
13 space Bh ( gamma_h . band () , " P1 " );
14 Bh . block ( " isolated " );
15 Bh . unblock ( " zero " );
16 trial u ( Bh ); test v ( Bh );
17 form a = lazy_integrate ( gamma_h , u * v + dot ( grad_s ( u ) , grad_s ( v )));
18 field lh = lazy_integrate ( gamma_h , f ( d )* v );
19 vector < field > b ( gamma_h . n_c onnect ed_co mponen t ());
20 vector < Float > z ( gamma_h . n_c onnect ed_co mponen t () , 0);
21 for ( size_t i = 0; i < b . size (); i ++) {
22 const domain & cci = gamma_h . band () [ " cc " + to_string ( i )];
23 field phi_h_cci ( Bh , 0);
24 phi_h_cci [ cci ] = phi_h_band [ cci ];
25 b [ i ] = phi_h_cci ;
26 }
27 form A = {{ a , trans ( b ) } ,
28 { b, 0 }};
29 field Fh = { lh , z };
30 A . set_symmetry ( true );
31 problem pa ( A );
32 field Uh ( Fh . get_space () , 0);
33 pa . solve ( Fh , Uh );
34 dout << catchmark ( " phi " ) << phi_h
35 << catchmark ( " u " ) << Uh [0];
36 }

Comments

The management of the special sides and vertices that are fully included in Γh is perfomed by:

Bh . block ( " isolated " );


Bh . unblock ( " zero " );

The addition of linear constraints is similar to the ‘neumann-laplace.cc’ code, as presented in


section 1.4:

form A = {{ a , trans ( b )} ,
{ b, 0 }};

Here b is a vector<field>, i.e. a vector of linear constraints, one per connected component of
the band βh .

How to run the program

The commands are similar to the previous iterative implementation, just replacing
helmholtz_band_iterative by helmholtz_band.
This approach could be also adapted to the Laplace-Beltrami problem on the torus.
108 Rheolef version 7.2

File 3.9: laplace_band.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 # include " torus . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo lambda ( argv [1]);
8 size_t d = lambda . dimension ();
9 space Xh ( lambda , " P1 " );
10 field phi_h = lazy_interpolate ( Xh , phi );
11 band gamma_h ( phi_h );
12 field phi_h_band = phi_h [ gamma_h . band ()];
13 space Bh ( gamma_h . band () , " P1 " );
14 Bh . block ( " isolated " );
15 Bh . unblock ( " zero " );
16 trial u ( Bh ); test v ( Bh );
17 form a = lazy_integrate ( gamma_h , dot ( grad_s ( u ) , grad_s ( v )));
18 field c = lazy_integrate ( gamma_h , v );
19 field lh = lazy_integrate ( gamma_h , f ( d )* v );
20 vector < field > b ( gamma_h . n_c onnect ed_co mponen t ());
21 vector < Float > z ( gamma_h . n_c onnect ed_co mponen t () , 0);
22 for ( size_t i = 0; i < b . size (); i ++) {
23 const domain & cci = gamma_h . band () [ " cc " + to_string ( i )];
24 field phi_h_cci ( Bh , 0);
25 phi_h_cci [ cci ] = phi_h_band [ cci ];
26 b [ i ] = phi_h_cci ;
27 }
28 form A = {{ a , trans ( b ) , c } ,
29 { b, 0, 0 },
30 { trans ( c ) , 0 , 0 }};
31 field Fh = { lh , z, 0 };
32 field Uh ( Fh . get_space () , 0);
33 A . set_symmetry ( true );
34 problem pa ( A );
35 pa . solve ( Fh , Uh );
36 dout << catchmark ( " phi " ) << phi_h
37 << catchmark ( " u " ) << Uh [0];
38 }

Comments

The code is similar to the previous one helmholtz_band.cc. Since the solution is defined up to a
constant, an additional linear constraint has to be inserted:
Z
uh dx = 0
Γh

This writes:
field c = integrate ( gamma_h , v );
form A = {{ a , trans ( b ) , c } ,
{ b, 0, 0 },
{ trans ( c ) , 0 , 0 }};

How to run the program

make laplace_band
mkgeo_grid -T 20 -a -2 -b 2 -c -2 -d 2 -f -2 -g 2 > cube-20.geo
./laplace_band cube-20.geo > torus-band.field
./proj_band < torus-band.field | field -stereo -

The solution is represented on Fig. 3.5.bottom.


Chapter 3. Advanced and highly nonlinear problems 109

Figure 3.5: The banded level set method: (top) circle; (center) sphere; (bottom) torus.
110 Rheolef version 7.2

cc0
cc1
cc2
cc3
cc4
cc5
cc6
cc7
cc8
cc9
isolated
zero

Figure 3.6: The banded level set method: the band is composed of several connected components.
Chapter 3. Advanced and highly nonlinear problems 111

3.2 The highly nonlinear p-laplacian problem


3.2.1 Problem statement
Let us consider the classical p-Laplacian problem with homogeneous Dirichlet boundary conditions
in a domain bounded Ω ⊂ Rd , d = 1, 2, 3:
(P): find u, defined in Ω such that:

−div η |∇u|2 ∇u = f in Ω
 

u = 0 on ∂Ω
p−2
where η : z ∈ R+ 7−→ z 2 ∈ R+ . Several variants of the η can be considered: see Saramito
[2016b] for practical and useful examples: this problem represents a pipe flow of a non-Newtonian
power-law fluid. Here p ∈]1, +∞[ and f are known. For the computational examples, we choose
f = 1. When p = 2, this problem reduces to the linear Poisson problem with homogeneous
Dirichlet boundary conditions. Otherwise, for any p > 1, the nonlinear problem is equivalent to
the following minimization problem:
(MP): find u ∈ W01,p (Ω) such that:
Z Z
1 2

u = arg min H |∇v| dx − f v dx,
v∈W 1,p (Ω) 2 Ω
0

where H denotes the primitive of η:


z
2z p
Z
H(z) = η(z) dz =
0 p

Here W01,p (Ω) denotes the usual Sobolev spaces of functions in W 1,p (Ω) We also assume that
f ∈W −1,p
(Ω), where W0−1,p (Ω) denotes the dual space of W01,p (Ω) that vanishes on the bound-
ary [Brezis, 1983, p. 118]. The variational formulation of this problem expresses:
(VF): find u ∈ W01,p (Ω) such that:

a(u; u, v) = l(v), ∀v ∈ W01,p (Ω)

where a(., .) and l(.) are defined for any u0 , u, v ∈ W 1,p (Ω) by
Z
η |∇u0 |2 ∇u.∇v dx, ∀u, v ∈ W01,p (Ω) (3.3)

a(u0 ; u, v) =
ZΩ
l(v) = f v dx, ∀u, v ∈ L2 (Ω) (3.4)

The quantity a(u; u, u)1/p = ∥∇u∥0,p,Ω induces a norm in W01,p , equivalent to the standard norm.
The form a(.; ., .) is bilinear with respect to the two last variable and is related to the energy form.

3.2.2 The fixed-point algorithm


Principe of the algorithm

This nonlinear problem is then reduced to a sequence of linear subproblems by using the fixed-point
algorithm. The sequence u(n) n⩾0 is defined by recurrence as:

• n = 0: let u(0) ∈ W01,p (Ω) be known.


• n ⩾ 0: suppose that u(n) ∈ W01,p (Ω) is known and find u∗ ∈ W01,p (Ω) such that:
 
a u(n) ; u∗ , v = l(v), ∀v ∈ W01,p (Ω)
112 Rheolef version 7.2

and then set

u(n+1) = ωu∗ + (1 − ω) ∗ u(n)

Here ω > 0 is the relaxation parameter: when ω = 1 we obtain the usual un-relaxed fixed point
algorithm. For stiff
 nonlinear problems, we will consider the under-relaxed case 0 < ω < 1. 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 satisfies u = G(u), it is a fixed-point of G.
Let us introduce a mesh Th of Ω and the finite dimensional space Xh of continuous piecewise poly-
nomial functions and Vh , the subspace of Xh containing elements that vanishes on the boundary
of Ω:

= {vh ∈ C00 Ω ; vh/K ∈ Pk , ∀K ∈ Th }



Xh
Vh = {vh ∈ Xh ; vh = 0 on ∂Ω}

(n)
where k = 1 or 2. The approximate problem expresses: suppose that uh ∈ Vh is known and find
u∗h ∈ Vh such that:

 
(n)
a uh ; u∗h , vh = l(vh ), ∀vh ∈ Vh

By developing u∗h on a basis of Vh , this problem reduces to a linear system.


Chapter 3. Advanced and highly nonlinear problems 113

File 3.10: p_laplacian_fixed_point.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " eta . h "
5 # include " dirichlet . icc "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 geo omega ( argv [1]);
9 Float eps = std :: numeric_limits < Float >:: epsilon ();
10 string approx = ( argc > 2) ? argv [2] : " P1 " ;
11 Float p = ( argc > 3) ? atof ( argv [3]) : 1.5;
12 Float w = ( argc > 4) ? ( is_float ( argv [4]) ? atof ( argv [4]) :2/ p ) :1;
13 Float tol = ( argc > 5) ? atof ( argv [5]) : 1 e5 * eps ;
14 size_t max_it = ( argc > 6) ? atoi ( argv [6]) : 500;
15 derr << " # P - Laplacian problem by fixed - point : " << endl
16 << " # geo = " << omega . name () << endl
17 << " # approx = " << approx << endl
18 << " # p = " << p << endl
19 << " # w = " << w << endl
20 << " # tol = " << tol << endl ;
21 space Xh ( omega , approx );
22 Xh . block ( " boundary " );
23 trial u ( Xh ); test v ( Xh );
24 form m = integrate ( u * v );
25 problem pm ( m );
26 field uh ( Xh ) , uh_star ( Xh , 0.);
27 uh [ " boundary " ] = uh_star [ " boundary " ] = 0;
28 field lh = integrate ( v );
29 dirichlet ( lh , uh );
30 derr << " # n r v " << endl ;
31 Float r = 1 , r0 = 1;
32 size_t n = 0;
33 do {
34 form a = integrate ( compose ( eta ( p ) , norm2 ( grad ( uh )))* dot ( grad ( u ) , grad ( v )));
35 field mrh = a * uh - lh ;
36 field rh ( Xh , 0);
37 pm . solve ( mrh , rh );
38 r = rh . max_abs ();
39 if ( n == 0) { r0 = r ; }
40 Float v = ( n == 0) ? 0 : log10 ( r0 / r )/ n ;
41 derr << n << " " << r << " " << v << endl ;
42 if ( r <= tol || n ++ >= max_it ) break ;
43 problem p ( a );
44 p . solve ( lh , uh_star );
45 uh = w * uh_star + (1 - w )* uh ;
46 } while ( true );
47 dout << catchmark ( " p " ) << p << endl
48 << catchmark ( " u " ) << uh ;
49 return ( r <= tol ) ? 0 : 1;
50 }

Comments

The
 implementation
 with Rheolef involves a weighted forms: the tensor-valued weight
2
(n)
η ∇uh is inserted in the variational expression passed to the integrate function. The
construction of the weighted form a(.; ., .) writes:
form a = integrate ( compose ( eta ( p ) , norm2 ( grad ( uh )))* dot ( grad ( u ) , grad ( v )));
 
2
(n)
Remarks the usage of the compose, norm2 and grad library functions. The weight η ∇uh is
represented by the compose(eta(p),norm2(grad(uh))) sub-expression. This weight is evaluated
on the fly at the quadrature nodes during the assembly process implemented by the integrate
function. Also, notice the distinction between uh , that represents the value of the solution at step
114 Rheolef version 7.2

n, and the trial u and test v functions, that represents any elements of the function space Xh .
These functions appear in the dot(grad(u),grad(v)) sub-expression.

File 3.11: eta.h


1 struct eta {
2 Float operator () ( const Float & z ) const {
3 check_macro ( z != 0 || p > 2 , " eta : division by zero ( HINT : check mesh ) " );
4 return pow (z , (p -2)/2);
5 }
6 Float derivative ( const Float & z ) const {
7 check_macro ( z != 0 || p > 4 , " eta ’: division by zero ( HINT : check mesh ) " );
8 return 0.5*( p -2)* pow (z , (p -4)/2);
9 }
10 eta ( const Float & q ) : p ( q ) {}
11 Float p ;
12 };

The η function is implemented separately, in file named eta.h in order to easily change its
definition. The derivative member function is not yet used here: it is implemented for a
forthcoming application (the Newton method). Note the guards that check for division by zero
and send a message related to the mesh: this will be commentated in the next paragraph.
Finally, the fixed-point algorithm is initiated with u(0) as the solution of the linear problem
associated to p = 2, i.e. the standard Poisson problem with Dirichlet boundary conditions.
File 3.12: dirichlet.icc
1 void dirichlet ( const field & lh , field & uh ) {
2 const space & Xh = lh . get_space ();
3 trial u ( Xh ); test v ( Xh );
4 form a = integrate ( dot ( grad ( u ) , grad ( v )));
5 problem p ( a );
6 p . solve ( lh , uh );
7 }

Running the program

Compile the program, as usual:

make p_laplacian_fixed_point

and enter the commands:

mkgeo_ugrid -t 50 > square.geo


geo square.geo

The triangular mesh has a boundary domain named boundary.

./p_laplacian_fixed_point square.geo P1 1.5 > square.field

Run the field visualization:

field square.field -elevation -stereo


field square.field -cut -origin 0.5 0.5 -normal 1 1 -gnuplot

The first command shows an elevation view of the solution (see Fig. 3.7) while the second one
shows a cut along the first bisector x0 = x1 . Observe that the solution becomes flat at the center
when p decreases. The p = 2 case, corresponding to the linear case, is showed for the purpose of
comparison.
There is a technical issue concerning the mesh: the computation could failed on some mesh that
presents at least one triangle with two edges on the boundary:
Chapter 3. Advanced and highly nonlinear problems 115

Figure 3.7: The p-Laplacian for d = 2: elevation view for p = 1.25 (left), p = 2 (center) and
p = 2.5 (right).

mkgeo_grid -t 50 > square-bedge.geo


geo square-bedge.geo
./p_laplacian_fixed_point square-bedge.geo P1 1.5 > square-bedge.field

The computation stops and claims a division by zero: the three nodes of such a triangle, the three
nodes are on the boundary, where uh = 0 is prescribed: thus ∇uh = 0 uniformly inside this ele-
ment. Note that this failure occurs only for linear approximations: the computation works well on
such meshes for Pk approximations with k ⩾ 2. While the mkgeo_grid generates uniform meshes
that have such triangles, the mkgeo_ugrid calls the gmsh generator that automatically splits the
triangles with two boundary edges. When using bamg, you should consider the -splitpbedge
option.

Convergence properties of the fixed-point algorithm

The fixed-point algorithm prints also rn , the norm of the residual term, at each iteration n, and the
convergence rate vn = log10 (rn /r0 )/n. The residual term of the non-linear variational formulation
is defined by:
   
(n) (n) (n) (n)
rh ∈ Vh and m rh , vh = a uh ; uh , vh − l(vh ), ∀vh ∈ Vh

(n) (n)
where m(., .) denotes the L2 scalar product. Clearly, uh is a solution if and only if rh = 0.
For clarity, let us drop temporarily the n index of the current iteration. The field rh ∈ Vh can be
extended as a field rh ∈ Xh with vanishing components on the boundary. The previous relation
writes, after expansion of the bilinear forms and fields on the unknown and blocked parts (see
page 16 for the notations):

m.uu*rh.u = a.uu*uh.u + a.ub*ub.b - lh.u


rh.b = 0

This relation expresses that the residual term rh is obtained by solving a linear system involving
the mass matrix.
It remains to choose a good norm for estimating this residual term. For the corresponding con-
tinuous formulation, we have:

r = −div η |∇u|2 ∇u − f ∈ W −1,p (Ω)


 

Thus, for the continuous formulation, the residual term may be measured with the W −1,p (Ω)
116 Rheolef version 7.2

norm. It is defined, for all φ ∈ W −1,p (Ω), by duality:

⟨φ, v⟩
∥φ∥−1,p,Ω = sup = sup ⟨φ, v⟩
φ∈W
1,p
(Ω)
∥v∥ 1,p,Ω 1,p
v∈W0 (Ω)
0
v̸=0 ∥v∥1,p,Ω =1

where ⟨., .⟩ denotes the duality bracked between W01,p (Ω) and W −1,p (Ω).
By analogy, let us introduce the discrete W −1,p (Ω) norm, denoted as ∥.∥−1,h , defined by duality
for all φh ∈ Vh by:
∥φh ∥−1,h = sup ⟨φh , vh ⟩
vh ∈Vh
∥vh ∥1,p,Ω =1

The dual of space of the finite element space Vh is identified to Vh and the duality bracket is the
Euclidean scalar product of Rdim(Vh ) . Then, ∥φh ∥−1,h is the largest absolute value of components
of φh considered as a vector of Rdim(Vh ) . With the notations of the Rheolef library, it simply
writes:

Float r = rh.u().max_abs()

(n) h = 1/10 (n) k =1


rh rh
−1,h h = 1/20 −1,h k =2
h = 1/30 k =3
1 h = 1/40 1 k =4
h = 1/50 k =5
p = 3/2 p = 3/2
10−5 10−5

10−10 10−10

10−15 10−15
0 25 50 0 25 50
n n
(n)
rh p = 2.95 (n)
rh p = 1.15
−1,h p = 2.90 −1,h p = 1.25
p = 2.50 p = 1.50
1 1

10−5 10−5

10−10 10−10

10−15 10−15
0 250 500 0 100 200
n n

Figure 3.8: The fixed-point algorithm on the p-Laplacian for d = 2: when p = 3/2, independence
of the convergence properties of the residue (top-left) with mesh refinement; (top-right) with
polynomial order Pk ; when h = 1/50 and k = 1, convergence (bottom-left) for p > 2 and (bottom-
right) for p < 2.
Chapter 3. Advanced and highly nonlinear problems 117

Fig 3.8.top-left shows that the residual term decreases exponentially versus n, since the slope of
the plot in semi-log scale tends to be strait. Moreover, observe that the slope is independent of
the mesh size h. Also, by virtue of the previous careful definition of the residual term and its
corresponding norm, all the slopes falls into a master curve.
These invariance properties applies also to the polynomial approximation Pk : Fig 3.8.top-right
shows that all the curves tends to collapse when k increases. Thus, the convergence properties
of the algorithm are now investigated on a fixed mesh h = 1/50 and for a fixed polynomial
approximation k = 1.
Fig 3.8.bottom-left and 3.8.bottom-right show the convergence versus the power-law index p:
observe that the convergence becomes easier when p approaches p = 2, where the problem is linear.
In that case, the convergence occurs in one iteration. Nevertheless, it appears two limitations.
From one hand, when p → 3 the convergence starts to slow down and p ⩾ 3 cannot be solved
by this algorithm (it will be solved later in this chapter). From other hand, when p → 1, the
convergence slows down too and numerical rounding effets limits the convergence: the machine
precision canot be reached. Let us introduce the convergence rate vn = log10 (rn /r0 )/n it tends to

v̄ computation v̄ computation: p < 2


computation: p > 2
fit: − log10 |p − 2|

2 2

1 1

0 0
1 2 3 10−3 10−2 10−1 100
p |p − 2|

Figure 3.9: The fixed-point algorithm on the p-Laplacian for d = 2: (left) convergence rate versus
p; (right) convergence rate versus p in semi-log scale.

a constant, denoted as v̄ and: rn ≈ r0 × 10−v̄ n . Observe on Fig 3.9.left 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 for p = 1 and p = 3 since the algorithm diverges. Fig 3.9.right shows the
same plot in semi-log scale and shows that v̄ behaves as: v̄ ≈ − log10 |p − 2|. This study shows
that the residual term of the fixed point algorithm behaves as:

rn ≈ r0 |p − 2|n

Improvement by relaxation

The relaxation parameter can improve the fixed-point algorithm: for instance, for p = 3 and
ω = 0.5 we get a convergent sequence:

./p_laplacian_fixed_point square.geo P1 3 0.5 > square.field

Observe on Fig. 3.10 the effect on the relaxation parameter ω upon the convergence rate v̄: for
p < 2 it can improve it and for p > 2, it can converge when p > 3. For each p, there is clearly an
optimal relaxation parameter, denoted by ωopt . A simple fit shows that (see Fig. 3.10.bottom-left):

ωopt = 2/p
118 Rheolef version 7.2

v̄ p = 1.2 v̄ p=3
p = 1.3 p=4
p = 1.4 p=5
p = 1.5 p=6
0.4 0.4

0.3 0.3

0.2 0.2

0.1 0.1

0 0
0 0.5 1 1.5 2 0 0.25 0.5 0.75 1
ω ω
2 v̄opt v̄ when ω = ωopt
computation: ωopt
ωopt fit: ωopt (p) = 2/p v̄ when ω = 1
2.5
1.5
2

1 1.5

1
0.5
0.5

0 0
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
p p

Figure 3.10: The fixed-point algorithm on the p-Laplacian for d = 2: effect of the relaxation
parameter ω (top-left) when p < 2; (top-right) when p > 2; (bottom-left) optimal ωopt ; (bottom-
right) optimal v̄opt .

Let us denote v̄opt the corresponding rate of convergence. Fig. 3.10.top-right shows that the
convergence is dramatically improved when p > 2 while the gain is less pronounced when p <
2. Coveniently replacing the extra parameter ω on the command line by - leads to compute
automatically ω = ωopt : the fixed-point algorithm is always convergent with an optimal convergent
rate, e.g.:

./p_laplacian_fixed_point square.geo P1 4.0 - > square.field

There is no way to improve more the fixed point algorithm: the next paragraph shows a different
algorithm that dramatically accelerates the computation of the solution.
Chapter 3. Advanced and highly nonlinear problems 119

3.2.3 The Newton algorithm


Principe of the algorithm

An efficient alternative to the fixed-point algorithm is to solve the nonlinear problem (P ) by using
the Newton algorithm. Let us consider the following operator:

F : W01,p (Ω) −→ W −1,p (Ω)  


u 7−→ F (u) = −div η |∇u|2 ∇u − f

The F operator computes simply the residual term and the problem expresses now as: find u ∈
W01,p (Ω) such that F (u) = 0.
The Newton algorithm reduces the nonlinear problem into a sequence of linear subproblems: the
sequence u(n) n⩾0 is classically defined by recurrence as:


• n = 0: let u(0) ∈ W01,p (Ω) be known.

• n ⩾ 0: suppose that u(n) is known, find δu(n) , defined in Ω, such that:


   
F ′ u(n) δu(n) = −F u(n)

and then compute explicitly:


u(n+1) := u(n) + δu(n)

The notation F ′ (u) stands for the Fréchet derivative of F , as an operator from W −1,p (Ω) into
W01,p (Ω). For any r ∈ W −1,p (Ω), the linear tangent problem writes:
find δu ∈ W01,p (Ω) such that:

F ′ (u) δu = −r
After the computation of the Fréchet derivative, we obtain the strong form of this problem:
(LT ): find δu, defined in Ω, such that

−div η |∇u|2 ∇(δu) + 2η ′ |∇u|2 {∇u.∇(δu)} ∇u = −r in Ω


  

δu = 0 on ∂Ω

where
1 p−4
η ′ (z) = (p − 2)z 2 , ∀z > 0
2
This is a Poisson-like problem with homogeneous Dirichlet boundary conditions and a non-constant
tensorial coefficient. The variational form of the linear tangent problem writes:
(V LT ): find δu ∈ W01,p (Ω) such that

a1 (u; δu, δv) = l1 (v), ∀δv ∈ W01,p (Ω)

where the a1 (.; ., .) is defined for any u, δu, δv ∈ W01,p (Ω) by:
Z
η |∇u|2 ∇(δu).∇(δv) + 2η ′ |∇u|2 {∇u.∇(δu)} {∇u.∇(δv)} dx
  
a1 (u; δu, δv) =

Z
l1 (v) = − r v dx

For any ξ ∈ Rd let us denote by ν(ξ) the following d × d matrix:

ν(ξ) = η |ξ|2 I + 2η ′ |ξ|2 ξ ⊗ ξ


 
120 Rheolef version 7.2

where I stands for the d-order identity matrix. Then the a1 expresses in a more compact form:
Z
a1 (u; δu, δv) = (ν(∇u)∇(δu)) .∇(δv) dx

Clearly a1 is linear and symmetric with respect to the two last variables.

File 3.13: p_laplacian_newton.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " p_laplacian . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 Float eps = std :: numeric_limits < Float >:: epsilon ();
9 string approx = ( argc > 2) ? argv [2] : " P1 " ;
10 Float p = ( argc > 3) ? atof ( argv [3]) : 1.5;
11 Float tol = ( argc > 4) ? atof ( argv [4]) : 1 e5 * eps ;
12 size_t max_iter = ( argc > 5) ? atoi ( argv [5]) : 500;
13 derr << " # P - Laplacian problem by Newton : " << endl
14 << " # geo = " << omega . name () << endl
15 << " # approx = " << approx << endl
16 << " # p = " << p << endl
17 << " # tol = " << tol << endl
18 << " # max_iter = " << max_iter << endl ;
19 p_laplacian F (p , omega , approx );
20 field uh = F . initial ();
21 int status = newton (F , uh , tol , max_iter , & derr );
22 dout << setprecision ( numeric_limits < Float >:: digits10 )
23 << catchmark ( " p " ) << p << endl
24 << catchmark ( " u " ) << uh ;
25 return status ;
26 }

File 3.14: p_laplacian.h


1 class p_laplacian {
2 public :
3 typedef field value_type ;
4 typedef Float float_type ;
5 p_laplacian ( Float p , const geo & omega , string approx );
6 field initial () const ;
7 field residue ( const field & uh ) const ;
8 void update_derivative ( const field & uh ) const ;
9 field derivative_solve ( const field & mrh ) const ;
10 field d erivat ive_t rans_m ult ( const field & mrh ) const ;
11 Float space_norm ( const field & uh ) const ;
12 Float dual_space_norm ( const field & mrh ) const ;
13 Float p ;
14 space Xh ;
15 field lh ;
16 form m ;
17 problem pm ;
18 mutable form a1 ;
19 mutable problem pa1 ;
20 };
21 # include " p_laplacian1 . icc "
22 # include " p_laplacian2 . icc "

Comments

The Newton algorithm is implemented in a generic way, for any F function, by the newton function
of the Rheolef library. The reference manual for the newton generic function is available online:

man newton
Chapter 3. Advanced and highly nonlinear problems 121

The function F and its derivative F ′ are provided by a template class argument. Here,
the p_laplacian class describes our F function, i.e. our problem to solve: its inter-
face is defined in the file ‘p_laplacian.h’ and its implementation in ‘p_laplacian1.icc’
and ‘p_laplacian2.icc’. The introduction of the class p_laplacian will allow an easy explo-
ration of some variants of the Newton algorithm for this problem, as we will see in the next section.
File 3.15: p_laplacian1.icc
1 # include " eta . h "
2 # include " nu . h "
3 # include " dirichlet . icc "
4 p_laplacian :: p_laplacian ( Float p1 , const geo & omega , string approx )
5 : p ( p1 ) , Xh () , lh () , m () , pm () , a1 () , pa1 () {
6 Xh = space ( omega , approx );
7 Xh . block ( " boundary " );
8 trial u ( Xh ); test v ( Xh );
9 lh = integrate ( v );
10 m = integrate ( u * v );
11 pm = problem ( m );
12 }
13 field p_laplacian :: initial () const {
14 field uh ( Xh , 0);
15 dirichlet ( lh , uh );
16 return uh ;
17 }
18 field p_laplacian :: residue ( const field & uh ) const {
19 trial u ( Xh ); test v ( Xh );
20 form a = integrate ( compose ( eta ( p ) , norm2 ( grad ( uh )))* dot ( grad ( u ) , grad ( v )));
21 field mrh = a * uh - lh ;
22 mrh . set_b () = 0;
23 return mrh ;
24 }
25 void p_laplacian :: update_derivative ( const field & uh ) const {
26 size_t d = Xh . get_geo (). dimension ();
27 trial u ( Xh ); test v ( Xh );
28 a1 = integrate ( dot ( compose ( nu < eta >( eta ( p ) , d ) , grad ( uh ))* grad ( u ) , grad ( v )));
29 pa1 = problem ( a1 );
30 }
31 field p_laplacian :: derivative_solve ( const field & rh ) const {
32 field delta_uh ( Xh ,0);
33 pa1 . solve ( rh , delta_uh );
34 return delta_uh ;
35 }

The residual term F (uh ) is computed by the member function residual while the resolution of
F ′ (uh )δuh = M rh is performed by the function derivative_solve. The derivative F ′ (uh ) is
computed separately by the function update_derivative:

a1 = integrate ( dot ( compose ( nu < eta >( eta ( p ) , d ) , grad ( uh ))* grad ( u ) , grad ( v )));

Note that the a1 (u; ., .) bilinear form is a tensorial weighted form, where ν = ν(∇u) is the weight
tensor. The tensorial weight ν is inserted as (ν∇u).∇v in the variational expression for the
integrate function. As the tensor ν is symmetric, the bilinear form a1 (., .) is also symmetric.
The linear system involving the derivative F ′ (uh ) is solved by the p_laplacian member function
derivative_solve. Finally, applying the generic Newton method requires a stopping criteria
on the residual term: this is the aim of the member function dual_space_norm. The three last
member functions are not used by the Newton algorithm, but by its extension, the damped Newton
method, that will be presented later.
122 Rheolef version 7.2

File 3.16: p_laplacian2.icc


1 field p_laplacian :: deriv ative _trans _mult ( const field & mrh ) const {
2 field rh ( Xh , 0);
3 pm . solve ( mrh , rh );
4 field mgh = a1 * rh ;
5 mgh . set_b () = 0;
6 return mgh ;
7 }
8 Float p_laplacian :: space_norm ( const field & uh ) const {
9 return sqrt ( m ( uh , uh ));
10 }
11 Float p_laplacian :: dual_space_norm ( const field & mrh ) const {
12 field rh ( Xh , 0);
13 pm . solve ( mrh , rh );
14 return sqrt ( dual ( mrh , rh ));
15 }

The ν function is implemented for a generic η function, as a class-function that accept as template
agument another class-function.

File 3.17: nu.h


1 template < class Function >
2 struct nu {
3 tensor operator () ( const point & grad_u ) const {
4 Float x2 = norm2 ( grad_u );
5 Float a = f ( x2 );
6 Float b = 2* f . derivative ( x2 );
7 tensor value ;
8 for ( size_t i = 0; i < d ; i ++) {
9 value (i , i ) = a + b * grad_u [ i ]* grad_u [ i ];
10 for ( size_t j = 0; j < i ; j ++)
11 value (j , i ) = value (i , j ) = b * grad_u [ i ]* grad_u [ j ];
12 }
13 return value ;
14 }
15 nu ( const Function & f1 , size_t d1 ) : f ( f1 ) , d ( d1 ) {}
16 Function f ;
17 size_t d;
18 };

Running the program

Enter:

make p_laplacian_newton
mkgeo_ugrid -t 50 > square.geo
./p_laplacian_newton square.geo P1 3 > square.field
field square.field -elevation -stereo

The program prints at each iteration n, the residual term rn in discrete L2 (Ω) norm. Convergence
occurs in less than ten iterations: it dramatically improves the previous algorithm (see Fig. 3.11).
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. Figs. 3.12.top-left and. 3.12.top-bottom shows that the algorithm converge
when p ⩾ 3 and that the convergence properties are independent of the mesh size h and the
polynomial order k. There are still two limitations of the method. From one hand, the Newton
algorithm is no more independent of h and k when p ⩽ 3/2 and to tends to diverges in that
case when h tends to zero (see Fig. 3.12.bottom-left). From other hand, when p becomes large
(see Fig. 3.12.bottom-right), an overshoot in the convergence tends to increase and destroy the
convergence, due to rounding problems. In order to circumvent these limitations, another strategy
is considered in the next section: the damped Newton algorithm.
Chapter 3. Advanced and highly nonlinear problems 123

(n) fixed point ω = 2/3


rh
−1,h Newton

1 p=3

10−5

10−10

10−15
0 5 10 15 20 25
n

Figure 3.11: The Newton algorithm on the p-laplacian for d = 2: comparison with the fixed-point
algorithm.
124 Rheolef version 7.2

(n) h = 1/10 (n) k =1


rh rh
−1,h h = 1/20 −1,h k =2
h = 1/30 k =3
1 h = 1/40 1 k =4
h = 1/50 k =5
p=3 p=3
10−5 10−5

10−10 10−10

10−15 10−15
0 5 10 15 20 25 0 5 10 15 20 25
n n
(n)
rh h = 1/10 (n)
rh p = 2.5
−1,h h = 1/20 −1,h p = 3.0
h = 1/30 p = 3.5
1 h = 1/40 100
h = 1/50

10−5 p = 3/2 10−5

10−10 10−10

10−15 10−15
0 50 100 0 25
n n

Figure 3.12: The Newton algorithm on the p-Laplacian for d = 2: (top-left) comparison with the
fixed-point algorithm; when p = 3, independence of the convergence properties of the residue (top-
left) with mesh refinement; (top-right) with polynomial order Pk ; (bottom-left) mesh-dependence
convergence when p < 2; (bottom-right) overshoot when p > 2.
Chapter 3. Advanced and highly nonlinear problems 125

3.2.4 The damped Newton algorithm


Principe of the algorithm

The Newton algorithm diverges when the initial u(0) is too far from a solution, e.g. when p is
not at the vicinity of 2. Our aim is to modify the Newton algorithm and to obtain a globally
convergent algorithm, i.e to converge to a solution for any initial u(0) . By this way, the algorithm
should converge for any value of p ∈]1, +∞[. 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 defined for any v ∈ V by:


1 −1
T (v) = ∥C F (v)∥2V ,
2
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 L2 norm. The convergence is global when for any initial u(0) , we have for any n ⩾ 0:
    D   E
T u(n+1) ⩽ T u(n) + α T ′ u(n) , u(n+1) − u(n) ′ (3.5)
V ,V

where ⟨., .⟩V ′ ,V is the duality product between V and its dual V ′ , and α ∈]0, 1[ is a small parameter.
Note that

T ′ (u) = {C −1 F ′ (u)}∗ C −1 F (u)

where the superscript ∗ denotes the adjoint operator, i.e. the transpose matrix the in finite
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 fixed step n ⩾ 0: for convenience
the n superscript is dropped in u(n) and δu(n) . Let g : R → R defined 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 finite
dimensional case. The practical algorithm for obtaining λ was introduced first in [J. E. Dennis
and Schnablel, 1983] and is also presented in [Press et al., 1997, p. 385]. The step length λ that
satisfy (3.5) is computed by using a finite sequence λk , k = 0, 1 . . . with a second order recurrence:

• k = 0 : initialization λ0 = 1. If (3.5) is satisfied with u + λ0 d then let λ := λ0 and the


sequence stop here.
• k = 1 : first order recursion. The quantities g(0) = f (u) et g ′ (0) = ⟨f ′ (u), d⟩ are already
computed at initialization. Also, we already have computed g(1) = f (u + d) when verifying
whether (3.5) was satisfied. Thus, we consider the following approximation of g(λ) by a
second order polynomial:

g̃1 (λ) = {g(1) − g(0) − g ′ (0)}λ2 + g ′ (0)λ + g(0)


126 Rheolef version 7.2

After a short computation, we find that the minimum of this polynomial is:

−g ′ (0)
λ̃1 =
2{g(1) − g(0) − g ′ (0)}

Since the initialization at k = 0 does not satisfy (3.5), it is possible to show that, when α is
small enough, we have λ̃1 ⩽ 1/2 and λ̃1 ≈ 1/2. Let λ1 := max(λmin , λ̃1 ). If (3.5) is satisfied
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, together with λk−1 , g(λk−1 ), λk−2 and g(λk−2 ). Then, g(λ) is approximated by
the following third order polynomial:

g̃k (λ) = aλ3 + bλ2 + g ′ (0)λ + g(0)

where a et b are expressed by:


1 1
 

 λ2k−1 λ2k−2  g(λk−1 ) − g ′ (0)λk−1 − g(0)
   
a 1
=
g(λk−2 ) − g ′ (0)λk−2 − g(0)
 
b λk−1 − λk−2  − λk−2 λk−1 
λ2k−1 λ2k−2

The minimum of g̃k (λ) is p


−b + b2 − 3ag ′ (0)
λ̃k =
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 λk−1 . If (3.5) is satisfied with u + λk d then let λ := λk and the sequence stop here.

The sequence (λk )k⩾0 is strictly decreasing: when the stopping criteria is not satisfied until λk
reaches the machine precision εmach then the algorithm stops with an error.

File 3.18: p_laplacian_damped_newton.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " p_laplacian . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 Float eps = numeric_limits < Float >:: epsilon ();
9 string approx = ( argc > 2) ? argv [2] : " P1 " ;
10 Float p = ( argc > 3) ? atof ( argv [3]) : 1.5;
11 Float tol = ( argc > 4) ? atof ( argv [4]) : eps ;
12 size_t max_iter = ( argc > 5) ? atoi ( argv [5]) : 500;
13 derr << " # P - Laplacian problem by damped Newton : " << endl
14 << " # geo = " << omega . name () << endl
15 << " # approx = " << approx << endl
16 << " # p = " << p << endl ;
17 p_laplacian F (p , omega , approx );
18 field uh = F . initial ();
19 int status = damped_newton (F , uh , tol , max_iter , & derr );
20 dout << catchmark ( " p " ) << p << endl
21 << catchmark ( " u " ) << uh ;
22 return status ;
23 }

Comments

The damped_newton function implements the damped Newton algorithm for a generic T (u) func-
tion, i.e. a generic nonlinear preconditioner. This algorithms use a backtrack strategy implemented
Chapter 3. Advanced and highly nonlinear problems 127

in the file ‘newton-backtrack.h’ of the Rheolef library. The simplest choice of the identity pre-
conditioner C = I i.e. T (u) = ∥F (u)∥2V ′ /2 is showed in file 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)∥2V ′

The ‘p_laplacian_damped_newton.cc’ is the application program to the p-Laplacian problem


together with the ∥.∥L2 (Ω) discrete norm for the function T .

Running the program

Figure 3.13: The p-Laplacian for d = 2: elevation view for p = 1.15 (left) and p = 7 (right).

As usual, enter:

make p_laplacian_damped_newton
mkgeo_ugrid -t 50 > square.geo
./p_laplacian_damped_newton square.geo P1 1.15 | field -stereo -elevation -
./p_laplacian_damped_newton square.geo P1 7 | field -stereo -elevation -

See Fig. 3.13 for the elevation view of the solution. The algorithm is now quite robust: the
convergence occurs for quite large range of p > 1 values and extends the range previously presented
on Fig. 3.7. The only limitation is now due to machine roundoff on some architectures.
Figs. 3.14.top shows that the convergence properties seems to slightly depend on the mesh re-
finement. Nevertheless, there are quite good and support both mesh refinement and high order
polynomial degree. When p is far from p = 2, i.e. either close to one or large, Figs. 3.14.bottom
shows that the convergence becomes slower and that the first linear regime, corresponding to the
line search, becomes longer. This first regime finishes by a brutal super-linear regime, where the
residual terms fall in few iterations to the machine precision.
128 Rheolef version 7.2

(n) h = 1/10 (n) k =1


rh rh
−1,h h = 1/20 −1,h k =2
h = 1/30 k =3
1 h = 1/40 1 k =4
h = 1/50 k =5
p = 3/2, k = 1 p = 3/2, k = 1
10−5 10−5

10−10 10−10

10−15 10−15
0 5 10 15 20 25 0 5 10 15 20 25
n n
(n)
rh p = 1.5 (n)
rh p=3
−1,h p = 1.4 −1,h p=4
p = 1.3 p=5
100 p = 1.2 100 p=6
p=7

h = 1/50, k = 1
10−5 10−5

10−10 h = 1/50, k = 1 10−10

10−15 10−15
0 25 50 0 5 10 15 20 25
n n

Figure 3.14: The damped Newton algorithm on the p-Laplacian for d = 2: when p = 1.5 and
h = 1/50, convergence properties of the residue (top-left) with mesh refinement; (top-right) with
polynomial order Pk ; (bottom-left) convergence when p < 2; (bottom-right) when p > 2.

3.2.5 Error analysis

While there is no simple explicit expression for the exact solution in the square Ω =]0, 1[2 , there
is one when considering Ω as the unit circle:

1
(p − 1) 2− p−1   p 
u(x) = 1 − x20 + x21 p−1)
p
Chapter 3. Advanced and highly nonlinear problems 129

∥u − uh ∥0,p,Ω ∥u − uh ∥0,∞,Ω
10−2 10−2

2=k+1
10−4 10−4 2=k+1

3
10−6 3
10−6

4 4
k=1 k=1
10−8 k=2 10−8 k=2
k=3 k=3
10−2 10−1 1 10−2 10−1 1
h h
1
|∇(u − uh )|0,p,Ω

10−2 1=k

2
10−4

k=1
k=2
3 k=3
10−6
10−2 10−1 1
h
Figure 3.15: The p-Laplacian for d = 2: error analysis.

File 3.19: p_laplacian_circle.h


1 struct u_exact {
2 Float operator () ( const point & x ) const {
3 return (1 - pow ( norm2 ( x ) , p /(2* p -2)))/(( p /( p -1))* pow (2. ,1/( p -1)));
4 }
5 u_exact ( Float q ) : p ( q ) {}
6 protected : Float p ;
7 };
8 struct grad_u {
9 point operator () ( const point & x ) const {
10 return - ( pow ( norm2 ( x ) , p /(2* p -2) - 1)/ pow (2. ,1/( p -1)))* x ;
11 }
12 grad_u ( Float q ) : p ( q ) {}
13 protected : Float p ;
14 };
130 Rheolef version 7.2

File 3.20: p_laplacian_error.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " p_laplacian_circle . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 Float tol = ( argc > 1) ? atof ( argv [1]) : 1e -15;
8 Float p ;
9 field uh ;
10 din >> catchmark ( " p " ) >> p
11 >> catchmark ( " u " ) >> uh ;
12 const geo & omega = uh . get_geo ();
13 const space & Xh = uh . get_space ();
14 field pi_h_u = lazy_interpolate ( Xh , u_exact ( p ));
15 field eh = pi_h_u - uh ;
16 integrate_option iopt ;
17 iopt . set_family ( integrate_option :: gauss );
18 iopt . set_order (2* Xh . degree ());
19 Float err_lp = pow ( integrate ( omega ,
20 pow ( fabs ( uh - u_exact ( p )) , p ) , iopt ) , 1./ p );
21 Float err_w1p = pow ( integrate ( omega ,
22 pow ( norm ( grad ( uh ) - grad_u ( p )) , p ) , iopt ) , 1./ p );
23 Float err_linf = eh . max_abs ();
24 dout << " err_linf = " << err_linf << endl
25 << " err_lp = " << err_lp << endl
26 << " err_w1p = " << err_w1p << endl ;
27 return ( err_linf < tol ) ? 0 : 1;
28 }
Note, in the file ‘p_laplacian_error.cc’, the usage of the integrate function, together with a
quadrature formula specification, for computing the errors in Lp norm and W 1,p semi-norm. Note
also the flexibility of expressions, mixing together fields as uh and functors, as u_exact. The
whole expression is evaluated by the integrate function at quadrature points inside each element
of the mesh.
By this way, the error analysis investigation becomes easy:

make p_laplacian_error
mkgeo_ball -t 10 -order 2 > circle-10-P2.geo
./p_laplacian_damped_newton circle-10-P2.geo P2 1.5 | ./p_laplacian_error

We can vary both the mesh size and the polynomial order and the error plots are showed on
Fig. 3.15 for both the L2 , L∞ norms and the W 1,p semi-norm. Observe the optimal error behavior:
the slopes in the log-log scale are the same as those obtained by a direct Lagrange interpolation
of the exact solution.

3.3 Continuation and bifurcation methods


This chapter is an introduction to continuation and bifurcation methods with Rheolef. We
consider a model nonlinear problem that depends upon a parameter. This problem is inspired
from application to combustion. Solutions exists for a limited range of this parameter and there
is a limit point: beyond this limit point there is no more solution. Our first aim is to compute
the branch of solutions until this limit point, thanks to the continuation algorithm. Moreover,
the limit point is a turning point for the branch of solutions: there exists a second branch of
solutions, continuing the first one. Our second aim is to compute the second branch of solutions
after this limit point with the Keller continuation algorithm. For simplicity in this presentation,
the discretization is in one dimension: the extension to high order space dimension is immediate.
Chapter 3. Advanced and highly nonlinear problems 131

3.3.1 Problem statement and the Newton method

Let us consider the following model problem (see Paumier, 1997, p. 59 or Crouzeix and Rappaz,
1990, p. 2), defined for all λ ∈ R:
(P ): find u, defined in Ω such that

−∆u + λ exp(u) = 0 in Ω
u = 0 on ∂Ω

In order to apply a Newton method to the whole problem, let us introduce:

F (λ, u) = −∆u + λ exp(u)

Then, the Gâteau derivative at any (λ, u) ∈ R × H01 (Ω) is given by:

∂F
(λ, u).(v) = −∆v + λ exp(u)v, ∀v ∈ H01 (Ω)
∂u

File 3.21: combustion.h


1 struct combustion {
2 typedef Float float_type ;
3 typedef field value_type ;
4 combustion ( const geo & omega = geo () , string approx = " " );
5 void reset ( const geo & omega , string approx );
6 field initial ( std :: string restart = " " );
7 idiststream & get ( idiststream & is , field & uh );
8 odiststream & put ( odiststream & os , const field & uh ) const ;
9 string parameter_name () const { return " lambda " ; }
10 float_type parameter () const { return lambda ; }
11 void set_parameter ( float_type lambda1 ) { lambda = lambda1 ; }
12 bool stop ( const field & xh ) const { return xh . max_abs () > 10; }
13 field residue ( const field & uh ) const ;
14 form derivative ( const field & uh ) const ;
15 field d e r i v a t i v e _ v e r s u s _ p a r a m e t e r ( const field & uh ) const ;
16 problem :: determinant_type update_derivative ( const field & uh ) const ;
17 field derivative_solve ( const field & mrh ) const ;
18 field d erivat ive_t rans_m ult ( const field & mrh ) const ;
19 field massify ( const field & uh ) const { return m * uh ; }
20 field unmassify ( const field & uh ) const ;
21 float_type space_dot ( const field & xh , const field & yh ) const ;
22 float_type dual_space_dot ( const field & mrh , const field & msh ) const ;
23 protected :
24 float_type lambda ;
25 space Xh ;
26 form m ;
27 problem pm ;
28 mutable form a1 ;
29 mutable problem pa1 ;
30 mutable branch event ;
31 };
32 # include " combustion1 . icc "
33 # include " combustion2 . icc "
132 Rheolef version 7.2

File 3.22: combustion1.icc


1 combustion :: combustion ( const geo & omega , string approx )
2 : lambda (0) , Xh () , m () , pm () , a1 () , pa1 () , event ( " lambda " ," u " ) {
3 if ( approx != " " ) reset ( omega , approx );
4 }
5 void combustion :: reset ( const geo & omega , string approx ) {
6 Xh = space ( omega , approx );
7 Xh . block ( " boundary " );
8 m = form ( Xh , Xh , " mass " );
9 pm = problem ( m );
10 }
11 field combustion :: initial ( std :: string restart ) {
12 if ( restart == " " ) return field ( Xh , 0);
13 idiststream in ( restart );
14 field xh0 ;
15 get ( in , xh0 );
16 derr << " # restart from lambda = " << lambda << endl ;
17 return xh0 ;
18 }
19 odiststream & combustion :: put ( odiststream & os , const field & uh ) const {
20 return os << event ( lambda , uh );
21 }
22 idiststream & combustion :: get ( idiststream & is , field & uh ) {
23 is >> event ( lambda , uh );
24 if (! is ) return is ;
25 if ( Xh . name () == " " ) reset ( uh . get_geo () , uh . get_approx ());
26 if ( uh . b (). dis_size () == 0) {
27 // re - allocate the field with right blocked / unblocked sizes
28 field tmp = field ( Xh , 0);
29 std :: copy ( uh . begin_dof () , uh . end_dof () , tmp . begin_dof ());
30 uh = tmp ;
31 }
32 return is ;
33 }
Chapter 3. Advanced and highly nonlinear problems 133

File 3.23: combustion2.icc


1 field combustion :: residue ( const field & uh ) const {
2 test v ( Xh );
3 field mrh = integrate ( dot ( grad ( uh ) , grad ( v )) - lambda * exp ( uh )* v );
4 mrh . set_b () = 0;
5 return mrh ;
6 }
7 form combustion :: derivative ( const field & uh ) const {
8 trial du ( Xh ); test v ( Xh );
9 return integrate ( dot ( grad ( du ) , grad ( v )) - lambda * exp ( uh )* du * v );
10 }
11 problem :: determinant_type
12 combustion :: update_derivative ( const field & uh ) const {
13 a1 = derivative ( uh );
14 solver_option sopt ;
15 sopt . compute_determinant = true ;
16 pa1 = problem ( a1 , sopt );
17 return pa1 . det ();
18 }
19 field combustion :: d e r i v a t i v e _ v e r s u s _ p a r a m e t e r ( const field & uh ) const {
20 test v ( Xh );
21 return - integrate ( exp ( uh )* v );
22 }
23 field combustion :: derivative_solve ( const field & rh ) const {
24 field delta_uh ( Xh ,0);
25 pa1 . solve ( rh , delta_uh );
26 return delta_uh ;
27 }
28 field combustion :: deri vative _trans _mult ( const field & mrh ) const {
29 field rh = unmassify ( mrh );
30 field mgh = a1 * rh ;
31 mgh [ " boundary " ] = 0;
32 return mgh ;
33 }
34 field combustion :: unmassify ( const field & mrh ) const {
35 field rh ( Xh , 0);
36 pm . solve ( mrh , rh );
37 return rh ;
38 }
39 Float combustion :: space_dot ( const field & xh , const field & yh ) const {
40 return m ( xh , yh ); }
41 Float combustion :: dual_space_dot ( const field & mrh , const field & msh ) const {
42 return dual ( unmassify ( mrh ) , msh ); }

File 3.24: combustion_newton.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " combustion . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 Float eps = numeric_limits < Float >:: epsilon ();
9 string approx = ( argc > 2) ? argv [2] : " P1 " ;
10 Float lambda = ( argc > 3) ? atof ( argv [3]) : 0.1;
11 Float tol = ( argc > 4) ? atof ( argv [4]) : eps ;
12 size_t max_iter = ( argc > 5) ? atoi ( argv [5]) : 100;
13 combustion F ( omega , approx );
14 F . set_parameter ( lambda );
15 field uh = F . initial ();
16 Float residue = tol ;
17 size_t n_iter = max_iter ;
18 damped_newton (F , uh , residue , n_iter , & derr );
19 F . put ( dout , uh );
20 return ( residue <= sqrt ( tol )) ? 0 : 1;
21 }

Let us choose α = 1/2 and λ = 8(α/ cosh(α))2 ≈ 1.57289546593186. Compilation and run are:
134 Rheolef version 7.2

make combustion_newton
mkgeo_grid -e 10 > line-10.geo
./combustion_newton line-10 P1 1.57289546593186 > line-10.field
field line-10.field

3.3.2 Error analysis and multiplicity of solutions


In one dimension, when Ω =]0, 1[, the problem can be solved explicitly [Paumier, 1997, p. 59]:

• when λ ⩽ 0 the solution is parameterized by α ∈]0, π/2[ and:

8α2
λ = −
cos2 (α)
 
cos(α)
u(x) = 2 log , x ∈]0, 1[
cos(α(1 − 2x))

• when 0 ⩽ λ ⩽ λc there is two solutions. The smallest one is parameterized by α ∈]0, αc ]


and: and the largest by α ∈]αc , +∞[ with:

8α2
λ =
cosh2 (α)
 
cosh(α)
u(x) = 2 log , x ∈]0, 1[
cosh(α(1 − 2x))

• when λ > λc there is no more solution.

The critical parameter value λc = 8αc2 / cosh2 (αc ) where αc is the unique positive solution to
tanh(α) = 1/α. The following code compute αc and λc by using a Newton method.

File 3.25: lambda_c.h


1 struct alpha_c_fun {
2 typedef Float value_type ;
3 typedef Float float_type ;
4 alpha_c_fun () : _f1 (0) {}
5 Float residue ( const Float & a ) const { return tanh ( a ) - 1/ a ; }
6 void update_derivative ( const Float & a ) const {
7 _f1 = 1/ sqr ( cosh ( a )) + 1/ sqr ( a ); }
8 Float derivative_solve ( const Float & r ) const { return r / _f1 ; }
9 Float dual_space_norm ( const Float & r ) const { return abs ( r ); }
10 mutable Float _f1 ;
11 };
12 Float alpha_c () {
13 Float tol = numeric_limits < Float >:: epsilon ();
14 size_t max_iter = 100;
15 alpha_c_fun f ;
16 Float ac = 1;
17 newton ( alpha_c_fun () , ac , tol , max_iter );
18 return ac ;
19 }
20 Float lambda_c () {
21 Float ac = alpha_c ();
22 return 8* sqr ( ac / cosh ( ac ));
23 }
Chapter 3. Advanced and highly nonlinear problems 135

File 3.26: lambda_c.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " lambda_c . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 dout << setprecision ( numeric_limits < Float >:: digits10 )
8 << " alpha_c = " << alpha_c () << endl
9 << " lambda_c = " << lambda_c () << endl ;
10 }

Compilation and run write:

make lambda_c
./lambda_c

and then αc ≈ 1.19967864025773 and λc ≈ 3.51383071912516. The exact solution and its gradient
at the limit point are computed by the following functions:

File 3.27: combustion_exact.icc


1 # include " lambda2alpha . h "
2 struct u_exact {
3 Float operator () ( const point & x ) const {
4 return 2* log ( cosh ( a )/ cosh ( a *(1 -2* x [0]))); }
5 u_exact ( Float lambda , bool is_upper )
6 : a ( lambda2alpha ( lambda , is_upper )) {}
7 u_exact ( Float a1 ) : a ( a1 ) {}
8 Float a ;
9 };
10 struct grad_u {
11 point operator () ( const point & x ) const {
12 return point (4* a * tanh ( a *(1 -2* x [0]))); }
13 grad_u ( Float lambda , bool is_upper )
14 : a ( lambda2alpha ( lambda , is_upper )) {}
15 grad_u ( Float a1 ) : a ( a1 ) {}
16 Float a ;
17 };

The lambda2alpha function converts λ into α. When 0 < λ < λc , there is two solutions to
2
the equation 8 (α/ cosh(α)) = λ and thus we specify with the Boolean is_upper which one is
expected. Then α is computed by a dichotomy algorithm.

File 3.28: lambda2alpha.h


1 # include " lambda_c . h "
2 Float lambda2alpha ( Float lambda , bool up = false ) {
3 static const Float ac = alpha_c ();
4 Float tol = 1 e2 * numeric_limits < Float >:: epsilon ();
5 size_t max_iter = 1000;
6 Float a_min = up ? ac : 0;
7 Float a_max = up ? 100 : ac ;
8 for ( size_t k = 0; abs ( a_max - a_min ) > tol ; ++ k ) {
9 Float a1 = ( a_max + a_min )/2;
10 Float lambda1 = 8* sqr ( a1 / cosh ( a1 ));
11 if (( up && lambda > lambda1 ) || (! up && lambda < lambda1 ))
12 { a_max = a1 ; }
13 else { a_min = a1 ; }
14 check_macro ( k < max_iter , " lambda2alpha : max_iter = " << k
15 << " reached and err = " << a_max - a_min );
16 }
17 return ( a_max + a_min )/2;
18 };

Finally, the errors in various norms are available:


136 Rheolef version 7.2

File 3.29: combustion_error.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " combustion_exact . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 bool is_upper = ( argc > 1) && ( argv [1][0] == ’1 ’ );
8 bool is_crit = ( argc > 1) && ( argv [1][0] == ’c ’ );
9 Float tol = ( argc > 2) ? atof ( argv [2]) : 1e -15;
10 Float lambda_h ;
11 field uh ;
12 din >> catchmark ( " lambda " ) >> lambda_h
13 >> catchmark ( " u " ) >> uh ;
14 Float lambda = ( is_crit ? lambda_c () : lambda_h );
15 const geo & omega = uh . get_geo ();
16 const space & Xh = uh . get_space ();
17 field pi_h_u = lazy_interpolate ( Xh , u_exact ( lambda , is_upper ));
18 field eh = pi_h_u - uh ;
19 integrate_option iopt ;
20 iopt . set_family ( integrate_option :: gauss );
21 iopt . set_order (2* Xh . degree ()+1);
22 Float err_l2
23 = sqrt ( integrate ( omega , norm2 ( uh - u_exact ( lambda , is_upper )) , iopt ));
24 Float err_h1
25 = sqrt ( integrate ( omega , norm2 ( grad ( uh ) - grad_u ( lambda , is_upper )) , iopt ));
26 Float err_linf = eh . max_abs ();
27 dout << " err_linf = " << err_linf << endl
28 << " err_l2 = " << err_l2 << endl
29 << " err_h1 = " << err_h1 << endl ;
30 return ( err_h1 < tol ) ? 0 : 1;
31 }

The computation of the error writes:

make combustion_error
./combustion_error < line-10.field

The solution is represented on Fig. 3.16. Then we consider the vicinity of λc ≈ 3.51383071912516.
0.25 1.2
u(x) u(x)
1
0.2

0.8
0.15
0.6
0.1
0.4

0.05 exact, λ = λc
0.2
exact h = 1/10, λ = 3.55
h = 1/10 h = 1/100, λ = 3.51
0 0
0 0.2 0.4 0.6 0.8 1 0 0.2 0.4 0.6 0.8 1
x x

Figure 3.16: Combustion problem: (left) α = 1/2. (right) near αc .

./combustion_newton line-10 P1 3.55 > line-10.field


field line-10.field
mkgeo_grid -e 100 > line-100.geo
./combustion_newton line-100 P1 3.51 > line-100.field
Chapter 3. Advanced and highly nonlinear problems 137

The Newton method fails when the parameter λ is greater to some critical value λc,h that depends
upon the mesh size. Moreover, while the approximate solution is close to the exact one for moderate
λ = 1.57289546593186, the convergence seems to be slower at the vicinity of λc .
In the next section, we compute accurately λc,h for each mesh and observe the convergence of λc,h
to λc and the convergence of the associated approximate solution to the exact one.

3.3.3 The Euler-Newton continuation algorithm


The Euler-Newton continuation algorithm writes [Paumier, 1997, p. 176]:
algorithm 1 (continuation)

• n = 0: Let (λ0 , u0 ) be given. Compute


 −1
∂F ∂F
u̇0 = − (λ0 , u0 ) (λ0 , u0 )
∂u ∂λ

• n ⩾ 0: Let (λn , un ) and u̇n being known.

1) First choose a step ∆λn and set λn+1 = λn + ∆λn .


2) Then, perform a prediction by computing
 −1
∂F ∂F
w0 = un − ∆λn (λn , un ) (λn , un )
∂u ∂λ

3) Then, perform a correction step: for all k ⩾ 0, with wk being known, compute
 −1
∂F
wk+1 = wk − (λn+1 , wk ) F (λn+1 , wk )
∂u

At convergence of the correction loop, set un+1 = w∞ .


4) Finally, compute
 −1
∂F ∂F
u̇n+1 = − (λn+1 , un+1 ) (λn+1 , un+1 )
∂u ∂λ

The step ∆λn can be chosen from a guest ∆λ∗ = ∆λn−1 by adjusting the contraction ratio κ(∆λ∗ )
of the Newton method. Computing the two first iterates w0,∗ and w1,∗ with the guest step ∆λ∗
and λ∗ = λn + ∆λ∗ we have:
 −1
∂F
(λ∗ , w1,∗ ) F (λ∗ , w1,∗ )
∂u
κ(∆λ∗ ) =  −1
∂F
(λ∗ , w0,∗ ) F (λ∗ , w0,∗ )
∂u

As the Newton method is expected to converge quadratically for small enough step, we get a
practical expression for ∆λn [Paumier, 1997, p. 185]:

κ0 κ(∆λ∗ )

∆λn ∆λ2∗

where κ0 ∈]0, 1[ is the chosen reference for the contraction ratio, for instance κ0 = 1/2.
138 Rheolef version 7.2

File 3.30: combustion_continuation.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " combustion . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 cin >> noverbose ;
8 geo omega ( argv [1]);
9 string approx = ( argc > 2) ? argv [2] : " P1 " ;
10 Float eps = numeric_limits < Float >:: epsilon ();
11 c on t inuation_option opts ;
12 opts . ini_delta_parameter = 0.1;
13 opts . max_delta_parameter = 1;
14 opts . min_delta_parameter = 1e -7;
15 opts . tol = eps ;
16 derr << setprecision ( numeric_limits < Float >:: digits10 )
17 << " # continuation in lambda : " << endl
18 << " # geo = " << omega . name () << endl
19 << " # approx = " << approx << endl
20 << " # dlambda_ini = " << opts . ini_delta_parameter << endl
21 << " # dlambda_min = " << opts . min_delta_parameter << endl
22 << " # dlambda_max = " << opts . max_delta_parameter << endl
23 << " # tol = " << opts . tol << endl ;
24 combustion F ( omega , approx );
25 field uh = F . initial ();
26 F . put ( dout , uh );
27 continuation (F , uh , & dout , & derr , opts );
28 }

Then, the program is compiled and run as:

make combustion_continuation
mkgeo_grid -e 10 > line-10.geo
./combustion_continuation line-10 > line-10.branch
branch line-10.branch -toc

The last command lists all the computations preformed by the continuation algorithm. The last

∥uh ∥0,∞,Ω h = 1/160


h = 1/10

0
0 1 2 3 λc 4
λ

Figure 3.17: Combustion problem: ∥uh ∥0,∞,Ω vs λ when h = 1/10 and 1/160.

recorded computation is associated to the limit point denoted and denoted as λc,h , says at index 21:
Let us visualize the solution uh at the limit point and compute its maximum ∥uh ∥0,∞,Ω = uh (1/2):
Chapter 3. Advanced and highly nonlinear problems 139

branch line-10.branch -extract 21 -branch | field -


branch line-10.branch -extract 21 -branch | field -max -

Fig. 3.17 plots ∥uh ∥0,∞,Ω versus λ for various meshes.


Fig. 3.18 plots its convergence to λc and also the convergence of the corresponding appoximate
solution uh to the exact one u, associated to λc . Observe that |λc,h − λc | converges to zero

1 1
k=1 k=1
k=2 k=2
|λc,h − λc | k = 3 ∥∇(uh − u)∥0,2,Ω k = 3
1

10−5 10−4
2

6 = 2k

3=k
4

10−10 10−8
10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h

Figure 3.18: Combustion problem: (left) convergence of |λc,h − λc | vs h ; (right) convergence of


|uh − u| vs h at the limit point.

as O(h2k ) while |uh − u| = O(hk ). When k = 3, the convergence of λc,h slows down around
10−9 : this is due to the stopping criterion of the Newton method that detects automatically the
propagation of rounding effects in the resolution of linear systems. Observe on Fig. 3.19 the plot

∂F

det ∂u
(λ, uh (λ))
1 ∂F

det ∂u
(0, uh (0))

10−1

10−2

10−3
h = 1/10
h = 1/20
1/2 h = 1/40
10−4
h = 1/80
h = 1/160
10−5
10−8 10−6 10−4 10−2 1
λc,h − λ

 
∂F
Figure 3.19: Combustion problem: det (λ, uh ) versus λc,h − λ for various h and k = 1.
∂u

∂F
of the determinant of the Jacobean (λ, uh (λ)) versus λc,h − λ: it tends to zero at the vicinity
∂u
of λ = λc and thus, the Newton method reaches increasing difficulties to solve  the problem.Note
∂F
that the determinant has been normalized by its value when λ = 0, i.e. det (0, uh (0)) : all
∂u
the curves tend to superpose on a unique master curve and the asymptotic behavior is independent
140 Rheolef version 7.2

of h. More precisely, Fig. 3.19 suggests a 1/2 slope in logarithmic scale and thus
 
∂F
det (λ, uh (λ))
∂u
  ≈ C (λc,h − λ)1/2
∂F
det (0, uh (0))
∂u

where C ≈ 0.52 when k = 1. This behavior explains that the Newton method is unable to reach
the limit point λc .

3.3.4 Beyond the limit point : the Keller algorithm


Note that the continuation method stops to the limit point (see Fig. 3.17) while the the branch
continues: the limit point is a turning point for the branch of solutions. Especially, for each
λ ∈]0, λc [ there are two solutions and only one has been computed. Keller proposed a method to
follow the branch beyond a turning point and this method is presented here. The main idea is to
parameterize the branch (λ, u(λ)) by a curvilinear abscissa s as (λ(s), u(s)). In order to have the
count of unknown and equations we add a normalization equation:

N (s, λ(s), u(s)) = 0


F (λ(s), u(s)) = 0

where N is a given normalization function. For the normalization function, Keller proposed to
choose, when (s, λ, u) is at the vicinity of (sn , λ(sn ), u(sn )) the one following orthogonal norms:

• The orthogonal norm:

Nn (s, χ = (λ, u)) = (χ′ (sn ), χ − χ(sn )) − (s − sn )


= λ′ (sn ) (λ − λ(sn )) + (u′ (sn ), u − u(sn ))V − (s − sn ) (3.6a)

• The spherical norm:

N
en (s, χ = (λ, u)) = ∥χ − χn ∥2 − |s − sn |2
= |λ − λn |2 + ∥u − un ∥2V − |s − sn |2 (3.6b)

The orthogonal norm induces a pseudo curvilinear arc-length s, measured on the tangent at s = sn .
The spherical norm measures is simply a distance between (s, χ) and (sn , χn ) (see also Paumier,
1997, pp. 179-180 and the corresponding Fig. 5.2). We add the subscript n to N in order to
emphasize that N depends upon both (λ(sn ), u(sn )) and (λ′ (sn ), u′ (sn )) For any s ∈ R and
χ = (λ, u) ∈ R × V we introduce:
   
Nn (s, χ) N
en (s, χ)
Fn (s, χ) = and Fen (s, χ) =
F (χ) F (χ)

Then, the Keller problem with the orthogonal norm reduces to find, for any s ∈ R, χ(s) ∈ R × V
such that
Fn (s, χ(s)) = 0 (3.7a)

Conversely, the Keller problem with the spherical norm reduces to find, for any s ∈ R, χ(s) ∈ R×V
such that
Fen (s, χ(s)) = 0 (3.7b)
Chapter 3. Advanced and highly nonlinear problems 141

Both problems falls into the framework of the previous paragraph, when F is replaced by either
Fn or Fen . Then, for any s and χ = (λ, u), the partial derivatives are:
 
∂Fn −1
(s, χ) =
∂s 0
 ∂N
λ′ (sn ) u′ (sn )
  
n
(s, χ)
∂Fn  ∂χ
(s, χ) =  =
  
∂χ ∂F ∂F

F ′ (s, χ) (λ, u) (λ, u)
∂λ ∂u
and
 
∂ Fen −2(s − sn )
(s, χ) =
∂s 0
2(λ − λn ) 2(u − un )T
 
2(χ − χn )T
 
∂ Fen
(s, χ) =  = 
 
∂χ ∂F ∂F
 

F (χ) (λ, u) (λ, u)
∂λ ∂u
Let us focus on the orthogonal norm case, as the spherical one is similar. The continuation algo-
rithm of the previous paragraph is able to follows the branch of solution beyond the limit point and
explore the second part of the branch. Let us compute λ′ (sn ) and u′ (sn ). By differentiating (3.7)
with respect to s, we get:

∂Fn ∂Fn
(s, χ(s)) + (s, χ(s)).(χ′ (s)) = 0
∂s ∂χ
that writes equivalently

∂Nn ∂Nn
(s, χ(s)) + (s, χ(s)).(χ′ (s)) = 0
∂s ∂χ
F ′ (s, χ(s)).(χ′ (s)) = 0

Using the expression 3.6 for Nn we obtain:

−1 + λ′ (sn )λ′ (s) + (u′ (sn ), u′ (s)) = 0 (3.8)


∂F ∂F
(λ(s), u(s)) λ′ (s) + (λ(s), u(s)).(u′ (s)) = 0 (3.9)
∂λ ∂u
Here (., .) denotes the scalar product of the V space for u. Let us choose s = sn , for any n ⩾ 0:
we obtain

|λ′ (sn )|2 + ∥u′ (sn )∥2 = 1


∂F ∂F
(λn , un ) λ′ (sn ) + (λn , un ).(u′ (sn )) = 0
∂λ ∂u
where we use the notations λn = λ(sn ) and un = u(sn ), and where ∥.∥ denotes the norm of the V
space. Thus
 ′ 
λ (sn )
χ′ (sn ) =
u′ (sn )
 
1
1   −1
=   ∂F ∂F 
2 1/2 − (λn , un ) (λn , un )

−1
∂u ∂λ

1 + ∂F ∂F
(λn , un ) (λn , un ) 
∂u ∂λ
142 Rheolef version 7.2

∂F
The previous relation requires (λn , un ) to be nonsingular, e.g. the computation is not possible
∂u
at a singular point (λn , un ).
For a singular point, suppose that n ⩾ 1 and that both (λn , un ), (λn−1 , un−1 ) and (λ̇n−1 , u̇n−1 )
are known. By differentiating (3.7) at step n − 1 we get the equivalent of (3.8)-(3.9) at step n − 1
that is then evaluated for s = sn . We get:
λ′ (sn−1 )λ′ (sn ) + (u′ (sn−1 ), u′ (sn )) = 1
∂F ∂F
(λn , un ) λ′ (sn ) + (λn , un ).(u′ (sn )) = 0
∂λ ∂u
that writes equivalently
λ′ (sn−1 ) u′ (sn−1 )
!
λ′ (sn )
  
1
∂F ∂F = (3.10)
(λn , un ) (λn , un ) u′ (sn ) 0
∂λ ∂u
The matrix involved in the left hand side is exactly the Jacobean of Fn−1 evaluated at point
χn = (λn , un ). This Jacobean is expected to be nonsingular at a simple limit point.
Thus, at the first step, we suppose that the initial point (λ0 , u0 ) is non-singular and we
compute (λ̇0 , u̇0 ). Then, at the begining of the n-th step, n ⩾ 1, of the Keller continu-
ation algorithm, we suppose that both (λn−1 , un−1 ) and (λ̇n−1 , u̇n−1 ) are known. We con-
sider the problem Fn−1 (s, χ(s)) = 0. Here, Fn−1 (s, χ) is completely defined at the vicinity of
(sn−1 , λn−1 , un−1 ). The step control procedure furnishes as usual a parameter step ∆sn−1 and
we set sn = sn−1 + ∆sn−1 . The Newton method is performed and we get (λn , un ). Finaly, we
compute λ̇n and u̇n from (3.10).
Recall that the function Fn−1 depends upon n and should be refreshed at the begining of each
iteration by using the values (λ̇n−1 , u̇n−1 ).
The Keller continuation algorithm writes:
algorithm 2 (Keller continuation)

• n = 0: Let (s0 , χ0 = (λ0 , u0 )) be given. The recurrence requires also χ̇0 and its orientation
ε0 ∈ {−1, +1}: they could either be given or computed. When computing (χ̇0 , ε0 ), the
present algorithm supposes that (λ0 , u0 ) is a regular point: on a singular point, e.g. a
bifurcation one, there a several possible directions, and one should be chosen. Then, choose
ε = ±1 and compute χ̇0 in three steps:
 −1
du ∂F ∂F
(λ0 ) = − (λ0 , u0 ) (λ0 , u0 )
dλ ∂u ∂λ
 −1/2
du
c = 1+ (λ0 )

 T
def T du
χ̇0 = (λ̇0 , u̇0 ) = c 1, (λ0 )

• n ⩾ 0: Let (sn , χn = (λn , un )), χ̇n = (λ̇n , u̇n ) and εn being known.
1) First choose a step ∆sn and set sn+1 = sn + ∆sn , as in the classical continuation
algorithm
2) Then, perform a prediction, as usual:
y0 = χn + εn ∆sn χ̇n

3) Also as usual, do a correction loop: for all k ⩾ 0, yk being known, compute


 −1
∂Fn
yk+1 = yk − (sn+1 , yk ) Fn (sn+1 , yk )
∂χ
Chapter 3. Advanced and highly nonlinear problems 143

At convergence of the correction loop, set χn+1 = y∞ . This Newton correction loop
can be replaced by a damped Newton one.

4) Check : if n ⩾ 1, compute the following angle cosinus:

c1 = (χn+1 − χn , χn − χn−1 )
= (λn+1 − λn )(λn − λn−1 ) + (un+1 − un , un − un−1 )V
c2 = (χ̇n , χn+1 − χn )
= (λ̇n , λn+1 − λn ) + (u̇n , un+1 − un )V

When either c1 ⩽ 0 or c2 ⩽ 0, then decreases ∆sn and go back at step 1. Otherwise


the computation of χn+1 is validated.

5) Finally, compute χ̇n+1 = (λ̇n+1 , u̇n+1 ) as:

 −1
∂Fn ∂Fn
χ̇n+1 = − (sn+1 , χn+1 ) (sn+1 , χn+1 )
∂χ ∂s

If εn (χ̇n+1 , χ̇n ) ⩾ 0 then set εn+1 = εn else εn+1 = −εn .

The Keller algorithm with the spherical norm is simply obtained by replacing Fn by Fen Both
algorithm variants still require to save χ̇n and εn at each step for restarting nicely with the same
sequence of computation. The only drawback is that, at a restart of the algorithm, we skip the
first Check step, and its possible to go back at the first iterate if ∆s0 is too large. A possible
remedy is, when restarting, to furnish two previous iterates, namely χ−1 and χ0 , together with
χ̇0 : χ−1 is used only for the Check of a possible change of direction.
Howell [2009] suggested that the Keller algorithm with spherical norm is more robust that its
variant with orthogonal one. Howell [2009] reported that

[...] the spherical constraint N2 is much more efficient than the orthogonal constraint
N1 , as N2 required only one step of length 0.01 to exceed the target value, while N1
required 25, with 8 additional convergence failures.

[...] spherical constraint was seen to be more efficient than an orthogonal constraint in
a region of high curvature of the solution manifold, while both constraints performed
similarly in regions of low or moderate curvature.
144 Rheolef version 7.2

File 3.31: combustion_keller.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " combustion . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 din >> noverbose ;
8 geo omega ( argv [1]);
9 string approx = ( argc > 2) ? argv [2] : " P1 " ;
10 string metric = ( argc > 3) ? argv [3] : " orthogonal " ;
11 Float eps = numeric_limits < Float >:: epsilon ();
12 c on t inuation_option opts ;
13 opts . ini_delta_parameter = 0.1;
14 opts . max_delta_parameter = 0.5;
15 opts . min_delta_parameter = 1e -10;
16 opts . tol = eps ;
17 derr << setprecision ( numeric_limits < Float >:: digits10 )
18 << " # continuation in s : " << endl
19 << " # geo = " << omega . name () << endl
20 << " # approx = " << approx << endl
21 << " # metric = " << metric << endl
22 << " # ds_init = " << opts . ini_delta_parameter << endl
23 << " # ds_min = " << opts . min_delta_parameter << endl
24 << " # ds_max = " << opts . max_delta_parameter << endl
25 << " # tol = " << opts . tol << endl ;
26 dout << catchmark ( " metric " ) << metric << endl ;
27 keller < combustion > F ( combustion ( omega , approx ) , metric );
28 keller < combustion >:: value_type xh = F . initial ();
29 F . put ( dout , xh );
30 continuation (F , xh , & dout , & derr , opts );
31 }

4 12
h = 1/10 u(x)
∥uh ∥0,∞,Ω h = 1/80
10
3
8

2 6

4
1
2
exact
h = 1/20
0 0
0 1 2 3 4 0 0.2 0.4 0.6 0.8 1
λ x

Figure 3.20: Combustion problem: (left) ∥uh ∥0,∞,Ω vs λ when h = 1/10 and 1/80. The full branch
of solutions when λ ⩾ 0, with the limit point and the upper part of the branch. (right) solution
of the upper branch when λ = 10−2 .

How to run the program

Enter the following unix commands:

make combustion_keller combustion_keller_post combustion_error


mkgeo_grid -e 20 > line-20.geo
Chapter 3. Advanced and highly nonlinear problems 145

./combustion_keller line-20 > line-20.branch


./combustion_keller_post < line-20.branch

The last command scans the file line-20.branch containing the branch of solutions (λ(s), u(s))
and compute some useful informations for graphical representations. Observe on Fig. 3.20.left
the full branch of solutions when λ ⩾ 0, with the limit point and the upper part of the branch.
Compare it with Fig. 3.17, where the continuation algorithm was limited to the lower part part of
the branch. Next, in order to vizualise the last computed solution, enter:

branch line-20.branch -toc


branch line-20.branch -extract 41 -branch | ./combustion_error 1

Please, replace 41 by the last computed index on the branch. Fig. 3.20.right represents this solution
and compares it ith the exact one. Observe the excellent agreement.

File 3.32: combustion_keller_post.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " combustion . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 string metric ;
8 din >> catchmark ( " metric " ) >> metric ;
9 keller < combustion > F ( combustion () , metric );
10 keller < combustion >:: value_type xh , dot_xh ;
11 dout << noverbose
12 << setprecision ( numeric_limits < Float >:: digits10 )
13 << " # metric " << metric << endl
14 << " # s lambda umax det ( mantissa , base , exp ) | u | | grad ( u )| | residue | "
15 << endl ;
16 for ( size_t n = 0; F . get ( din , xh ); ++ n ) {
17 problem :: determinant_type det ;
18 if ( n > 0 || metric != " spherical " ) det = F . update_derivative ( xh );
19 const space & Xh = xh . second . get_space ();
20 trial u ( Xh ); test v ( Xh );
21 form a = integrate ( dot ( grad ( u ) , grad ( v ))) ,
22 m = integrate ( u * v );
23 const combustion & F0 = F . get_problem ();
24 field mrh = F0 . residue ( xh . second );
25 dout << F . parameter () << " " << xh . first
26 << " " << xh . second . max_abs ()
27 << " " << det . mantissa
28 << " " << det . base
29 << " " << det . exponant
30 << " " << sqrt ( m ( xh . second , xh . second ))
31 << " " << sqrt ( a ( xh . second , xh . second ))
32 << " " << sqrt ( F0 . dual_space_dot ( mrh , mrh ))
33 << endl ;
34 dot_xh = F . direction ( xh );
35 F . refresh ( F . parameter () , xh , dot_xh );
36 }
37 }
146 Rheolef version 7.2
Chapter 4

Discontinuous Galerkin methods

4.1 Linear first-order problems


The aim of this chapter is to introduce to discontinuous Galerkin methods within the Rheolef
environment. For some recent presentations of discontinuous Galerkin methods, see di Pietro
and Ern [2012] for theoretical aspects and Hesthaven and Warburton [2008] for algorithmic and
implementation.

4.1.1 The stationary transport equation


The steady scalar transport problem writes:
(P ): find ϕ, defined in Ω, such that
u.∇ϕ + σϕ = f in Ω
ϕ = ϕΓ on ∂Ω−
where u, σ > 0, f and ϕΓ being known. Note that this is the steady version of the unsteady
diffusion-convection problem previously introduced in section 2.4.2, page 79 and when the diffusion
coefficient ν vanishes. Here, the ∂Ω− notation is the upstream boundary part, defined by
∂Ω− = {x ∈ ∂Ω; u(x).n(x) < 0}
Let us suppose that u ∈ W 1,∞ (Ω)d and introduce the space:
X = {φ ∈ L2 (Ω); (u.∇)φ ∈ L2 (Ω)d }
and, for all ϕ, φ ∈ X
Z Z
a(ϕ, φ) = (u.∇ϕ φ + σ ϕ φ) dx + max (0, −u.n) ϕ φ ds
Ω Z Z∂Ω
l(φ) = f φ dx + max (0, −u.n) ϕΓ φ ds
Ω ∂Ω

Then, the variational formulation writes:


(F V ): find ϕ ∈ X such that
a(ϕ, φ) = l(φ), ∀φ ∈ X
Note that the term max(0, −u.n) = (|u.n| − u.n)/2 is positive and vanishes everywhere except on
∂Ω− . Thus, the boundary condition ϕ = ϕΓ is weakly imposed on ∂Ω− via the integrals on the
boundary. The discontinuous finite element space is defined by:
Xh = {φh ∈ L2 (Ω); φh|K ∈ Pk , ∀K ∈ Th }

147
148 Rheolef version 7.2

where k ⩾ 0 is the polynomial degree. Note that Xh ̸⊂ X and that the ∇ϕh term has no more
sense for discontinuous functions ϕh ∈ Xh . Following di Pietro and Ern [2012, p. 14], we introduce
the broken gradient ∇h as a convenient notation:

(∇h ϕh )|K = ∇(ϕh|K ), ∀K ∈ Th

Thus
Z X Z
u.∇h ϕh φh dx = u.∇ϕh φh dx, ∀ϕh , φh ∈ Xh
Ω K∈Th K

This leads to a discrete version ah of the bilinear form a, defined for all ϕh , φh ∈ Xh by (see
e.g. di Pietro and Ern, 2012, p. 57, eqn. (2.34)):
Z Z
ah (ϕh , φh ) = (u.∇h ϕh φh + σϕh φh ) dx + max (0, −u.n) ϕh φh ds
Ω ∂Ω
Z
X  α 
+ − u.n [[ϕh ]] {{φh }} + |u.n| [[ϕh ]] [[φh ]] ds
(i) S 2
S∈Sh

(i)
The last term involves a sum over Sh , the set of internal sides of the mesh Th . Each internal side

n−
n+
ϕ+
h n = n− = −n+ on S
ϕ−
h
S
K+
K−

Figure 4.1: Discontinuous Galerkin method: an internal side, its two neighbor elements and their
opposite normals.

(i)
S ∈ Sh has two possible orientations: one is choosen definitively. In practice, this orientation
is defined in the ‘.geo’ file containing the mesh, where all sides are listed, together with their
orientation. Let n the normal to the oriented side S: as S is an internal side, there exists two
elements K− and K+ such that S = ∂K− ∩ ∂K+ and n is the outward unit normal of K− on
∂K− ∩ S and the inward unit normal of K+ on ∂K+ ∩ S, as shown on Fig. 4.1. For all ϕh ∈ Xh ,
recall that ϕh is in general discontinuous across the internal side S. We define on S the inner
value ϕ−h = ϕh|K− of ϕh as the restriction ϕh|K− of ϕh in K− along ∂K− ∩ S. Conversely, we

define the outer value ϕ+ h = ϕh|K+ . We also denote on S the jump [[ϕh ]] = ϕh − ϕh and the
+

average {{ϕh }} = (ϕh + ϕh )/2. The last term in the definition of ah is pondered by a coefficient
+

α ⩾ 0. Choosing α = 0 correspond to the so-called centered flux approximation, while α = 1 is


the upwinding flux approximation. The case α = 1 and k = 0 (piecewise constant approximation)
leads to the popular upwinding finite volume scheme. Finally, the discrete variational formulation
writes:
(F V )h : find ϕh ∈ Xh such that

ah (ϕh , φh ) = l(φh ), ∀φh ∈ Xh

The following code implement this problem in the Rheolef environment.


Chapter 4. Discontinuous Galerkin methods 149

File 4.1: transport_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 geo omega ( argv [1]);
7 space Xh ( omega , argv [2]);
8 Float alpha = ( argc > 3) ? atof ( argv [3]) : 1;
9 Float sigma = ( argc > 4) ? atof ( argv [4]) : 3;
10 point u (1 ,0 ,0);
11 trial phi ( Xh ); test psi ( Xh );
12 form ah = integrate ( dot (u , grad_h ( phi ))* psi + sigma * phi * psi )
13 + integrate ( " boundary " , max (0. , - dot (u , normal ()))* phi * psi )
14 + integrate ( " internal_sides " ,
15 - dot (u , normal ())* jump ( phi )* average ( psi )
16 + 0.5* alpha * abs ( dot (u , normal ()))* jump ( phi )* jump ( psi ));
17 field lh = integrate ( " boundary " , max (0 , - dot (u , normal ()))* psi );
18 field phi_h ( Xh );
19 problem p ( ah );
20 p . solve ( lh , phi_h );
21 dout << catchmark ( " sigma " ) << sigma << endl
22 << catchmark ( " phi " ) << phi_h ;
23 }

Comments

The data are f = 0, ϕγ = 1 and u = (1, 0, 0), and then the exact solution is known: ϕ(x) =
exp(−σx0 ). The numerical tests are running with σ = 3 by default. The one-dimensional case
writes:

make transport_dg
mkgeo_grid -e 10 > line.geo
./transport_dg line P0 | field -
./transport_dg line P1d | field -
./transport_dg line P2d | field -

Observe the jumps across elements: these jumps decreases with mesh refinement or when polyno-
mial approximation increases. The two-dimensional case writes:

mkgeo_grid -t 10 > square.geo


./transport_dg square P0 | field -
./transport_dg square P1d | field -elevation -
./transport_dg square P2d | field -elevation -

The elevation view shows details on inter-element jumps. Finaly, the three-dimensional case writes:

mkgeo_grid -T 5 > cube.geo


./transport_dg cube P0 | field -
./transport_dg cube P1d | field -
./transport_dg cube P2d | field -

Fig. 4.2 plots the solution when d = 1 and k = 0: observe that the boundary condition ϕ = 1 at
x0 = 0 is only weakly satisfied. It means that the approximation ϕh (0) tends to 1 when h tends
to zero. Fig. 4.3 plots the error ϕ − ϕh in L2 and L∞ norms: these errors behave as O hk+1
for all k ⩾ 0, which is optimal. A theoretical O hk+1/2 error bound was shown by Johnson and


Pitkäranta [1986]. The present numerical results confirm that these theoretical error bounds can
be improved for some families of meshes, as pointed out by Richter [1988], that showed a O hk+1
optimal bound for the transport problem. This result was recently extended by Cockburn et al.
150 Rheolef version 7.2

ϕ(x) ϕ(x)
1 h = 1/20 ϕh (x) 1 h = 1/40 ϕh (x)

0.5 0.5

0 0
0 0.5 1 0 0.5 1
x x

Figure 4.2: The discontinuous Galerkin method for the transport problem when k = 0 and d = 1.

1 1
∥ϕ − ϕh ∥L2 ∥ϕ − ϕh ∥L∞
1=k+1
1=k+1

10−5 10−5

10−10 10−10
k =0 k =0
k =1 k =1
5=k+1 k =2 5=k+1 k =2
k =3 k =3
k =4 k =4
10−15 10−15
10−2 10−1 10−2 10−1
h h

Figure 4.3: The discontinuous Galerkin method for the transport problem: convergence when
d = 2.

[2010a], while Peterson [1991] showed that the estimate O hk+1/2 is sharp for general families of


quasi-uniform meshes.

4.1.2 [New] The time-dependent transport equation

Problem statement

The time-dependent transport equation is involved in many problems of mathematical physics.


One of these problem concerns a moving domain, transported by a velocity field. Let us denote,
at any time t ⩾ 0 by ω(t) ⊂ Rd , d = 2, 3, a bounded moving domain and by Γ(t) = ∂ω(t) its
boundary, called the interface. Let Ω ⊂ Rd be the bounded computational domain. We assume
that it contains at any time t ⩾ 0 the moving domain ω(t). A function ϕ : [0, ∞[×Ω → R is a level
set for the moving domain if and only if Γ(t) = {x ∈ Ω / ϕ(t, x) = 0} for any t ⩾ 0. For a given
velocity field u, associated to the motion, the level set function is defined as the solution of the
following time dependent transport problem:
Chapter 4. Discontinuous Galerkin methods 151

(P ): find ϕ, defined in ]0, ∞[×Ω such that



 ∂ϕ + u.∇ϕ = 0 in ]0, ∞[×Ω (4.1a)
∂t
ϕ(t = 0) = ϕ0 in Ω (4.1b)

where ϕ0 is given and represents the initial shape ω(0). Here, we assume a divergence free velocity
field, such that the volume of the moving domain is conserved. This is a linear hyperbolic problem.
The level set method for describing moving interfaces was introduced by Osher and Sethian [1988]
(see also Sethian, 1999).

Figure 4.4: Zalesak slotted disk: elevation view of the interpolated level set function (upside-down),
together with its zero level set, in black thick line.

Note that several choices are possible for the function ϕ: the only requirement being that a fixed
isocontour of ϕ coincides with the front at each time t. A common choice is the signed distance
from the front: e.g. ω(t) is the part where ϕ(t, .) is negative and ϕ(t, x) = −dist(Γ(t), x) for
all x ∈ ω(t). Fig. 4.4, page 151, represents in elevation such a signed distance level set function:
the interface Γ, the thick black line, corresponds to the Zalesak slotted disk, that will be our
benchmark in the next section.

Approximation

The evolution transport equation (4.1a) is then discretized with respect to time. di Pietro et al.
[2006] and Marchandise et al. [2006] used an explicit and strong stability preserving Runge-Kutta
method [Gottlieb et al., 2001]. Here, we propose to use a high order implicit scheme based on the
following backward differentiation formula (BDF):
p
∂ϕ 1 X
(t) = αp,i ϕ(t−i∆t) + O (∆tp )
∂t ∆t i=0

where ∆t > 0 is the time step, p ⩾ 1 is the order of the scheme and (αp,i )0⩽i⩽p are the p + 1
coefficients of the formula. When p = 1, the scheme coincides with the usual implicit Euler
method. For 1 ⩽ p ⩽ 2, the scheme is unconditionally stable, for 3 ⩽ p ⩽ 6, the scheme is almost
unconditionally stable while when p > 6, the scheme is unstable and cannot be used (see e.g. Süli
and Mayers, 2003, p. 349).
The coefficients when p ⩽ 6 are given in table 4.1 and the file ‘bdf.icc’ implements this ta-
ble. This file it is not listed here but is available in the Rheolef example directory. When
the time step n < p, the scheme is started by using the BDF(n) formula instead and we intro-
duce pn = min(p, n) for convenience. The discretization with respect to space follows the method
developed in the previous section 4.1.1. Let us introduce the following linear and bilinear form,
152 Rheolef version 7.2

p\i 0 1 2 3 4 5 6
1 1 −1
2 3/2 −2 1/2
3 11/6 −3 3/2 −1/3
4 25/12 −4 3 −4/3 1/4
5 137/60 −5 5 −10/3 5/4 −1/5
6 147/60 −6 15/2 −20/3 15/4 −6/5 1/6

Table 4.1: BDF(p) schemes: the αp,i coefficients, 1 ⩽ p ⩽ 6, 1 ⩽ i ⩽ p.

defined for all φ, ξ ∈ Xh by

Z  Z
αpn ,0 
an (φ, ξ) = φ ξ + u(tn ).∇h φ ξ dx + max(0, −u(tn ).n) φ ξ ds
Ω ∆t ∂Ω
 
X Z |u(tn ).n|
+ [[φ]] [[ξ]] − u(tn ).n {{ξ}} ds (4.2a)
(i) S 2
S∈Sh
pn Z
X αpn ,i (n−i)
ln (ξ) = − ϕh ξdx (4.2b)
i=1
∆t Ω

(0) (n)
When n = 0 we set ϕh = πh (ϕ0 ) and when n ⩾ 1, the time approximation ϕh is defined by a
pn -order recurrence as the solution of the following linear problem:
(n)
(P )h : find ϕh ∈ Xh such that

 
(n)
an ϕh , ξh = ln (ξh ), , ∀ξh ∈ Xh

Finally, at each time step, the problem reduces to a linear system.

4.1.3 [New] Example: the Zalesak slotted disk

The Zalesak [1979, p. 350] slotted disk in rotation is a widely used benchmark for compar-
ing the performances of interface capturing methods. The radius of the disk is 0.15 and its
center is (0.5, 0.75) The width of the slot is 0.05 and its length is 0.25. This slotted disk
is rotated around the barycenter (0.5, 0.5) of the computational domain Ω = [0, 1]2 at velocity
u(x0 , x1 ) = (−(x1 − 1/2)/2, (x0 − 1/2)/2), such that the angular velocity curl u = 1 is constant.
Note that the slotted disk returns to its initial position after a period tf = 4π. The presentation
of this section is mainly inspired from Ta et al. [2016], that used Rheolef. The following code
implement the previous BDF scheme for this benchmark.
Chapter 4. Discontinuous Galerkin methods 153

File 4.2: zalesak_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " zalesak . h "
5 # include " bdf . icc "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 geo omega ( argv [1]);
9 space Xh ( omega , argv [2]);
10 size_t n_max = ( argc > 3) ? atoi ( argv [3]) : 1000;
11 size_t strip = ( argc > 4) ? string ( argv [4]) == " true " : false ;
12 size_t p = ( argc > 5) ? atoi ( argv [5]) : min ( Xh . degree ()+1 , bdf :: pmax );
13 Float tf = u :: period () , delta_t = tf / n_max ;
14 trial phi ( Xh ); test xi ( Xh );
15 form m = integrate ( phi * xi ) ,
16 a0 = integrate ( dot ( u () , grad_h ( phi ))* xi )
17 + integrate ( " boundary " , max (0 , - dot ( u () , normal ()))* phi * xi )
18 + integrate ( " internal_sides " ,
19 - dot ( u () , normal ())* jump ( phi )* average ( xi )
20 + 0.5* abs ( dot ( u () , normal ()))* jump ( phi )* jump ( xi ));
21 problem pb ;
22 branch event ( " t " ," phi " );
23 vector < field > phi_h ( p +1);
24 phi_h [0] = phi_h [1] = lazy_interpolate ( Xh , phi0 ());
25 dout << event (0 , phi_h [0]);
26 for ( size_t n = 1; n <= n_max ; n ++) {
27 Float t = n * delta_t ;
28 if ( n % 10 == 0) derr << " [ " << n << " ] " ;
29 size_t pn = min (n , p );
30 field rhs ( Xh , 0);
31 for ( size_t i = 1; i <= pn ; i ++)
32 rhs += ( bdf :: alpha [ pn ][ i ]/ delta_t )* phi_h [ i ];
33 field lh = integrate ( rhs * xi )
34 + integrate ( " boundary " , max (0 , - dot ( u () , normal ()))* phi_exact ( t )* xi );
35 if ( pn <= p ) {
36 form an = a0 + ( bdf :: alpha [ pn ][0]/ delta_t )* m ;
37 pb = problem ( an );
38 }
39 pb . solve ( lh , phi_h [0]);
40 check_macro ( phi_h [0]. max_abs () < 100 , " BDF failed -- HINT : decrease delta_t " );
41 if (! strip || n == n_max ) dout << event (t , phi_h [0]);
42 for ( size_t i = min (p , pn +1); i >= 1; i - -)
43 phi_h [ i ] = phi_h [i -1];
44 }
45 derr << endl ;
46 }

Comments

Numerical computations are performed on the time interval [0, tf ] with a time step ∆t = tf /nmax
where nmax is the number of time steps. Since d = 2, a direct method is preferable (see sec-
tion 1.1.10 and Fig. 1.5). Observe that u is here independent upon t. Then an , as given by (4.2a),
is independent upon n when n ⩾ p, and an can be factored one time for all n ⩾ p. The file
‘zalesak.h’ included here implements the phi0 and phi_exact class-functions representing the
exact solution ϕ(t) of level set function for the slotted disk, together with the u class-function for
the velocity field. This file it is not listed here but is available in the Rheolef example directory.
Recall that the BDF(p) scheme is unconditionally stable when p ∈ {1, 2} and almost uncondition-
ally stable when p ∈ {3, . . . , 6} (see e.g. Süli and Mayers, 2003, p. 349). Then, when an instability
is detected, an issue for decreasing the time step is generated and the computation is stopped.
154 Rheolef version 7.2

Figure 4.5: Zalesak slotted disk: superposition of the initial disk (black) and after a period
(red). Polynomial degree k = 2, time step ∆t = 4π/6000, implicit BDF(3) scheme with a fixed
mesh h = 1/100. On the right: zoom.

How to run the program

Let us first generate a mesh for the Ω = ]0, 1[2 geometry with h = 1/100 and then run the code
with a k = 2 discontinuous Galerkin method and a BDF (3) scheme:

mkgeo_ugrid -t 50 > square.geo


make zalesak_dg
time mpirun -np 8 ./zalesak_dg square P2d > square-P2d.branch

The computation could take about ten minutes, depending upon your computer: there are 1000
time steps by default ; a prompt shows the step advancement until 1000. The time and mpirun
-np 8 prefixes are optional and you should omit them if you are running a sequential installation
of Rheolef. Then, the visualization writes:

branch square-P2d.branch -iso 0 -bw

The slotted disk appears at his initial position. Then, click on the video play button, at the
top of the window: the disk starts to rotate. Fig. 4.5 compares the initial disk position with the
final one after one period when h = 1/100 and 6000 time steps. Observe the dramatic efficiency
of the combination of the discontinuous Galerkin method and the high-order time scheme. The
corresponding solution could be directly observed as a video from the following link (3 Mo):

http://www-ljk.imag.fr/membres/Pierre.Saramito/rheolef/zalesak-100-P2d-6000.mp4

Moreover, when using higher order polynomials and schemes, e.g. for k ⩾ 3, the changes are no
more perceptible and the disk after one period fits the initial shape. The error between the two
shapes could also be evaluated, and the convergence versus mesh and polynomial degree could be
checked: see Ta et al. [2016] for more details.

4.1.4 [New] Example: the Leveque vortex-in-box


The LeVeque [1996, p. 653] vortex-in-box is also another widely used benchmark. This example
tests the ability of the scheme to accurately resolve thin filaments on the scale of the mesh which
Chapter 4. Discontinuous Galerkin methods 155

can occur in swirling and stretching flows. An initial shape is deformed by a non-constant velocity
field given by:

sin2 (πx0 ) sin(2πx1 )


  
cos(πt/t ) when d = 2

f
− sin(2πx0 ) sin2 (πx1 )





u(t, x) = 
2 sin2 (πx0 ) sin(2πx1 ) sin(2πx2 )

when d = 3

cos(πt/tf )  − sin(2πx0 ) sin2 (πx1 ) sin(2πx2 ) 




− sin2 (πx0 ) sin(2πx1 ) sin(2πx2 )

At the half-period t = tf /2, the initial data is quite deformed and the flow slows down and reverses
direction in such a way that the initial data should be recovered at time tf . This gives a very
useful test problem since we then know the true solution at time tf even though the flow field has
a quite complicated structure. Here we use tf = 8 when d = 2 and tf = 3 when d = 3. The initial
shape is a circle of radius 0.15 placed at (0.5, 0.75) when d = 2 and a sphere of radius 0.15 placed
at (0.35, 0.35, 0.35) when d = 3. The tridimensional flow field was first proposed by LeVeque
[1996, p. 662] and the corresponding initial shape described by Enright et al. [2002, p. 112]. The
computational domain is Ω =]0, 1[d and note that the flow field u vanishes on ∂Ω.

File 4.3: leveque_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " leveque . h "
5 # include " bdf . icc "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 geo omega ( argv [1]);
9 space Xh ( omega , argv [2]);
10 size_t n_max = ( argc > 3) ? atoi ( argv [3]) : 1000;
11 size_t strip = ( argc > 4) ? string ( argv [4]) == " true " : false ;
12 size_t p = ( argc > 5) ? atoi ( argv [5]) : min ( Xh . degree ()+1 , bdf :: pmax );
13 size_t d = omega . dimension ();
14 Float tf = u :: period () , delta_t = tf / n_max ;
15 trial phi ( Xh ); test xi ( Xh );
16 branch event ( " t " ," phi " );
17 vector < field > phi_h ( p +1);
18 phi_h [0] = phi_h [1] = lazy_interpolate ( Xh , phi0 ( d ));
19 dout << event (0 , phi_h [0]);
20 for ( size_t n = 1; n <= n_max ; n ++) {
21 Float t = n * delta_t ;
22 if ( n % 10 == 0) derr << " [ " << n << " ] " ;
23 size_t pn = min (n , p );
24 form an = integrate (( bdf :: alpha [ pn ][0]/ delta_t * phi + dot ( u (d , t ) , grad_h ( phi )))* xi )
25 + integrate ( " internal_sides " ,
26 - dot ( u (d , t ) , normal ())* jump ( phi )* average ( xi )
27 + 0.5* abs ( dot ( u (d , t ) , normal ()))* jump ( phi )* jump ( xi ));
28 field rhs ( Xh , 0);
29 for ( size_t i = 1; i <= pn ; i ++)
30 rhs += ( bdf :: alpha [ pn ][ i ]/ delta_t )* phi_h [ i ];
31 field lh = integrate ( rhs * xi );
32 problem pb ( an );
33 pb . solve ( lh , phi_h [0]);
34 check_macro ( phi_h [0]. max_abs () < 100 , " BDF failed -- HINT : decrease delta_t " );
35 if (! strip || n == n_max ) dout << event (t , phi_h [0]);
36 for ( size_t i = min (p , pn +1); i >= 1; i - -)
37 phi_h [ i ] = phi_h [i -1];
38 }
39 derr << endl ;
40 }
156 Rheolef version 7.2

Comments

Numerical computations are performed on the time interval [0, T ] with a time step ∆t = T /nmax
where nmax is the number of time steps. Observe that u is here time-dependent. Then an , as
given by (4.2a), is also dependent upon n, and an can no more be factored one time for all, as it
was done for the Zalesak case. The file ‘leveque.h’ included here implements both the u and phi0
class-functions for the flow field and the initial data ϕ0 associated to the initial circular shape,
respectively. This file it is not listed here but is available in the Rheolef example directory. Recall
that u vanishes on ∂Ω. Then, all boundary terms for an and ℓn , involved in (4.2a)-(4.2b), are here
omitted.

Figure 4.6: Leveque vortex-in-box: approximation with h = 1/100, k = 2, p = 3 and ∆t =


4π/6000. (left) solution at half period : the deformation is maximal ; (right) after a full period :
comparison with the initial shape, in red.

How to run the program

Compilation and run writes:


mkgeo_ugrid -t 100 > square.geo
make leveque_dg
mirun -np 8 ./leveque_dg square P2d 6000 > square-P2d.branch
The computation could take about two hours, depending upon your computer: a prompt shows
the advancement for the 6000 time steps. The mpirun -np 8 prefix is optional and you should
omit it if you are running a sequential installation of Rheolef. Then, the visualization writes:
branch square-P2d.branch -iso 0 -bw
As for the Zalesak case, the solution is represented by an animation with paraview. The video
could be directly observed from the link (7 Mo):
http://www-ljk.imag.fr/membres/Pierre.Saramito/rheolef/leveque-100-P2d-6000.mp4
Fig. 4.6.left represents the solution when h = 1/100 and 6000 time steps at the half-period t = tf /2,
when the deformation is maximal. Conversely, Fig. 4.6.right represents it when t = tf , when the
shape recovers its initial position. Observe the good correspondence between the final and the
initial shapes, which is represented in red.
Chapter 4. Discontinuous Galerkin methods 157

4.2 Nonlinear first-order problems


4.2.1 Abstract setting
The aim of this paragraph is to study the discontinuous Galerkin discretization of scalar nonlinear
hyperbolic equations. This section presents the general framework and discretization tools while
the next section illustrates the method for the Burgers equation.

Problem statement

A time-dependent nonlinear hyperbolic problem writes in general form [di Pietro and Ern, 2012,
p. 99]:
(P ): find u, defined in ]0, T [×Ω, such that

∂u
+ div f (u) = 0 in ]0, T [×Ω (4.3a)
∂t
u(t = 0) = u0 in Ω (4.3b)
f (u).n = Φ(n; u, g) on ]0, T [×∂Ω (4.3c)

where T > 0, Ω ⊂ Rd , d = 1, 2, 3 and the initial condition u0 being known. As usual, n denotes the
outward unit normal on the boundary ∂Ω. The function f : R −→ Rd is also known and supposed
to be continuously differentiable. The initial data u0 , defined in Ω, and the boundary one, g,
defined on ∂Ω are given. The function Φ, called the Godunov flux associated to f , is defined, for
all ν ∈ Rd and a, b ∈ R, by

 min f (v).ν when a ⩽ b



v∈[a,b]
Φ(ν; a, b) = (4.3d)
 max f (v).ν otherwise
v∈[b,a]

Note that, with this general formalism, the linear transport problem considered in the previous
section 4.1.1 corresponds to (see e.g. di Pietro and Ern, 2012, p. 104:

f (u) = au (4.4a)
u + v |a.n|
Φ(n; u, v) = a.n + (u − v) (4.4b)
2 2

Space discretization

In this section, we consider first the semi-discretization with respect to space while the prob-
lem remains continuous with respect to time. The semi-discrete problem writes in variational
form [di Pietro and Ern, 2012, p. 100]:
(P )h : find uh ∈ C 1 ([0, T ], Xh ) such that
Z Z X Z Z
∂uh − +
vh dx − f (uh ).∇h vh dx + Φ(n; uh , uh )[[vh ]] ds + Φ(n; uh , g)vh ds = 0, ∀vh ∈ Xh
Ω ∂t Ω (i) S ∂Ω
S∈Sh

uh (t = 0) = πh (u0 )

where πh denotes the Lagrange interpolation operator on Xh and others notations has been intro-
duced in the previous section.
For convenience, we introduce the discrete operator Gh , defined for all uh , vh ∈ Xh by
Z Z X Z Z
− +
Gh (uh )vh dx = − f (uh ).∇h vh dx + Φ(n; uh , uh )[[vh ]] ds + Φ(n; uh , g)vh ds(4.5)
Ω Ω (i) S ∂Ω
S∈Sh
158 Rheolef version 7.2

For a given uh ∈ Xh , we also define the linear form gh as


Z
g(vh ) = Gh (uh )vh dx

As the matrix M , representing the L2 scalar product in Xh , is block-diagonal, it can be easily


inverted at the element level, and for a given uh ∈ Xh , we have G(uh ) = M −1 gh . Then, the
problem writes equivalently as a set of coupled nonlinear ordinary differential equations.
(P )h : find uh ∈ C 1 ([0, T ], Xh ) such that

∂uh
+ Gh (uh ) = 0
∂t

Time discretization

Let ∆t > 0 be the time step. The previous nonlinear ordinary differential equations are discretized
by using a specific explicit Runge-Kutta with intermediates states [Shu and Osher, 1988, Gottlieb
and Shu, 1998, Gottlieb et al., 2001]. This specific variant of the usual Runge-Kutta scheme, called
strong stability preserving, is suitable for avoiding possible spurious oscillations of the approximate
solution when the exact solution has a poor regularity. Let unh denotes the approximation of uh
at time tn = n∆t, n ⩾ 0. Then un+1 h is defined by recurrence:

un,0
h = unh
i−1
X  
un,i
h = αi,j un,j
h − ∆t β G
i,j h un,j
h , 1⩽i⩽p
j=0

un+1
h = un,p
h

where the coefficients satisfy αi,j ⩾ 0 and βi,j ⩾ 0 for all 1 ⩽ i ⩽ p and 0 ⩽ j ⩽ i − 1, and
Pi−1
j=0 αi,j = 1 for all 1 ⩽ i ⩽ p. Note that when p = 1 this scheme coincides with the usual
explicit Euler scheme. For a general p ⩾ 1 order scheme, there are p − 1 intermediate states un,i
h ,
i = 1 . . . p − 1. Computation of the coefficients αi,j and βi,j can be founded in [Shu and Osher,
1988, Gottlieb and Shu, 1998, Gottlieb et al., 2001] and are grouped in file ‘runge_kutta_ssp.icc’
of the examples directory for convenience.

4.2.2 Slope limiters


Slope limiters are required when the solution develops discontinuities: this is a very classical
feature of most solutions of nonlinear hyperbolic equations. A preliminary version of the slope
limiter proposed by Cockburn [1998, p. 208] is implemented in Rheolef: this preliminary version
only supports the d = 1 dimension and k = 1 polynomial degree. Recall that the k = 0 case do
not need any limiter. More general implementation will support the d = 2, 3 and k ⩾ 2 cases in
the future. The details of the limiter implementation is presented in this section: the impatient
reader, who is interested by applications, can jump to the next section, devoted to the Burgers
equation.
Fig. 4.7 shows the d+1 neighbor elements Ki , i = 0 . . . d around an element d. Let Si = ∂K ∩∂Ki ,
i = 0 . . . d be the i-th side of K. We denote by xK , xKi ad xSi the barycenters of these elements
and sides, i = 0 . . . d. When d = 2, the barycenter xSi of the edge belongs to the interior of a
triangle (xK , xKi , xKJi,1 ) for exactly one of the two possible Ji,1 ̸= i and 0 ⩽ Ji,1 ⩽ d. When d = 3,
the barycenter xSi of the face belongs to the interior of a tetrahedron (xK , xKi , xKJi,1 , xKJi,2 )
for exactly one pair (Ji,1 , Ji,2 ), up to a permutation, of the three possible pairs Ji,1 , Ji,2 ̸= i and
0 ⩽ Ji,1 , Ji,2 ⩽ d. Let us denote Ji,0 = i. Then, the vector − x− −−→
K xSi decompose on the basis
Chapter 4. Discontinuous Galerkin methods 159

K1

J(0, 1) = 1
xK1 K
xS0
xK0
xK
K0

xK2

K2

Figure 4.7: Limiter: the neighbors elements and the middle edge points.

(−
x−−−−−→
K xKJi,k )0⩽k⩽d−1 as

d−1

x−−−→ αi,k −
x−−−−−→
X
K xSi = K xKJi,k (4.6)
k=0

where αi,k ⩾ 0, k = 0 . . . d − 1. Let us consider now the patch ωK composed of K and its d
neighbors:
ωK = K ∪ K0 ∪ . . . ∪ Kd
For any affine function ξ ∈ P1 (ωK ) over this patch, let us denote
d−1
X  
δK,i (ξ) = αi,k ξ(xKJi,k ) − ξ(xK ) , i = 0 . . . d − 1
k=0
= ξ(xSi ) − ξ(xK ) from (4.6)
In other terms, δK,i (ξ) represents the departure of the value of ξ at xSi from its average ξ(xK on
the element K.
Let now (φi )0⩽i⩽d−1 denote the Lagrangian basis in K associated to the set of nodes (xSi )0⩽i⩽d−1 :
φi (xSj ) = δi,j , 0 ⩽ i, j ⩽ d − 1
d−1
X
φi (x) = 1, ∀x ∈ K
i=0

The affine function ξ ∈ P1 (ωK ) expresses on this basis as


d−1
X
ξ(x) = ξ(xK ) + δK,i (ξ) φi (x), ∀x ∈ K
i=0

Let now uh ∈ P1d (Th ). On any element K ∈ Th , let us introduce its average value:
Z
1
ūK = uh (x) dx
meas(K) K
and its departure from its average value:
ũK (x) = uh|K (x) − ūK , ∀x ∈ K
160 Rheolef version 7.2

Note that uh ̸∈ P1 (ωK ). Let us extends δK,i to uh as


d−1
X  
δK,i (uh ) = αi,k ūKJi,k − ūK , i = 0 . . . d − 1
k=0

Since uh ̸∈ P1 (ωK ), we have ũK (xKJi,k ) ̸= δK,i (uh ) in general. The idea is then to capture
oscillations by controlling the departure of the values ũK (xKJi,k ) from the values δK,i (uh ). Thus,
associate to uh ∈ P1d (Th ) the quantities
 
∆K,i (uh ) = minmodTVB ũK (xKJi,k ), θδK,i (uh )

for all i = 0 . . . d − 1 and where θ ⩾ 1 is a parameter of the limiter and


when |a| ⩽ M h2

a
minmodTVB (a, b) =
minmod(a, b) otherwise
where M > 0 is a tunable parameter which can be evaluated from the curvature of the initial
datum at its extrema by setting

M= sup |∇ ⊗ ∇u0 | (4.7)


x∈Ω,∇u0 (x)=0

Introduced by Shu [1987], the basic idea is to deactivate the limiter when space derivatives are of
order h2 . This improves the limiter behavior near smooth local extrema. The minmod function is
defined by
sgn(a) min(|a|, |b|) when sgn(a) = sgn(b)

minmod(a, b) =
0 otherwise
Then, for all i = 0 . . . d − 1 we define
d−1
X
max(0, −∆K,j (uh ))
j=0
rK (uh ) = d−1
⩾0
X
max(0, ∆K,j (uh ))
j=0
ˆ K,i (uh )
∆ = min(1, rK (uh )) max(0, ∆K,i (uh ))
− min(1, 1/rK (uh )) max(0, −∆K,i (uh )), i = 0 . . . d − 1 when rK (uh ) ̸= 0

Finally, the limited function Λh (uh ) is defined element by element for all element K ∈ Th for all
x ∈ K by
 d−1
X
∆K,i (uh ) φi (x) when rK (uh ) = 0

 ūK +



i=1
Λh (uh )|K (x) = d−1
 X
 ūK +

 ˆ K,i (uh ) φi (x) otherwise


i=1

Note that there are two types of computations involved in the limiter: one part is independent of
uh and depends only upon the mesh: Ji,k and αi,k on each element. It can be computed one time
for all. The other part depends upon the values of uh .
Note that the limiter preserves the average value of uh on each element K and also the functions
that are globally affine on the patch ωK . Also we have, inside each element K and for all side
index i = 0 . . . d − 1:
 
ˆ K,i (uh )| ⩽ |∆K,i (uh )| ⩽ uh|K (xS ) − ūK
Λh (uh )|K (xSi ) − ūK ⩽ max |∆K,i (uh )|, |∆ i
Chapter 4. Discontinuous Galerkin methods 161

It means that, inside each element, the gradient of the P1 limited function is no larger than that
of the original one.
The limiter on an element close to the boundary should takes into account the inflow condition,
see Cockburn et al. [1990].

4.2.3 Example: the Burgers equation


As an illustration, let us consider now the test with the one-dimensional (d = 1) Burgers equation
for a propagating slant step (see e.g. Carey and Jianng, 1988, p. 87) in Ω =]0, 1[. We have
f (u) = u2 /2, for all u ∈ R. In that case, the Godunov flux (4.3d), introduced page 157, can be
computed explicitly for any ν = (ν0 ) ∈ Rd and a, b ∈ R:

when ν0 ⩾ 0 and a ⩽ b or ν0 ⩽ 0 and a ⩾ b



ν0 min a2 , b2 /2

Φ(ν; a, b) =
ν0 max a2 , b2 /2 otherwise

File 4.4: burgers.icc


1 point f ( const Float & u ) { return point ( sqr ( u )/2); }

File 4.5: burgers_flux_godunov.icc


1 Float phi ( const point & nu , Float a , Float b ) {
2 if (( nu [0] >= 0 && a <= b ) || ( nu [0] <= 0 && a >= b ))
3 return nu [0]* min ( sqr ( a ) , sqr ( b ))/2;
4 else
5 return nu [0]* max ( sqr ( a ) , sqr ( b ))/2;
6 }

Computing an exact solution

An exact solution is useful for testing numerical methods. The computation of such an exact
solution for the one dimensional Burgers equation is described by Harten et al. [1987]. The
authors consider first the problem with a periodic boundary condition:
(P ): find u : ]0, T [×]− 1, 1[7−→ R such that

∂ u2
 
∂u
+ = 0 in ]0, T [×] − 1, 1[
∂t ∂x 2
u(t = 0, x) = α + β sin(πx + γ), a.e. x ∈] − 1, 1[
u(t, x = −1) = u(t, x = 1) a.e. t ∈]0, T [

where α, β and γ are real parameters. Let us denote w the solution of the problem when β = 1
and α = γ = 0; i.e. with the initial condition w(t = 0, x) = sin(πx), a.e. x ∈] − 1, 1[. For any
x ∈ [0, 1[ and t > 0, the solution w̄ = w(t, x) satisfies the characteristic relation

w̄ = sin(π(x − w̄t))

This nonlinear relation can be solved by a Newton algorithm. Then, for x ∈]−1, 0[, the solution
is completed by symmetry: w(t, x) = −w(t, −x). Finally, the general solution for any α, β and
γ = 0 writes u(t, x) = α + w(βt, x − αt + γ). File ‘harten.h’ implements this approach.
162 Rheolef version 7.2

File 4.6: harten.h


1 # include " harten0 . h "
2 struct harten {
3 Float operator () ( const point & x ) const {
4 Float x0 = x [0] - a * t + c ;
5 Float shift = -2* floor (( x0 +1)/2);
6 Float xs = x0 + shift ;
7 check_macro ( xs >= -1 && xs <= 1 , " invalid xs = " << xs );
8 return a + b * h0 ( point ( xs ));
9 }
10 harten ( Float t1 =0 , Float a1 =1 , Float b1 =0.5 , Float c1 =0):
11 h0 ( b1 * t1 ) , t ( t1 ) , a ( a1 ) , b ( b1 ) , c ( c1 ) {}
12 Float M () const { Float pi = acos ( -1.0); return sqr ( pi )* b ; }
13 Float min () const { return a - b ; }
14 Float max () const { return a + b ; }
15 protected :
16 harten0 h0 ;
17 Float t , a , b , c ;
18 };
19 using u_init = harten ;
20 using g = harten ;

File 4.7: harten_show.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " harten . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh ( omega , argv [2]);
9 size_t nmax = ( argc > 3) ? atoi ( argv [3]) : 1000;
10 Float tf = ( argc > 4) ? atof ( argv [4]) : 2.5;
11 Float a = ( argc > 5) ? atof ( argv [5]) : 1;
12 Float b = ( argc > 6) ? atof ( argv [6]) : 0.5;
13 Float c = ( argc > 7) ? atof ( argv [7]) : 0;
14 branch even ( " t " ," u " );
15 for ( size_t n = 0; n <= nmax ; ++ n ) {
16 Float t = n * tf / nmax ;
17 field pi_h_u = lazy_interpolate ( Xh , harten (t ,a ,b , c ));
18 dout << even (t , pi_h_u );
19 }
20 }

The included file ‘harten0.h’ is not shown here but is available in the example directory.

Comments

Note that the constant M , used by the limiter in (4.7), can be explicitly computed for this solution:
M = βπ 2 .
The animation of this exact solution is performed by the following commands:

make harten_show
mkgeo_grid -e 2000 -a -1 -b 1 > line2.geo
./harten_show line2 P1 1000 2.5 > line2-exact.branch
branch line2-exact -gnuplot

Fig. 4.8 shows the solution u for α = 1, β = 1/2 and γ = 0. It is regular until t = 2/π (Fig. 4.8.c)
and then develops a chock for t > 2/π (Fig. 4.8.d). After its apparition, this chock interacts with
the expansion wave in ] − 1, 1[: this brings about a fast decay of the solution (Figs. 4.8.e and f).
Fig. 4.8 plots also a numerical solution: its computation is the aim of the next section.
Chapter 4. Discontinuous Galerkin methods 163

Numerical resolution

When replacing the periodic boundary condition with a inflow one, associated with the boundary
data g, we choose g to be the value of the exact solution of the problem with periodic boundary
conditions: g(t, x) = α + w(βt, x − αt) for x ∈ {−1, 1}.

File 4.8: burgers_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " harten . h "
5 # include " burgers . icc "
6 # include " burgers_flux_godunov . icc "
7 # include " runge_kutta_ssp . icc "
8 int main ( int argc , char ** argv ) {
9 environment rheolef ( argc , argv );
10 geo omega ( argv [1]);
11 space Xh ( omega , argv [2]);
12 Float cfl = 1;
13 limiter_option lopt ;
14 size_t nmax = ( argc > 3) ? atoi ( argv [3]) : numeric_limits < size_t >:: max ();
15 Float tf = ( argc > 4) ? atof ( argv [4]) : 2.5;
16 size_t p = ( argc > 5) ? atoi ( argv [5]) : ssp :: pmax ;
17 lopt . M = ( argc > 6) ? atoi ( argv [6]) : u_init (). M ();
18 if ( nmax == numeric_limits < size_t >:: max ()) {
19 nmax = ( size_t ) floor (1+ tf /( cfl * omega . hmin ()));
20 }
21 Float delta_t = tf / nmax ;
22 integrate_option iopt ;
23 iopt . invert = true ;
24 trial u ( Xh ); test v ( Xh );
25 form inv_m = integrate ( u *v , iopt );
26 vector < field > uh ( p +1 , field ( Xh ,0));
27 uh [0] = lazy_interpolate ( Xh , u_init ());
28 branch even ( " t " ," u " );
29 dout << catchmark ( " delta_t " ) << delta_t << endl
30 << even (0 , uh [0]);
31 for ( size_t n = 1; n <= nmax ; ++ n ) {
32 for ( size_t i = 1; i <= p ; ++ i ) {
33 uh [ i ] = 0;
34 for ( size_t j = 0; j < i ; ++ j ) {
35 field lh =
36 - integrate ( dot ( compose (f , uh [ j ]) , grad_h ( v )))
37 + integrate ( " internal_sides " ,
38 compose ( phi , normal () , inner ( uh [ j ]) , outer ( uh [ j ]))* jump ( v ))
39 + integrate ( " boundary " ,
40 compose ( phi , normal () , uh [ j ] , g ( n * delta_t ))* v );
41 uh [ i ] += ssp :: alpha [ p ][ i ][ j ]* uh [ j ] - delta_t * ssp :: beta [ p ][ i ][ j ]*( inv_m * lh );
42 }
43 uh [ i ] = limiter ( uh [ i ] , g ( n * delta_t )( point ( -1)) , lopt );
44 }
45 uh [0] = uh [ p ];
46 dout << even ( n * delta_t , uh [0]);
47 }
48 }

Comments

The Runge-Kutta time discretization combined with the discontinuous Galerkin space discretiza-
tion is implemented for this test case.
The P0 approximation is performed by the following commands:

make burgers_dg
mkgeo_grid -e 200 -a -1 -b 1 > line2-200.geo
./burgers_dg line2-200.geo P0 1000 2.5 > line2-200-P0.branch
164 Rheolef version 7.2

branch line2-200-P0.branch -gnuplot

The two last commands compute the P0 approximation of the solution, as shown on Fig. 4.8.
Observe the robust behavior of the solution at the vicinity of the chock. By replacing P0 by P1d
in the previous commands, we obtain the P1 -discontinuous approximation.

./burgers_dg line2-200.geo P1d 1000 2.5 > line2-200-P1d.branch


branch line2-200-P1d.branch -gnuplot

Fig. 4.9 plots the error vs h for k = 0 and k = 1. Fig. 4.9.a plots in a time interval [0, T ] with
T = 1/π, before the chock that occurs at t = 2/π. In that interval, the solution is regular and the
error approximation behaves as O(hk+1 ). The time interval has been chosen sufficiently small for
the error to depend only upon h. Fig. 4.9.b plots in a larger time interval [0, T ] with T = 5/2, that
includes the chock. Observe that the error behaves as O(h) for both k = 0 and 1. This is optimal
when k = 0 but not when k = 1. This is due to the loss of regularity of the exact solution that
presents a discontinuity; A similar observation can be found in Zhong and Shu [2013], table 4.1.
Chapter 4. Discontinuous Galerkin methods 165

1.5 1.5
u(t, x) (a) t = 0 (b) t = 1/2

1 1

exact
P0
0.5 0.5
−1 0 1 −1 0 1
x x
1.5 1.5
(c) t = 2/π u(t, x) (d) t = 0, 75

1 1

exact exact
P0 P0
0.5 0.5
−1 0 1 −1 0 1
x x
1.5 1.5
u(t, x) (e) t = 1 u(t, x) (f) t = 1.75

1 1

exact exact
P0 P0
0.5 0.5
−1 0 1 −1 0 1
x x

Figure 4.8: Harten’s exact solution of the Burgers equation (α = 1, β = 1/2, γ = 0). Comparison
with the P0 approximation (h = 1/100, RK-SSP(3)).
166 Rheolef version 7.2

10−1 10−1
∥u − uh ∥L∞ (0,T ;L1 ) ∥u − uh ∥L∞ (0,T ;L1 )
(a) T = 2/π (b) T = 5/2
10−2
0.96

10−3 10−2 0.85

10−4
1
1.9
k=0 k=0
k=1 k=1
10−5 10−3
10−3 10−2 10−1 10−3 10−2 10−1
h h

Figure 4.9: Burgers equation: error between the P0 approximation and the exact solution of the
Harten’s problem (α = 1, β = 1/2, γ = 0): (a) before chock, with T = 1/π; (b) after chock, with
T = 5/2.
Chapter 4. Discontinuous Galerkin methods 167

4.3 Scalar second-order problems


4.3.1 The Poisson problem with Dirichlet boundary conditions
The Poisson problem with non-homogeneous Dirichlet boundary conditions has been already in-
troduced in volume 1, section 1.1.12, page 24:
(P ): find u, defined in Ω, such that

−∆u = f in Ω
u = g on ∂Ω

where f and g are given.


The discontinuous finite element space is defined by:

Xh = {vh ∈ L2 (Ω); vh|K ∈ Pk , ∀K ∈ Th }

where k ⩾ 1 is the polynomial degree. As elements of Xh do not belongs to H 1 (Ω), due to


discontinuities at inter-elements, we introduce the broken Sobolev space:

H 1 (Th ) = {v ∈ L2 (Ω); v|K ∈ H 1 (K), ∀K ∈ Th }

such that Xh ⊂ H 1 (Th ). We introduce the folowing bilinear form ah (., .) and linear for lh (.),
defined for all u, v ∈ H 1 (Th ) by (see e.g. di Pietro and Ern, 2012, p. 125 and 127, eqn. (4.12)):
Z X Z
ah (u, v) = ∇h u.∇h v dx + (κs [[u]] [[v]] − {{∇h u.n}} [[v]] − [[u]] {{∇h v.n}}) ds (4.8)
Ω S∈Sh S
Z Z
lh (v) = f u dx + (κs g v − g ∇h v.n) ds (4.9)
Ω ∂Ω

The last term involves a sum over Sh , the set of all sides of the mesh Th , i.e. the internal sides
and the boundary sides. By convenience, the definition of the jump and average are extended
to all boundary sides as [[u]] = {{u}} = u. Note that, as for the previous transport problem, the
Dirichlet boundary condition u = g is weakly imposed on ∂Ω via the integrals on the boundary.
Finally, κs > 0 is a stabilization parameter on a side S. The stabilization term associated to κs is
present in order to achieve coercivity: it penalize interface and boundary jumps. A common choice
is κs = β h−1s where β > 0 is a constant and hs is a local length scale associated to the current
side S. One drawnback to this choice is that it requires the end user to specify the numerical
constant β. From one hand, if the value of this parameter is not sufficiently large, the form ah (., .)
is not coercive and the approximate solution develops instabilities an do not converge [Epshteyn
and Rivière, 2007]. From other hand, if the value of this parameter is too large, its affect the
overall efficiency of the iterative solver of the linear system: the spectral condition number of the
matrix associated to ah (., .) grows linearly with this paramater [Castillo, 2002]. An explicit choice
of penalty parameter is proposed [Shahbazi, 2005]: κs = β ϖs where β = (k + 1)(k + d)/d and

meas(∂K)
when S = K ∩ ∂Ω is a boundary side


meas(K)

ϖs =  
meas(∂K0 ) meas(∂K1 )
 max , when S = K0 ∩ K1 is an internal side


meas(K0 ) meas(K1 )

Note that ϖs scales as h−1 s . Now, the computation of the penalty parameter is fully automatic
and the convergence of the method is always guaranted to converge. Moreover, this choice has
been founded to be sharp and it preserves the optimal efficiency of the iterative solvers. Finally,
the discrete variational formulation writes:
(F V )h : find uh ∈ Xh such that

ah (uh , vh ) = lh (vh ), ∀vh ∈ Xh


168 Rheolef version 7.2

The following code implement this problem in the Rheolef environment.

File 4.9: dirichlet_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cosinusprod_laplace . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh ( omega , argv [2]);
9 size_t d = omega . dimension ();
10 size_t k = Xh . degree ();
11 Float beta = ( k +1)*( k + d )/ Float ( d );
12 trial u ( Xh ); test v ( Xh );
13 form a = integrate ( dot ( grad_h ( u ) , grad_h ( v )))
14 + integrate ( " sides " , beta * penalty ()* jump ( u )* jump ( v )
15 - jump ( u )* average ( dot ( grad_h ( v ) , normal ()))
16 - jump ( v )* average ( dot ( grad_h ( u ) , normal ())));
17 field lh = integrate ( f ( d )* v )
18 + integrate ( " boundary " , beta * penalty ()* g ( d )* v
19 - g ( d )* dot ( grad_h ( v ) , normal ()));
20 a . uu (). s et_def inite_ posit ive ( true );
21 field uh ( Xh );
22 problem p ( a );
23 p . solve ( lh , uh );
24 dout << uh ;
25 }

Comments

The penalty() pseudo-function implements the computation of ϖs in Rheolef. The right-hand


side f and g are given by (1.4), volume 1, page 25. In that case, the exact solution is known.
Running the one-dimensional case writes:

make dirichlet_dg
mkgeo_grid -e 10 > line.geo
./dirichlet_dg line P1d | field -
./dirichlet_dg line P2d | field -

1 u(x) 1 u(x)
uh (x) uh (x)

h = 1/10 h = 1/20

0 0

−1 −1
0 0.5 1 0 0.5 1
x x

Figure 4.10: The discontinuous Galerkin method for the Poisson problem when k = 1 and d = 1.

Fig. 4.10 plots the one-dimensional solution when k = 1 for two meshes. Observe that the jumps at
inter-element nodes decreases very fast with mesh refinement and are no more perceptible on the
Chapter 4. Discontinuous Galerkin methods 169

plots. Recall that the Dirichlet boundary conditions at x = 0 and x = 1 is only weakly imposed:
the corresponding jump at the boundary is also not perceptible.
The two-dimensional case writes:

mkgeo_grid -t 10 > square.geo


./dirichlet_dg square P1d | field -elevation -
./dirichlet_dg square P2d | field -elevation -

and the three-dimensional one

mkgeo_grid -T 10 > cube.geo


./dirichlet_dg cube P1d | field -
./dirichlet_dg cube P2d | field -

Error analysis

1 1
∥u − uh ∥0,Ω ∥u − uh ∥∞,Ω

2=k+1
2=k+1
10−5 10−5

5=k+1 5=k+1
10−10 10−10
k =1 k =1
k =2 k =2
k =3 k =3
k =4 k =4
10−15 10−15
10−2 10−1 10−2 10−1
h h
1
∥u − uh ∥1,h
1=k

10−5

4=k

k =1
k =2
k =3
k =4
10−10
10−2 10−1
h

Figure 4.11: The discontinuous Galerkin method for the Poisson problem: convergence when
d = 2.

The space H 1 (Th ) is equiped with the norm ∥.∥1,h , defined for all v ∈ H 1 (Th by di Pietro and Ern
170 Rheolef version 7.2

[2012, p. 128]:
X Z
∥v∥21,h = ∥∇h v∥20,Ω + h−1 2
s [[v]] ds
S∈Sh S

The code cosinusprod_error_dg.cc compute the error in these norms. This code it is not listed
here but is available in the Rheolef example directory. The computation of the error writes:

make cosinusprod_error_dg
./dirichlet_dg square P1d | cosinusprod_error_dg

Fig. 4.11 plots the error u − uh in L2 , L∞ and the ∥.∥1,h norms. The L2 and L∞ error norms
behave as O hk+1 for all k ⩾ 0, while the ∥.∥1,h one behaves as O hk , which is optimal.

4.3.2 The Helmholtz problem with Neumann boundary conditions

The Poisson problem with non-homogeneous Neumann boundary conditions has been already
introduced in volume 1, section 1.2, page 32:
(P ): find u, defined in Ω, such that

u − ∆u = f in Ω
∂u
= g on ∂Ω
∂n

where f and g are given. We introduce the folowing bilinear form ah (., .) and linear for lh (.),
defined for all u, v ∈ H 1 (Th ) by (see e.g. di Pietro and Ern, 2012, p. 127, eqn. (4.16)):

Z
ah (u, v) = (u v + ∇h u.∇h v) dx (4.10)

X Z
+ (βϖs [[u]] [[v]] − {{∇h u.n}} [[v]] − [[u]] {{∇h v.n}}) ds (4.11)
(i) S
S∈Sh
Z Z
lh (v) = f u dx + g v ds (4.12)
Ω ∂Ω

Let us comment the changes between these forms and those used for the Poisson problem with
Dirichlet boundary conditions. The Poisson operator −∆ has been replaced by the Helmholtz one
I − ∆ in order to have an unique solution. Remark also that the sum is performed in (4.8) for all
(i)
internal sides in Sh , while, in (4.8), for Dirichlet boundary conditions, it was for all sides in Sh ,
i.e. for both boundary and internal sides. Also, the right-hand-side linear form lh (.). do no more
involves any sum over sides.
Finally, the discrete variational formulation writes:
(F V )h : find uh ∈ Xh such that

ah (uh , vh ) = lh (vh ), ∀vh ∈ Xh

The following code implement this problem in the Rheolef environment.


Chapter 4. Discontinuous Galerkin methods 171

File 4.10: neumann_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sinusprod_helmholtz . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh ( omega , argv [2]);
9 size_t d = omega . dimension ();
10 size_t k = Xh . degree ();
11 Float beta = ( k +1)*( k + d )/ Float ( d );
12 trial u ( Xh ); test v ( Xh );
13 form a = integrate ( u * v + dot ( grad_h ( u ) , grad_h ( v )))
14 + integrate ( " internal_sides " ,
15 beta * penalty ()* jump ( u )* jump ( v )
16 - jump ( u )* average ( dot ( grad_h ( v ) , normal ()))
17 - jump ( v )* average ( dot ( grad_h ( u ) , normal ())));
18 field lh = integrate ( f ( d )* v ) + integrate ( " boundary " , g ( d )* v );
19 field uh ( Xh );
20 problem p ( a );
21 p . solve ( lh , uh );
22 dout << uh ;
23 }

Comments

The right-hand side f and g are given by (1.5), volume 1, page 25. In that case, the exact solution
is known. Running the program is obtained from the non-homogeneous Dirichlet case, by replacing
dirichlet_dg by neumann_dg.

4.3.3 [New] Heterogeneous diffusion


The problem with with non-constant diffusion coefficients has already been introduced in sec-
tion 4.3.3, page 171 when studying continuous approximations. It writes
(P ): find u defined in Ω such that:

−div(η∇u) = f in Ω
u = 0 on Γleft ∪ Γright
∂u
= 0 on Γtop ∪ Γbottom when d ⩾ 2
∂n
∂u
= 0 on Γfront ∪ Γback when d = 3
∂n
where f is a given source term. We consider here the important special case when the diffusion
coefficient η is piecewise constant:

ε when x ∈ Ωwest

η(x) =
1 when x ∈ Ωeast

where (Ωwest , Ωeast ) is a partition of Ω in two parts (see Fig. 1.10, page 38). This is the so-
called transmission problem: the solution and the flux are continuous on the interface Γ0 =
∂Ωeast ∩ ∂Ωwest between the two domains where the problem reduce to a constant diffusion one:

uΩwest = uΩeast on Γ0
∂u/Ωwest ∂uΩeast
ε = on Γ0
∂n ∂n
172 Rheolef version 7.2

Following Dryja [2003], we use the discontinuous Galerkin with diffusion-dependent weights to
formulate the consistency and symmetry terms in the discrete bilinear form and we penalize
interface and boundary jumps using a diffusion-dependent parameter scaling as the harmonic
mean of the diffusion coeffcient. As pointed out by di Pietro and Ern [2012, p. 150], this a
penalty strategy is particularly important in heterogeneous diffusion-convection problems where
the diffusion coeffcient takes locally small values leading to advection-dominated regimes. In this
context, the exact solution exhibits sharp inner layers which, in practice, are not resolved by the
underlying meshes, so that excessive penalty at such layers triggers spurious oscillations. Using
the harmonic mean of the diffusion coeffcient to penalize jumps turns out to tune automatically
the amount of penalty and thereby avoid such oscillations.
(i)
The weighted average is defined for any function v and on any side S ∈ Sh by

{{v}}ω = ωK0 ,S v|K0 + ωK1 ,S v|K1

where K0 , K1 ∈ Th such that S = K0 ∩ K1 and where the two weights ωK0 ,S , ωK1 ,S express

η|K0 η|K1
ωK0 ,S = and ωK1 ,S =
η|K0 + η|K1 η|K0 + η|K1

Note that ωK0 ,S + ωK1 ,S = 1. On a boundary side S ⊂ ∂Ω„ we set {{v}}ω = v.


The bilinear form ah (., .) and linear for lh (.) are obtained by simply using the weighted aver-
age {{.}}ω instead of {{.}} and taking into accound upon te mixed Dirichlet and Neumann boundary
condition. For all u, v ∈ H 1 (Th ), they are given by (see e.g. di Pietro and Ern, 2012, p. 155, eqn.
(4.64)):

Z X Z
ah (u, v) = η∇h u.∇h v dx + (ηs κs [[u]] [[v]] − {{η∇h u.n}}w [[v]] − {{η∇h v.n}}w [[u]]) ds
Ω (i) S
S∈Sh ∪Γd
Z Z
lh (v) = f u dx + (κs g v − g ∇h v.n) ds
Ω Γd

where Γd = Γleft ∪ Γright denote, for convenience, the boundary domain corresponding to the non-
homogeous Dirichlet boundary condition, ηs = 2/(1/η|K0 + 1/η|K1 ) is the geometric mean of the
diffusion coefficient on an internal side and κs = β ϖs is the stabilization, as introduced in the
previous section 4.3.1. Finally, the discrete variational formulation writes:
(F V )h : find uh ∈ Xh such that

ah (uh , vh ) = lh (vh ), ∀vh ∈ Xh

The following code implement this problem in the Rheolef environment.


Chapter 4. Discontinuous Galerkin methods 173

File 4.11: transmission_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 geo omega ( argv [1]);
7 string approx = ( argc > 2) ? argv [2] : " P1d " ;
8 Float epsilon = ( argc > 3) ? atof ( argv [3]) : 1e -2;
9 space Xh ( omega , approx );
10 size_t d = omega . dimension ();
11 size_t k = Xh . degree ();
12 check_macro ( k >= 1 , " polynomial degree k = " <<k < < " shoud be >= 1 " );
13 Float beta = ( k +1)*( k + d )/ d ;
14 field eta_h ( Xh );
15 eta_h [ " west " ] = epsilon ;
16 eta_h [ " east " ] = 1;
17 geo gamma_d = omega [ " left " ] + omega [ " right " ];
18 geo Shd = omega [ " internal_sides " ] + gamma_d ;
19 trial u ( Xh ); test v ( Xh );
20 auto eta_s = 2/(1/ inner ( eta_h ) + 1/ outer ( eta_h ));
21 auto eta_w_o = inner ( eta_h )/( inner ( eta_h ) + outer ( eta_h ));
22 auto eta_w_i = outer ( eta_h )/( inner ( eta_h ) + outer ( eta_h ));
23 auto average_w_u = eta_w_i * inner ( eta_h * dot ( grad_h ( u ) , normal ()))
24 + eta_w_o * outer ( eta_h * dot ( grad_h ( u ) , normal ()));
25 auto average_w_v = eta_w_i * inner ( eta_h * dot ( grad_h ( v ) , normal ()))
26 + eta_w_o * outer ( eta_h * dot ( grad_h ( v ) , normal ()));
27 form a = integrate ( eta_h * dot ( grad_h ( u ) , grad_h ( v )))
28 + integrate ( Shd , beta * penalty ()* eta_s * jump ( u )* jump ( v )
29 - jump ( u )* average_w_v
30 - jump ( v )* average_w_u );
31 field lh = integrate ( v );
32 solver_option sopt ;
33 sopt . iterative = false ;
34 problem p (a , sopt );
35 field uh ( Xh );
36 p . solve ( lh , uh );
37 dout << catchmark ( " epsilon " ) << epsilon << endl
38 << catchmark ( " u " ) << uh ;
39 }

Comments

The test-case from section 4.3.3, page 171, is considered here: recall that the exact solution is
known. The gamma_d boundary domain represents the union Γd of the two boundary domains Γleft
(i)
and Γright pre-defined by the mesh. Conversely, the Shd domain groups the internal sides from Sh
with those of Γd . Note the usage of the summation for merging these domains. Note the usage of
the class solver_option for imposing a direct linear solver: indeed, when d = 3, Rheolef selects
by default an iterative solver, but due to the bad condition number of the system, it is easier to
use a direct one. Running the one-dimensional case writes:

make transmission_dg
mkgeo_grid -e 10 -region > line.geo
./transmission_dg line P1d | field -
./transmission_dg line P2d | field -

Fig. 4.12 plots the one-dimensional solution when k = 1 for two meshes. Observe that the jumps
at inter-element nodes decreases very fast with mesh refinement and are no more perceptible on
the plots. Recall that the Dirichlet boundary conditions at x = 0 and x = 1 is only weakly
imposed: the corresponding jump at the boundary is small on the finer mesh. When k ⩾ 2, the
approximate solution coincides with the interpolation of the exact one, since the exact solution is
piecewise quadratic and the interface of discontinuity coincides with internal mesh sides.
174 Rheolef version 7.2

h = 1/10
h = 1/6
3 exact

0
0 0.5 1

Figure 4.12: The discontinuous Galerkin method for the transmission problem with ε = 10−2 ,
d = 1, P1d approximation.

The two-dimensional case writes:

mkgeo_grid -t 10 -region > square.geo


./transmission_dg square P1d | field -elevation -
./transmission_dg square P2d | field -elevation -

and the three-dimensional one

mkgeo_grid -T 10 -region > cube.geo


./transmission_dg cube P1d | field -
./transmission_dg cube P2d | field -
Chapter 4. Discontinuous Galerkin methods 175

4.3.4 Nonlinear scalar hyperbolic problems with diffusion

A time-dependent nonlinear second order problem with nonlinear first order dominated terms
problem writes:
(P ): find u, defined in ]0, T [×Ω, such that

∂u
+ div f (u) − ε∆u = 0 in ]0, T [×Ω (4.13a)
∂t
u(t = 0) = u0 in Ω (4.13b)
u = g on ]0, T [×∂Ω (4.13c)

where ε > 0, T > 0, Ω ⊂ Rd , d = 1, 2, 3 and the initial condition u0 being known. The function
f : R −→ Rd is also known and supposed to be continuously differentiable. The initial data u0 ,
defined in Ω, and the boundary one, g, defined on ∂Ω are given.
Comparing (4.13a)-(4.13c) with the non-diffusive case (4.3a)-(4.3c) page 157, the second order
term has been added in (4.13a) and the upstream boundary condition has been replaced by a
Dirichlet one (4.13c).

4.3.5 Example: the Burgers equation with diffusion

Problem statement and its exact solution

u(t, x) t=0
t=1
2

0
−1 0 1
x

Figure 4.13: An exact solution for the Burgers equation with diffusion (ε = 10−1 , x0 = −1/2).

Our model problem in this chapter is the one-dimensional Burgers equation. It was introduced
in section 4.2.3, page 161 with the choice f (u) = u2 /2, for all u ∈ R. Equation (4.13a) admits an
exact solution

 
x − x0 − t
u(t, x) = 1 − tanh (4.14)

176 Rheolef version 7.2

File 4.12: burgers_diffusion_exact.h


1 struct u_exact {
2 Float operator () ( const point & x ) const {
3 return 1 - tanh (( x [0] - x0 - t )/(2* epsilon )); }
4 u_exact ( Float e1 , Float t1 =0) : epsilon ( e1 ) , t ( t1 ) , x0 ( -0.5) {}
5 Float M () const { return 0; }
6 Float epsilon , t , x0 ;
7 };
8 using u_init = u_exact ;
9 using g = u_exact ;
The solution is represents on Fig. 4.13. Here x0 represent the position of the front at t = 0 and
ε is a characteristic width of the front. The initial and boundary condition are chosen such that
u(t, x) is the solution of (4.13a)-(4.13c).

10−1 10−1
∥u − uh ∥L∞ (0,T ;L2 ) ∥u − uh ∥L∞ (0,T ;L2 )
(a) k = 1, h = 2/50 (b) k = 1, h = 2/400
10−2 10−2

3
10−3 10−3
1

10−4 1
10−4
p=1
k=1 p=2
k=2 p=3
10−5 10−5
10−6 10−5 10−4 10−3 10−2 10−1 10−5 10−4 10−3 10−2 10−1
∆t ∆t

Figure 4.14: Convergence of the first order semi-implicit scheme for the Burgers equation with
diffusion (ϵ = 0.1, T = 1). (a) first order semi-implicit scheme ; (b) Runge-Kutta semi-implicit
scheme with p = 3.

Fig. 4.14.a plots the error versus ∆t for the semi-implicit scheme when k = 1 and 2, and for
h = 2/50. The time step for which the error becomes independent upon ∆t and depends only
upon h is of about ∆t = 10−3 when k = 1 and of about 10−5 when k = 2. This approach is clearly
inefficient for high order polynomial k and a hiher order time scheme is required.
Fig. 4.14.b plots the error versus ∆t for the Runge-Kutta semi-implicit scheme with p = 3, k = 1
and h = 2/200. The scheme is clearly only first-order, which is still unexpected. More work is
required...

Space discretization

The discontinuous finite element space is defined by:

Xh = {vh ∈ L2 (Ω); vh|K ∈ Pk , ∀K ∈ Th }

where k ⩾ 1 is the polynomial degree. As elements of Xh do not belongs to H 1 (Ω), due to


discontinuities at inter-elements, we introduce the broken Sobolev space:

H 1 (Th ) = {v ∈ L2 (Ω); v|K ∈ H 1 (K), ∀K ∈ Th }

such that Xh ⊂ H 1 (Th ). As for the Dirichlet problem, introduce the folowing bilinear form
ah (., .) and linear for lh (.), defined for all u, v ∈ H 1 (Th ) by (see e.g. di Pietro and Ern, 2012,
Chapter 4. Discontinuous Galerkin methods 177

p. 125 and 127, eqn. (4.12)):


Z X Z
ah (u, v) = ∇h u.∇h v dx + (κs [[u]] [[v]] − {{∇h u.n}} [[v]] − [[u]] {{∇h v.n}}) ds(4.15)
Ω S∈Sh S
Z
ℓh (v) = (κs g v − g ∇h v.n) ds (4.16)
∂Ω

The semi-discrete problem writes in variational form [di Pietro and Ern, 2012, p. 100]:
(P )h : find uh ∈ C 1 ([0, T ], Xh ) such that
Z Z
∂uh
vh dx Gh (uh ) vh dx + ε ah (uh , vh ) = ε ℓh (vh ), ∀vh ∈ Xh
Ω ∂t Ω
uh (t = 0) = πh (u0 )

where Gh has been introduced in (4.5), page 157.

Time discretization

Explicit Runge-Kutta scheme is possible for this problem but it leads to an excessive Courant-
Friedrichs-Levy condition for the time step ∆t, that is required to be lower than an upper bound
that varies in O(h2 ). The idea here is to continue to explicit the first order nonlinear terms and
implicit the linear second order terms. Semi-implicit second order Runge-Kutta scheme was first
introduced by Ascher, Ruuth, and Spiteri [1997] and then extended to third and fourth order by
Calvo, de Frutos, and Novo [2001]. Wang, Shu, and Zhang [2015a,b] applied it in the context of
the discontinuous Galerkin method. The finite dimensional problem can be rewritten as
(P )h : find uh ∈ C 1 ([0, T ], Xh ) such that

∂uh
+ Gh (t, uh ) + Ah (t, uh ) = 0, ∀t ∈]0, T [
∂t
uh (t = 0) = πh (u0 )

where Gh has been introduced in (4.5), page 157 and Ah denotes the diffusive term. The semi-
implicit Runge-Kutta scheme with p ⩾ 0 intermediate steps writes at time step tn :

un,0
h = unh (4.17a)
i
X   i−1
X  
un,i
h = unh − ∆t αi,j Ah tn,j , un,j
h − ∆t α̃i,j Gh tn,j , un,j
h , i = 1, . . . , p(4.17b)
j=1 j=0
p
X   p
X  
un+1
h = unh − ∆t βi Ah tn,i , un,i
h − ∆t β̃i Gh tn,i , un,i
h (4.17c)
i=1 i=0
  Pi Pi−1
where un,i
h are the p ⩾ 1 intermediate states, tn,i = tn + γi ∆t, γi = j=1 αi,j = j=0 α̃i,j ,
1⩽i⩽p
and (αi,j )0⩽i,j⩽p , (α̃i,j )0⩽i,j⩽p , (βi )0⩽i⩽p and (β̃i )0⩽i⩽p are the coefficients of the scheme [Ascher
et al., 1997, Calvo et al., 2001, Wang et al., 2015a]. At each time step, have to solve p linear
systems. From (4.17b) we get for all i = 1, . . . , p:
i−1
X   i−1
X  
(I + ∆t αi,i Ah (tn,i )) un,i
h = unh − ∆t αi,j Ah tn,j , un,j
h − ∆t α̃i,j Gh tn,j , un,j
h
j=1 j=0

Note that when the matrix coefficients of Ah (t, .) are indepencdent of t, the matrix involved on
the right-hand-side of the previous equation can be factored one time for all.
178 Rheolef version 7.2

File 4.13: burgers_diffusion_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " burgers . icc "
5 # include " burgers_flux_godunov . icc "
6 # include " ru n g e_ k u tt a _ se m i im p l ic i t . icc "
7 # include " b u rg er s _d if fu s io n_ ex a ct . h "
8 # undef NEUMANN
9 # include " b u r g e r s _ d i f f u s i o n _ o p e r a t o r s . icc "
10 int main ( int argc , char ** argv ) {
11 environment rheolef ( argc , argv );
12 geo omega ( argv [1]);
13 space Xh ( omega , argv [2]);
14 size_t k = Xh . degree ();
15 Float epsilon = ( argc > 3) ? atof ( argv [3]) : 0.1;
16 size_t nmax = ( argc > 4) ? atoi ( argv [4]) : 500;
17 Float tf = ( argc > 5) ? atof ( argv [5]) : 1;
18 size_t p = ( argc > 6) ? atoi ( argv [6]) : min ( k +1 , rk :: pmax );
19 Float delta_t = tf / nmax ;
20 size_t d = omega . dimension ();
21 Float beta = ( k +1)*( k + d )/ Float ( d );
22 trial u ( Xh ); test v ( Xh );
23 form m = integrate ( u * v );
24 integrate_option iopt ;
25 iopt . invert = true ;
26 form inv_m = integrate ( u *v , iopt );
27 form a = epsilon *(
28 integrate ( dot ( grad_h ( u ) , grad_h ( v )))
29 # ifdef NEUMANN
30 + integrate ( " internal_sides " ,
31 # else // NEUMANN
32 + integrate ( " sides " ,
33 # endif // NEUMANN
34 beta * penalty ()* jump ( u )* jump ( v )
35 - jump ( u )* average ( dot ( grad_h ( v ) , normal ()))
36 - jump ( v )* average ( dot ( grad_h ( u ) , normal ()))));
37 vector < problem > pb ( p +1);
38 for ( size_t i = 1; i <= p ; ++ i ) {
39 form ci = m + delta_t * rk :: alpha [ p ][ i ][ i ]* a ;
40 pb [ i ] = problem ( ci );
41 }
42 vector < field > uh ( p +1 , field ( Xh ,0));
43 uh [0] = lazy_interpolate ( Xh , u_init ( epsilon ));
44 branch even ( " t " ," u " );
45 dout << catchmark ( " epsilon " ) << epsilon << endl
46 << even (0 , uh [0]);
47 for ( size_t n = 0; n < nmax ; ++ n ) {
48 Float tn = n * delta_t ;
49 Float t = tn + delta_t ;
50 field uh_next = uh [0] - delta_t * rk :: tilde_beta [ p ][0]*( inv_m * gh ( epsilon , tn , uh [0] , v ));
51 for ( size_t i = 1; i <= p ; ++ i ) {
52 Float ti = tn + rk :: gamma [ p ][ i ]* delta_t ;
53 field rhs = m * uh [0] - delta_t * rk :: tilde_alpha [ p ][ i ][0]* gh ( epsilon , tn , uh [0] , v );
54 for ( size_t j = 1; j <= i -1; ++ j ) {
55 Float tj = tn + rk :: gamma [ p ][ j ]* delta_t ;
56 rhs -= delta_t *( rk :: alpha [ p ][ i ][ j ]*( a * uh [ j ] - lh ( epsilon , tj , v ))
57 + rk :: tilde_alpha [ p ][ i ][ j ]* gh ( epsilon , tj , uh [ j ] , v ));
58 }
59 rhs += delta_t * rk :: alpha [ p ][ i ][ i ]* lh ( epsilon , ti , v );
60 pb [ i ]. solve ( rhs , uh [ i ]);
61 uh_next -= delta_t *( inv_m *( rk :: beta [ p ][ i ]*( a * uh [ i ] - lh ( epsilon , ti , v ))
62 + rk :: tilde_beta [ p ][ i ]* gh ( epsilon , ti , uh [ i ] , v )));
63 }
64 uh_next = limiter ( uh_next );
65 dout << even ( tn + delta_t , uh_next );
66 uh [0] = uh_next ;
67 }
68 }
Chapter 4. Discontinuous Galerkin methods 179

File 4.14: burgers_diffusion_operators.icc


1 field lh ( Float epsilon , Float t , const test & v ) {
2 # ifdef NEUMANN
3 return field ( v . get_vf_space () , 0);
4 # else // NEUMANN
5 size_t d = v . get_vf_space (). get_geo (). dimension ();
6 size_t k = v . get_vf_space (). degree ();
7 Float beta = ( k +1)*( k + d )/ Float ( d );
8 return epsilon * integrate ( " boundary " ,
9 beta * penalty ()* g ( epsilon , t )* v
10 - g ( epsilon , t )* dot ( grad_h ( v ) , normal ()));
11 # endif // NEUMANN
12 }
13 field gh ( Float epsilon , Float t , const field & uh , const test & v ) {
14 return - integrate ( dot ( compose (f , uh ) , grad_h ( v )))
15 + integrate ( " internal_sides " ,
16 compose ( phi , normal () , inner ( uh ) , outer ( uh ))* jump ( v ))
17 + integrate ( " boundary " ,
18 compose ( phi , normal () , uh , g ( epsilon , t ))* v );
19 }
The included file ‘runge_kutta_semiimplicit.icc’ is not shown here but is available in the
example directory.

Running the program

u(t, x) t=0 u(t, x) t=0


t=1 t=1
2 2

1 1

0 0

−1 0 1 −1 0 1
x x

Figure 4.15: Burgers equation with a small diffusion (ε = 10−3 ). Third order in time semi-implicit
scheme with P1d element. (left) without limiter ; (right) with limiter.

Running the program writes with h = 2/400 and ε = 10−2 writes:

make burgers_diffusion_dg
mkgeo_grid -e 400 -a -1 -b 1 > line.geo
mpirun -np 8 ./burgers_diffusion_dg line P1d 0.01 1000 1 3 > line.branch
branch -gnuplot line.branch -umin -0.1 -umax 2.1

It could take about one minute, depending on your computer. Decreasing ε = 10−3 leads to a
sharper solution:

mpirun -np 8 ./burgers_diffusion_dg line P1d 0.001 1000 1 3 > line.branch


branch -gnuplot line.branch -umin -0.1 -umax 2.1
180 Rheolef version 7.2

As mentioned by Wang et al. [2015a], the time step should be chosen smaller when ε decreases.
The result is shown on Fig. 4.15.left. Observe the oscillations near the smoothed shock when there
is no limiter while the value goes outside [0, 2]. Conversely, with a limiter (see Fig. 4.15.right) the
approximate solution is decreasing and there are no more oscillations: the values remains in the
range [0 : 2].
Chapter 4. Discontinuous Galerkin methods 181

4.4 Fluids and solids computations revisited

4.4.1 The linear elasticity problem

The elasticity problem (2.2) has been introduced in volume 1, section 2.1.1, page 43.
(P ): find u such that

−div (λdiv(u).I + 2D(u)) = f in Ω


u = g on ∂Ω

where λ ⩾ −1 is a constant and f , g given. This problem is a natural extension to vector-valued


field of the Poisson problem with Dirichlet boundary conditions.
The variational formulation writes:
(F V )h : find u ∈ V(g) such that

a(u, v) = lh (v), ∀v ∈ V(0)

where

V(g) = {v ∈ H 1 (Ω)d ; v = g on ∂Ω}


Z
a(u, v) = (λ div(u) div(v) + 2D(u) : D(v)) dx
ZΩ
l(v) = f .v dx

The discrete variational formulation writes:


(F V )h : find uh ∈ Xh such that

ah (uh , vh ) = lh (vh ), ∀vh ∈ Xh

where

Xh = {vh ∈ L2 (Ω)d ; vh|K ∈ Pkd , ∀K ∈ Th }


Z
ah (u, v) = (λ divh (u) divh (v) + 2Dh (u) : Dh (v)) dx

X Z
+ (βϖs [[u]].[[v]] − [[u]].{{λdivh (v)n + 2Dh (v)n}} − [[v]].{{λdivh (u)n + 2Dh (u)n}}) ds
S∈Sh S
Z Z
lh (v) = f .v dx + g. (βϖs v − λdivh (v)n − 2Dh (v)n) ds
Ω ∂Ω

where k ⩾ 1 is the polynomial degree in Xh .


182 Rheolef version 7.2

File 4.15: elasticity_taylor_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " taylor . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh ( omega , argv [2] , " vector " );
9 Float lambda = ( argc > 3) ? atof ( argv [3]) : 1;
10 size_t d = omega . dimension ();
11 size_t k = Xh . degree ();
12 Float beta = ( k +1)*( k + d )/ Float ( d );
13 trial u ( Xh ); test v ( Xh );
14 form a = integrate ( lambda * div_h ( u )* div_h ( v ) + 2* ddot ( Dh ( u ) , Dh ( v )))
15 + integrate ( omega . sides () ,
16 beta * penalty ()* dot ( jump ( u ) , jump ( v ))
17 - lambda * dot ( jump ( u ) , average ( div_h ( v )* normal ()))
18 - lambda * dot ( jump ( v ) , average ( div_h ( u )* normal ()))
19 - 2* dot ( jump ( u ) , average ( Dh ( v )* normal ()))
20 - 2* dot ( jump ( v ) , average ( Dh ( u )* normal ())));
21 field lh = integrate ( dot ( f () , v ))
22 + integrate ( omega . boundary () ,
23 beta * penalty ()* dot ( g () , jump ( v ))
24 - lambda * dot ( g () , average ( div_h ( v )* normal ()))
25 - 2* dot ( g () , average ( Dh ( v )* normal ())));
26 field uh ( Xh );
27 problem p ( a );
28 p . solve ( lh , uh );
29 dout << uh ;
30 }

Comments

The data are given when d = 2 by:


 
− cos(πx0 ) sin(πx1 )
g(x) = and f = 2π 2 g (4.18)
sin(πx0 ) cos(πx1 )
This choice is convenient since the exact solution is known u = g. This benchmark solution
was proposed by Taylor [1923] in the context of the Stokes problem. Note that the solution is
independent of λ since div(u) = 0.

File 4.16: taylor.h


1 struct g {
2 point operator () ( const point & x ) const {
3 return point ( - cos ( pi * x [0])* sin ( pi * x [1]) ,
4 sin ( pi * x [0])* cos ( pi * x [1])); }
5 g () : pi ( acos ( Float ( -1.0))) {}
6 const Float pi ;
7 };
8 struct f {
9 point operator () ( const point & x ) const { return 2* sqr ( pi )* _g ( x ); }
10 f () : pi ( acos ( Float ( -1.0))) , _g () {}
11 const Float pi ; g _g ;
12 };

As the exact solution is known, the error can be computed. The code
elasticity_taylor_error_dg.cc and its header file taylor_exact.h compute the error
in L2 , L∞ and energy norms. These files are not listed here but are available in the Rheolef
example directory. The computation writes:

make elasticity_taylor_dg elasticity_taylor_error_dg


mkgeo_grid -t 10 > square.geo
Chapter 4. Discontinuous Galerkin methods 183

./elasticity_taylor_dg square P1d | ./elasticity_taylor_error_dg


./elasticity_taylor_dg square P2d | ./elasticity_taylor_error_dg

4.4.2 The Stokes problem

Let us consider the Stokes problem for the driven cavity in Ω =]0, 1[d , d = 2, 3. The problem has
been introduced in volume 1, section 2.1.4, page 53.
(P ): find u and p, defined in Ω, such that

− div(2D(u)) + ∇p = f in Ω,
− div u = 0 in Ω,
u = g on ∂Ω

where f and g are given. This problem is the extension to divergence free vector fields of the
elasticity problem. The variational formulation writes:
(V F )h find u ∈ V(g) and p ∈ L2 (Ω) such that:

a(u, v) + b(v, p) = l(v), ∀v ∈ V(0),


(4.19)
b(u, q) = 0, ∀q ∈ L2 (Ω)

where

V(g) = {v ∈ H 1 (Ω)d ; v = g on ∂Ω}


Z
a(u, v) = 2D(u) : D(v) dx

Z
b(u, q) = − div(u) q dx
Z Ω
l(v) = f .v dx

The discrete variational formulation writes:


(V F )h find uh ∈ Xh and ph ∈ Qh such that:

ah (uh , vh ) + bh (vh , ph ) = lh (vh ), ∀vh ∈ Xh ,


(4.20)
bh (uh , qh ) − ch (ph , qh ) = kh (q), ∀qh ∈ Qh .

The discontinuous finite element spaces are defined by:

Xh = {vh ∈ L2 (Ω)d ; vh|K ∈ Pkd , ∀K ∈ Th }


Qh = {qh ∈ L2 (Ω)d ; qh|K ∈ Pkd , ∀K ∈ Th }

where k ⩾ 1 is the polynomial degree. Note that velocity and pressure are approximated by the
same polynomial order. This method was introduced by Cockburn et al. [2002] and some recent
theoretical results can be founded in di Pietro and Ern [2010]. The forms are defined for all
184 Rheolef version 7.2

u, v ∈ H 1 (Th )d and q ∈ L2 (Ω) by (see e.g. di Pietro and Ern, 2012, p. 249):

Z
ah (u, v) = 2Dh (u) : Dh (v) dx

X Z
+ (βϖs [[u]].[[v]] − [[u]].{{2Dh (v)n}} − [[v]].{{2Dh (u)n}}) ds
S∈Sh S
Z X Z
bh (u, q) = u.∇h q dx − {{u}}.n [[q]] ds
Ω (i) S
S∈Sh
X Z
ch (p, q) = hs [[p]] [[q]] ds
(i) S
S∈Sh
Z Z
lh (v) = f .v ds + g. (βϖs v − 2Dh (v) n) ds
ZΩ ∂Ω

kh (q) = g.n q ds
∂Ω

The stabilization form ch controls the pressure jump across internal sides. This stabilization term
is necessary when using equal order polynomial approximation for velocity and pressure. The
definition of the forms is grouped in a subroutine: it will be reused later for the Navier-Stokes
problem.

File 4.17: stokes_dirichlet_dg.icc


1 void s tokes_dirichlet_dg ( const space & Xh , const space & Qh ,
2 form & a , form & b , form & c , form & mp , field & lh , field & kh ,
3 integrate_option iopt = integrate_option ())
4 {
5 size_t k = Xh . degree ();
6 size_t d = Xh . get_geo (). dimension ();
7 Float beta = ( k +1)*( k + d )/ Float ( d );
8 trial u ( Xh ) , p ( Qh );
9 test v ( Xh ) , q ( Qh );
10 a = integrate (2* ddot ( Dh ( u ) , Dh ( v )) , iopt )
11 + integrate ( " sides " , beta * penalty ()* dot ( jump ( u ) , jump ( v ))
12 - 2* dot ( jump ( u ) , average ( Dh ( v )* normal ()))
13 - 2* dot ( jump ( v ) , average ( Dh ( u )* normal ())) , iopt );
14 lh = integrate ( dot ( f () , v ) , iopt )
15 + integrate ( " boundary " , beta * penalty ()* dot ( g () , v )
16 - 2* dot ( g () , Dh ( v )* normal ()) , iopt );
17 b = integrate ( dot (u , grad_h ( q )) , iopt )
18 + integrate ( " internal_sides " , - dot ( average ( u ) , normal ())* jump ( q ) , iopt );
19 kh = integrate ( " boundary " , dot ( g () , normal ())* q , iopt );
20 c = integrate ( " internal_sides " , h_local ()* jump ( p )* jump ( q ) , iopt );
21 mp = integrate ( p *q , iopt );
22 }

A simple test program writes:


Chapter 4. Discontinuous Galerkin methods 185

File 4.18: stokes_taylor_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " taylor . h "
5 # include " stokes_dirichlet_dg . icc "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 geo omega ( argv [1]);
9 space Xh ( omega , argv [2] , " vector " );
10 space Qh ( omega , argv [2]);
11 form a , b , c , mp ;
12 field lh , kh ;
13 s to k es_dirichlet_dg ( Xh , Qh , a , b , c , mp , lh , kh );
14 field uh ( Xh , 0) , ph ( Qh , 0);
15 problem_mixed stokes (a , b , c );
16 stokes . set_metric ( mp );
17 stokes . solve ( lh , kh , uh , ph );
18 dout << catchmark ( " u " ) << uh
19 << catchmark ( " p " ) << ph ;
20 }

Comments

The data are given when d = 2 by (4.18). This choice is convenient since the exact solution is
known u = g and p = 0. The code stokes_taylor_error_dg.cc compute the error in L2 , L∞
and energy norms. This code it is not listed here but is available in the Rheolef example directory.
The computation writes:

make stokes_taylor_dg stokes_taylor_error_dg


mkgeo_grid -t 10 > square.geo
./stokes_taylor_dg square P1d | ./stokes_taylor_error_dg
./stokes_taylor_dg square P2d | ./stokes_taylor_error_dg

4.5 The stationnary Navier-Stokes equations


4.5.1 Problem statemment
The Navier-Stokes problem has been already introduced in volume 1, section 4.5 page 185. Here
we consider the stationary version of this problem. Let Re ⩾ 0 be the Reynolds number. The
problem writes:
(P ): find u and p, defined in Ω, such that

Re (u.∇)u − div(2D(u)) + ∇p = f in Ω,
− div u = 0 in Ω,
u = g on ∂Ω

Note that, when Re > 0, the problem is nonlinear, due to the inertia term u.∇u. When Re = 0
the problem reduces to the linear Stokes problem, presented in the previous section/
The variational formulation of this nonlinear problem writes:
(F V ): find u ∈ V(g) and p ∈ L2 (Ω) such that

Re t(u; u, v) + a(u, v) + b(v, p) = l(v), ∀v ∈ V(0),


b(u, q) = 0, ∀q ∈ L2 (Ω)

where the space V(g) and forms a, b and l are given as in the previous section 4.4.2 for the Stokes
186 Rheolef version 7.2

problem and the trilinear form t(.; ., .) is given by:


Z
t(w; u, v) = ((w.∇)u).v dx

4.5.2 The discrete problem


Let
Z
t(w; u, u) = (w.∇u).u dx

Observe that, for all u, w ∈ H 1 (Ω)d we have


Z d−1 Z
X
(w.∇u).u dx = ui wj ∂j (ui ) dx
Ω i,j=0 Ω

d−1
X Z Z
= − ui ∂j (ui wj ) dx + u2i wj nj dx
i,j=0 Ω ∂Ω

d−1
X Z Z Z
= − ui ∂j (ui ) wj dx − u2i ∂j (wj ) dx + u2i wj nj dx
i,j=0 Ω Ω ∂Ω
Z Z Z
= − (w.∇u).u dx − 2
div(w) |u| dx + w.n |u|2 ds (4.21)
Ω Ω ∂Ω

Thus
Z Z Z
1 1
t(w; u, u) = (w.∇u).u dx = − div(w) |u|2 dx + w.n |u|2 ds
Ω 2 Ω 2 ∂Ω

When div(w) = 0, the trilinear form t(.; ., .) reduces to a boundary term: it is formally skew-
symmetric. The skew-symmetry of t is an important property: let (v, q) = (u, p) as test functions
in (F V ). We obtain:
a(u, u) = l(u)

In other words, we obtain the same energy balance as for the Stokes flow and inertia do not
contribute to the energy balance. This is an important property and we aim at obtaining the
same one at the discrete level. As the discrete solution uh is not exactly divergence free, following
Temam, we introduce the following modified trilinear form:
Z   Z
1 1
t∗ (w; u, v) = (w.∇u) .v + div(w) u.v dx − (w.n) u.v ds, ∀u, v, w ∈ H 1 (Ω)d
Ω 2 2 ∂Ω

This form integrates the non-vanishing terms and we have:

t∗ (w; u, u) = 0, ∀u, w ∈ H 1 (Ω)d

When the discrete solution is not exactly divergence free, it is better to use t∗ than t.
The discontinuous finite element spaces Xh and Qh and forms ah , bh , ch , lh and kh are defined as
in the previous section. Let us introduce t∗h , the following discrete trilinear form, defined for all
uh , vh , wh ∈ Xh :
Z   Z
∗ 1 1
th (wh ; uh , vh ) = (wh .∇h uh ) .vh + divh (wh ) uh .vh dx − (wh .n) uh .vh ds
Ω 2 2 ∂Ω
Chapter 4. Discontinuous Galerkin methods 187

Note that t∗h is similar to t∗ : the gradient and divergence has been replaced by their broken
counterpart in the first term. As Xh ̸⊂ H 1 (Ω)d , the skew-symmetry property is not expected to
be true at the discrete level. Then
X Z  1

1
Z
∗ 2
th (wh ; uh , uh ) = (wh .∇uh ) .uh + div(wh ) |uh | dx − (wh .n) |uh |2 ds
K 2 2 ∂Ω
K∈Th

As the restriction of uh and wh to each K ∈ Th belongs to H 1 (K)d , we have, using a similar


integration by part:
Z Z Z
1 1
(wh .∇uh ).uh dx = − div(wh ) |uh |2 dx + (wh .n) |uh |2 ds
K 2 K 2 ∂K
Thus
Z Z
1 X 1
t∗h (wh ; uh , uh ) = (wh .n) |uh |2 ds − (wh .n) |uh |2 ds
2 ∂K 2 ∂Ω
K∈Th

The terms on boundary sides vanish while those on internal sides can be grouped:
Z
1 X
t∗h (wh ; uh , uh ) = [[|uh |2 wh ]].n ds
2 (i) S
S∈Sh

The jump term [[(uh .vh ) wh ]].n is not easily manageable and could be developed. A short compu-
tation shows that, for all scalar fields ϕ, φ we have on any internal side the following identity:

[[ϕφ]] [[ϕ]]{{φ}} + {{ϕ}}[[φ]]


= (4.22)
1
{{ϕφ}} = {{ϕ}}{{φ}} + [[ϕ]][[φ]] (4.23)
4
Then
Z
1 X
t∗h (wh ; uh , uh ) {{wh }}.n [[|uh |2 ]] + [[wh ]].n {{|uh |2 }} ds

=
2 (i) S
S∈Sh
X Z  1

= {{wh }}.n ([[uh ]].{{uh }}) + [[wh ]].n {{|uh |2 }} ds
(i) S 2
S∈Sh

Thus, as expected, the skew-symmetry property is no more satisfied at the discrete level, due to
the jumps of the fields at the inter-element boundaries. Following the previous idea, we introduce
the following modified discrete trilinear form:
X Z  1


th (wh ; uh , vh ) = th (wh ; uh , vh ) − {{wh }}.n ([[uh ]].{{vh }}) + [[wh ]].n {{uh .vh }} ds
(i) S 2
S∈Sh
Z   Z
1 1
= (wh .∇h uh ) .vh + divh (wh ) uh .vh dx − (wh .n) uh .vh ds
Ω 2 2 ∂Ω
Z  
X 1
− {{wh }}.n ([[uh ]].{{vh }}) + [[wh ]].n {{uh .vh }} ds (4.24)
(i) S 2
S∈Sh

This expression has been proposed by di Pietro and Ern [2010, p. 22], eqn (72) (see also di Pietro
and Ern, 2012, p. 272, eqn (6.57)). The boundary term introduced in th may be compensated in
the right-hand side:
Z
∗ Re
lh (v) := lh (v) − (g.n) g.vh ds
2 ∂Ω
188 Rheolef version 7.2

Note that the boundary term introduced in th is compensated in the right-hand side lh∗ .

File 4.19: inertia.h


1 template < class W , class U , class V >
2 form inertia ( W w , U u , V v ,
3 integrate_option iopt = integrate_option ())
4 {
5 return
6 integrate ( dot ( grad_h ( u )* w , v ) + 0.5* div_h ( w )* dot (u , v ) , iopt )
7 + integrate ( " boundary " , - 0.5* dot (w , normal ())* dot (u , v ) , iopt )
8 + integrate ( " internal_sides " ,
9 - dot ( average ( w ) , normal ())* dot ( jump ( u ) , average ( v ))
10 - 0.5* dot ( jump ( w ) , normal ())
11 *( dot ( average ( u ) , average ( v )) + 0.25* dot ( jump ( u ) , jump ( v ))) , iopt );
12 }
13 field inertia_fix_rhs ( test v ,
14 integrate_option iopt = integrate_option ())
15 {
16 return integrate ( " boundary " , - 0.5* dot ( g () , normal ())* dot ( g () , v ) , iopt );
17 }

The discrete problem is


(F V )h : find uh ∈ Xh and p ∈ Qh such that

Re th (uh ; uh , vh ) + ah (uh , vh ) + bh (vh , ph ) = lh∗ (vh ), ∀vh ∈ Xh ,


(4.25)
bh (uh , qh ) − ch (ph , qh ) = kh (q), ∀qh ∈ Qh

The simplest
 approach
 for solving the discrete problem is to consider a fixed-point algorithm. The
(k)
sequence uh is defined by recurrence as:
k⩾0

(0)
• k = 0: let uh ∈ Xh being known.

(k−1) (k) (k)


• k ⩾ 0: let uh ∈ Xh given. Find uh ∈ Xh and ph ∈ Qh such that

     
(k−1) (k) (k) (k)
Re th uh ; uh , vh + ah uh , vh + bh vh , ph = lh∗ (vh ), ∀vh ∈ Xh ,
   
(k) (k)
bh uh , qh − ch ph , qh = kh (q), ∀qh ∈ Qh .

At each step k ⩾ 0, this algorithm involves a linear subproblem of Stokes-type.


Chapter 4. Discontinuous Galerkin methods 189

File 4.20: navier_stokes_taylor_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " taylor . h "
5 # include " stokes_dirichlet_dg . icc "
6 # include " inertia . h "
7 int main ( int argc , char ** argv ) {
8 environment rheolef ( argc , argv );
9 geo omega ( argv [1]);
10 space Xh ( omega , argv [2] , " vector " );
11 space Qh ( omega , argv [2]);
12 Float Re = ( argc > 3) ? atof ( argv [3]) : 1;
13 size_t max_iter = ( argc > 4) ? atoi ( argv [4]) : 1;
14 form a , b , c , mp ;
15 field lh , kh ;
16 s to k es_dirichlet_dg ( Xh , Qh , a , b , c , mp , lh , kh );
17 field uh ( Xh , 0) , ph ( Qh , 0);
18 problem_mixed stokes (a , b , c );
19 stokes . set_metric ( mp );
20 stokes . solve ( lh , kh , uh , ph );
21 trial u ( Xh ); test v ( Xh );
22 form a1 = a + Re * inertia ( uh , u , v );
23 lh += Re * inertia_fix_rhs ( v );
24 derr << " # k r as " << endl ;
25 for ( size_t k = 0; k < max_iter ; ++ k ) {
26 stokes = problem_mixed ( a1 , b , c );
27 stokes . set_metric ( mp );
28 stokes . solve ( lh , kh , uh , ph );
29 form th = inertia ( uh , u , v );
30 a1 = a + Re * th ;
31 field rh = a1 * uh + b . trans_mult ( ph ) - lh ;
32 derr << k << " " << rh . max_abs () << " " << th ( uh , uh ) << endl ;
33 }
34 dout << catchmark ( " Re " ) << Re << endl
35 << catchmark ( " u " ) << uh
36 << catchmark ( " p " ) << ph ;
37 }

Comments

The data are given when d = 2 by (4.18). This choice is convenient since the exact solution is known
u = g and p = −(Re/4)(cos(2πx0 ) + cos(2πx1 )). The code navier_stokes_taylor_error_dg.cc
compute the error in L2 , L∞ and energy norms. This code it is not listed here but is available in
the Rheolef example directory. The computation writes:

make navier_stokes_taylor_dg navier_stokes_taylor_error_dg


./navier_stokes_taylor_dg square P1d 10 10 | ./navier_stokes_taylor_error_dg
./navier_stokes_taylor_dg square P2d 10 10 | ./navier_stokes_taylor_error_dg

4.5.3 A conservative variant


Remark the identity
div(u ⊗ u) = (u.∇)u + div(u) u

The momentum conservation can be rewritten in conservative form and the problem writes:
(P̃ ): find u and p, defined in Ω, such that

div(Re u ⊗ u − 2D(u)) + ∇p = f in Ω,
− div u = 0 in Ω,
u = g on ∂Ω
190 Rheolef version 7.2

Note the Green formulae (see volume 1, appendix A.1.2, page 279):
Z Z Z
div(u ⊗ u).v dx = − (u ⊗ u) : ∇v dx + (u.n) (u.v) ds
Ω Ω ∂Ω

The variational formulation is:


(F
g V ): find u ∈ V(g) and p ∈ L2 (Ω) such that

Re t̃(u; u, v) + a(u, v) + b(v, p) = ˜l(v), ∀v ∈ V(0),


b(u, q) = 0, ∀q ∈ L2 (Ω)

where the forms t̃ and ˜lh are given by:


Z
t̃(w; u, v) = − (w ⊗ u) : ∇v dx

Z
˜l(v) = l(v) − Re (g.n) (g.v) ds
∂Ω

Note that the right-hand side ˜l contains an additional term that compensates those coming from
the integration by parts. Then, with v = u:
Z
t̃(w; u, u) = − (w ⊗ u) : ∇u dx
Z Ω Z
= div(w ⊗ u).u dx − (w ⊗ u) : (u ⊗ n) dx
ZΩ ∂Ω
Z
= (((u.∇)w).u + div(u) (u.w)) dx − (u.n) (u.w) dx
Ω ∂Ω

From an integration by part similar to (4.21):


Z Z Z Z
(u.∇w).u dx = − (u.∇u).w dx − div(u) (u.w) dx + (u.n) (u.w) ds
Ω Ω Ω ∂Ω

The term (u.∇w).u do not reapper after the integration by parts: instead, it appears (u.∇u).w.
Thus, the structure of the t̃ trilinear form do not permit a general skew-symmetry property as it
was the case for t. It requires the three arguments to be the same:
Z Z
2
(u.n) |u|2 dx

t̃(u; u, u) = ((u.∇)u).u + div(u) |u| dx −
Ω ∂Ω

Using (4.21) with w = u leads to:


Z Z Z
1 1
((u.∇)u).u dx = − div(u) |u|2 dx + (u.n) |u|2 ds (4.26)
Ω 2 Ω 2 ∂Ω

Then
Z Z
1 1
t̃(u; u, u) = div(u) |u|2 dx − (u.n) |u|2 ds
2 Ω 2 ∂Ω

When working with velocities that are not divergence-free, a possible modification of the trilinear
form t̃ is to consider
Z Z
∗ 1 1
t̃ (w; u, v) = t̃(w; u, v) − div(v) (u.w) dx + (v.n) (u.w) ds
2 Ω 2 ∂Ω
Z   Z
1 1
= − (w ⊗ u) : D(v) + div(v) (u.w) dx + (v.n) (u.w) ds
Ω 2 2 ∂Ω
Chapter 4. Discontinuous Galerkin methods 191

Then we have

t̃∗ (u; u, u) = 0, ∀u ∈ H 1 (Ω)d

The new variational formulation is:


(F
g V )∗ : find u ∈ V(g) and p̃ ∈ L2 (Ω) such that

Re t̃∗ (u; u, v) + a(u, v) + b(v, p̃) = ˜l(v), ∀v ∈ V(0),


b(u, q) = 0, ∀q ∈ L2 (Ω)

One can easily check that when (u, p̃) is a solution of (F gV )∗ , then (u, p) is a solution of (F
g V)
with p = p̃ + Re|u|/2. The apparition of the kinetic energy term Re|u|/2 in the modified pressure
field p̃ is due to the introduction of the div(v) (u.w) term in the trilinear form t̃∗ .
At the discrete level, let us define for all uh , vh , wh ∈ Xh :
Z  
∗ 1
t̃h (wh ; uh , vh ) = − (wh ⊗ uh ) : ∇h vh + divh (vh ) (uh .wh ) dx
Ω 2
Z
1
+ (vh .n) (uh .wh ) ds
2 ∂Ω

Note that t̃∗h is similar to t̃∗ : the gradient and divergence has been replaced by their broken
counterpart in the first term. As Xh ̸⊂ H 1 (Ω)d , the skew-symmetry property is not expected to
be true at the discrete level. Then
Z   Z
1 1
t̃∗h (uh ; uh , uh ) = − (uh ⊗ uh ) : ∇h uh + divh (uh ) |uh |2 dx + (uh .n) |uh |2 ds
Ω 2 2 ∂Ω
Next, using (4.26) in each K, and then developing thanks to (4.22)-(4.23), we get
Z Z
1 1 X
t̃∗h (uh ; uh , uh ) = (uh .n) |uh |2 ds − (uh .n) |uh |2 ds
2 ∂Ω 2 ∂K
K∈Th
Z
1 X
= − [[(uh .n) |uh |2 ]] ds
2 (i) S
S∈Sh
Z
1 X
({{uh }}.n) [[|uh |2 ]] + ([[uh ]].n) {{|uh |2 }} ds

= −
2 (i) S
S∈Sh
X Z  1

2
= − ({{uh }}.n) ({{uh }}.[[uh ]]) + ([[uh ]].n) {{|uh | }} ds
(i) S 2
S∈Sh

The idea is to integrate this term in the definition of a discrete t̃h . One of the possibilities is
X Z  1

t̃h (wh ; uh , vh ) = t̃∗h (wh ; uh , vh ) + ({{uh }}.n) ({{wh }}.[[vh ]]) + {{uh .wh }} ([[vh ]].n) ds
(i) S 2
S∈Sh
Z  
1
= − (wh ⊗ uh ) : ∇h vh + divh (vh ) (uh .wh ) dx
Ω 2
Z
1
+ (vh .n) (uh .wh ) ds
2 ∂Ω
X Z  1

+ ({{uh }}.n) ({{wh }}.[[vh ]]) + {{uh .wh }} ([[vh ]].n) ds (4.27)
(i) S 2
S∈Sh

This expression was proposed by di Pietro and Ern [2010, p. 21], eqn (73) (see also di Pietro and
Ern, 2012, p. 282) following and original idea introduced in Cockburn et al. [2005].
192 Rheolef version 7.2

File 4.21: inertia_cks.icc


1 form inertia ( field w , trial u , test v ,
2 integrate_option iopt = integrate_option ())
3 {
4 return
5 integrate ( - dot ( trans ( grad_h ( v ))* w , u ) - 0.5* div_h ( v )* dot (u , w ) , iopt )
6 + integrate ( " internal_sides " ,
7 dot ( average ( u ) , normal ())* dot ( jump ( v ) , average ( w ))
8 + 0.5* dot ( jump ( v ) , normal ())
9 *( dot ( average ( u ) , average ( w )) + 0.25* dot ( jump ( u ) , jump ( w ))) , iopt )
10 + integrate ( " boundary " , 0.5* dot (v , normal ())* dot (u , w ) , iopt );
11 }
12 field inertia_fix_rhs ( test v ,
13 integrate_option iopt = integrate_option ())
14 {
15 return integrate ( " boundary " , - dot ( g () , normal ())* dot ( g () , v ) , iopt );
16 }

The discrete problem is


(F
g V )h : find uh ∈ Xh and p̃ ∈ Qh such that

Re t̃h (uh ; uh , vh ) + ah (uh , vh ) + bh (vh , p̃h ) = ˜lh∗ (vh ), ∀vh ∈ Xh ,


bh (uh , qh ) − ch (ph , qh ) = kh (q), ∀qh ∈ Qh

A simple test program is obtained by replacing in navier_stokes_taylor_dg.cc the include


inertia.h by inertia_cks.icc. The compilation and run are similar.

4.5.4 Newton solver


The discrete problems (F V )h can be put in a compact form:

F (uh , ph ) = 0

where F is defined in variational form:


ah (uh , vh ) + bh (vh , ph ) − lh∗ (vh )
 
Re th (uh ; uh , vh ) +
⟨F (uh , ph ), (vh , qh )⟩ =
bh (uh , qh ) − ch (ph , qh ) − kh (q)

for all (vh , qh ) ∈ Xh × Qh . Note that, after some minor modifications in the definition of F ,
this method could also applies for the locally conservative formulation (F gV )h . The previous
formulation is simply
 the variational expression of F (u ,
h hp ) = 0. The Newton method defines
(k)
the sequence uh by recurrence as:
k⩾0

(0)
• k = 0: let uh ∈ Xh being known.
(k−1)
• k ⩾ 0: let uh ∈ Xh given. Find δuh ∈ Xh and δph ∈ Qh such that
   
(k−1) (k−1) (k−1) (k−1)
F ′ uh , ph .(δuh , δph ) = −F uh ,ph

and then defines


(k) (k−1) (k) (k−1)
uh = uh + δuh and ph = ph + δph

At each step k ⩾ 0, this algorithm involves a linear subproblem involving the Jacobean F ′ that is
defined by its variational form:
 
(k−1) (k−1)
⟨F ′ uh , ph .(δuh , δph ), (vh , qh )⟩
 
Re (th (δuh ; uh , vh ) + th (uh ; δuh , vh )) + ah (δuh , vh ) + bh (vh , δph )
=
bh (δuh , qh ) − ch (δph , qh )
Chapter 4. Discontinuous Galerkin methods 193

File 4.22: navier_stokes_taylor_newton_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " taylor . h "
5 # include " stokes_dirichlet_dg . icc "
6 # include " inertia . h "
7 # include " navier_stokes_dg . h "
8 int main ( int argc , char ** argv ) {
9 environment rheolef ( argc , argv );
10 Float eps = numeric_limits < Float >:: epsilon ();
11 geo omega ( argv [1]);
12 string approx = ( argc > 2) ? argv [2] : " P1d " ;
13 Float Re = ( argc > 3) ? atof ( argv [3]) : 100;
14 Float tol = ( argc > 4) ? atof ( argv [4]) : eps ;
15 size_t max_iter = ( argc > 5) ? atoi ( argv [5]) : 100;
16 string restart = ( argc > 6) ? argv [6] : " " ;
17 navier_stokes_dg F ( Re , omega , approx );
18 navier_stokes_dg :: value_type xh = F . initial ( restart );
19 int status = damped_newton (F , xh , tol , max_iter , & derr );
20 dout << catchmark ( " Re " ) << Re << endl
21 << catchmark ( " u " ) << xh [0]
22 << catchmark ( " p " ) << xh [1];
23 return status ;
24 }

Comments

The implementation of the Newton method follows the generic approach introduced in volume 1,
section 3.2.3, page 119. For that purpose we define a class navier_stokes_dg.

File 4.23: navier_stokes_dg.h


1 struct navier_stokes_dg {
2 typedef Float float_type ;
3 typedef Eigen :: Matrix < field ,2 ,1 > value_type ;
4 navier_stokes_dg ( Float Re , const geo & omega , string approx );
5 value_type initial ( string restart ) const ;
6 value_type residue ( const value_type & uh ) const ;
7 void update_derivative ( const value_type & uh ) const ;
8 value_type derivative_solve ( const value_type & mrh ) const ;
9 value_type der ivati ve_tra ns_mu lt ( const value_type & mrh ) const ;
10 Float space_norm ( const value_type & uh ) const ;
11 Float dual_space_norm ( const value_type & mrh ) const ;
12 Float Re ;
13 space Xh , Qh ;
14 integrate_option iopt ;
15 form a0 , b , c , mu , mp ;
16 field lh0 , lh , kh ;
17 problem pmu , pmp ;
18 mutable form a1 ;
19 mutable problem_mixed stokes1 ;
20 };
21 # include " navier_stokes_dg1 . icc "
22 # include " navier_stokes_dg2 . icc "

The member functions of the class are defined in two separate files.
194 Rheolef version 7.2

File 4.24: navier_stokes_dg1.icc


1 navier_stokes_dg :: navier_stokes_dg (
2 Float Re1 , const geo & omega , string approx )
3 : Re ( Re1 ) , Xh () , Qh () , iopt () , a0 () , b () , c () , mu () , mp () , lh0 () , lh () , kh () ,
4 pmu () , pmp () , a1 () , stokes1 ()
5 {
6 Xh = space ( omega , approx , " vector " );
7 Qh = space ( omega , approx );
8 iopt . set_family ( integrate_option :: gauss );
9 iopt . set_order (2* Xh . degree ()+1);
10 s to k es_dirichlet_dg ( Xh , Qh , a0 , b , c , mp , lh0 , kh , iopt );
11 trial u ( Xh ); test v ( Xh );
12 lh = lh0 + Re * inertia_fix_rhs (v , iopt );
13 mu = integrate ( dot (u , v ) , iopt );
14 pmu = problem ( mu );
15 pmp = problem ( mp );
16 }
17 navier_stokes_dg :: value_type
18 navier_stokes_dg :: initial ( string restart ) const {
19 value_type xh = { field ( Xh , 0) , field ( Qh , 0) };
20 Float Re0 = 0;
21 if ( restart == " " ) {
22 problem_mixed stokes0 ( a0 , b , c );
23 stokes0 . set_metric ( mp );
24 stokes0 . solve ( lh0 , kh , xh [0] , xh [1]);
25 } else {
26 idiststream in ( restart );
27 in >> catchmark ( " Re " ) >> Re0
28 >> catchmark ( " u " ) >> xh [0]
29 >> catchmark ( " p " ) >> xh [1];
30 check_macro ( xh [1]. get_space () == Qh , " unexpected "
31 << xh [0]. get_space (). name () << " approximation in file \" "
32 << restart << " \" ( " << Xh . name () << " expected ) " );
33 }
34 derr << " # continuation : from Re = " << Re0 << " to " << Re << endl ;
35 return xh ;
36 }
37 navier_stokes_dg :: value_type
38 navier_stokes_dg :: residue ( const value_type & xh ) const {
39 trial u ( Xh ); test v ( Xh );
40 form a = a0 + Re * inertia ( xh [0] , u , v , iopt );
41 value_type mrh = { a * xh [0] + b . trans_mult ( xh [1]) - lh ,
42 b * xh [0] - c * xh [1] - kh };
43 return mrh ;
44 }
45 void navier_stokes_dg :: update_derivative ( const value_type & xh ) const {
46 trial u ( Xh ); test v ( Xh );
47 a1 = a0 + Re *( inertia ( xh [0] , u , v , iopt ) + inertia (u , xh [0] , v , iopt ));
48 stokes1 = problem_mixed ( a1 , b , c );
49 stokes1 . set_metric ( mp );
50 }
51 navier_stokes_dg :: value_type
52 navier_stokes_dg :: derivative_solve ( const value_type & mrh ) const {
53 value_type delta_xh = { field ( Xh , 0) , field ( Qh , 0) };
54 stokes1 . solve ( mrh [0] , mrh [1] , delta_xh [0] , delta_xh [1]);
55 return delta_xh ;
56 }
57 navier_stokes_dg :: value_type
58 navier_stokes_dg :: deriva tive_ trans_ mult ( const value_type & mrh ) const {
59 value_type rh = { field ( Xh ) , field ( Qh ) };
60 pmu . solve ( mrh [0] , rh [0]);
61 pmp . solve ( mrh [1] , rh [1]);
62 value_type mgh = { a1 . trans_mult ( rh [0]) + b . trans_mult ( rh [1]) ,
63 b * rh [0] - c * rh [1] };
64 return mgh ;
65 }
Chapter 4. Discontinuous Galerkin methods 195

File 4.25: navier_stokes_dg2.icc


1 Float navier_stokes_dg :: space_norm ( const value_type & xh ) const {
2 return sqrt ( mu ( xh [0] , xh [0]) + mp ( xh [1] , xh [1]));
3 }
4 Float navier_stokes_dg :: dual_space_norm ( const value_type & mrh ) const {
5 value_type rh = { field ( Xh ,0) , field ( Qh ,0) };
6 pmu . solve ( mrh [0] , rh [0]);
7 pmp . solve ( mrh [1] , rh [1]);
8 return sqrt ( dual ( rh [0] , mrh [0]) + dual ( rh [1] , mrh [1]));
9 }

make navier_stokes_taylor_newton_dg navier_stokes_taylor_error_dg


./navier_stokes_taylor_newton_dg square P2d 1000 | ./navier_stokes_taylor_error_dg

4.5.5 Application to the driven cavity benchmark

File 4.26: cavity_dg.h


1 struct g {
2 point operator () ( const point & x ) const {
3 return point (( abs (1 - x [1]) < 1e -7) ? 1 : 0 , 0 , 0); }
4 };
5 struct f {
6 point operator () ( const point & x ) const { return point (0 ,0 ,0); }
7 };

The program navier_stokes_cavity_newton_dg.cc is obtained by replacing in


navier_stokes_taylor_newton_dg.cc the include taylor.h by cavity_dg.h that defines
the boundary conditions. The compilation and run are similar.

make navier_stokes_cavity_newton_dg streamf_cavity


./navier_stokes_cavity_newton_dg square P1d 500 > square.field
field square.field -proj -field | ./streamf_cavity | \
field -bw -n-iso-negative 10 -

(n) Re = 100
rh
L∞ 500
1000

100

10−5

h = 1/40, k = 1
10−10

0 5 10 15 20 25
n

Figure 4.16: The discontinuous Galerkin method for the Navier-Stokes problem on the driven
cavity benchmark when k = 1 and d = 2: convergence of the damped Newton algorithm.
196 Rheolef version 7.2

4.5.6 Upwinding
The skew symmetry property is generalized to the requirement that th be non-dissipative (see
di Pietro and Ern, 2012, p. 282, eqn (6.68)):

th (wh ; uh , uh ) ⩾ 0, ∀wh , uh ∈ Xh

A way to satisfy this property is to add an upwinding term in th :

t̆h (wh ; uh , vh ) := th (wh ; uh , vh ) + sh (wh ; uh , vh )

with Z
1 X
sh (wh ; uh , vh ) = |{{wh }}.n| ([[uh ]].[[vh ]]) ds
2 (i) S
S∈Sh

We aim at using a Newton method. We replace th by its extension t̆h containing the upwind
terms in the definition of F , and then we compute its Jacobean F ′ . As the absolute value is
not differentiable, the functions sh , t̆h and then F are also not differentiable with respect to wh .
Nevertheless, the absolute value is convex and we can use some concepts of the subdifferential
calculus. Let us introduce the multi-valued sign function:

when x > 0

 {1}
sgn(x) = [−1, 1] when x = 0
{−1} when x < 0

Then, the subdifferential of the absolute value function is sgn(x) and for all δwh , wh , uh , vh ∈ Xh ,
we define a generalization of the partial derivative as
Z
∂sh 1 X
(wh ; uh , vh ).(δwh ) = sgn({{wh }}.n) ({{δwh }}.n) ([[uh ]].[[vh ]]) ds
∂wh 2 (i) S
S∈Sh

File 4.27: inertia_upw.icc


1 # include " sgn . icc "
2 form inertia_upw ( field w , trial u , test v ,
3 integrate_option iopt = integrate_option ())
4 {
5 return integrate ( " internal_sides " ,
6 0.5* abs ( dot ( average ( w ) , normal ()))* dot ( jump ( u ) , jump ( v )));
7 }
8 form d_inertia_upw ( field w , trial dw , field u , test v ,
9 integrate_option iopt = integrate_option ())
10 {
11 return integrate ( " internal_sides " ,
12 0.5* compose ( sgn , dot ( average ( w ) , normal ()))
13 * dot ( average ( dw ) , normal ())* dot ( jump ( u ) , jump ( v )));
14 }

A multi-valued Jacobean F ′ is then defined:


 
(k−1) (k−1)
⟨F ′ uh , ph .(δuh , δph ), (vh , qh )⟩
 ∂sh 
th (δuh ; uh , vh ) + th (uh ; δuh , vh ) + (uh ; uh , vh ).(δuh ) + sh (uh ; δuh , vh )
= Re 
 ∂wh 

0
 
ah (δuh , vh ) + bh (vh , δph )
+  
bh (δuh , qh ) − ch (δph , qh )
Chapter 4. Discontinuous Galerkin methods 197

We are able to extend the Newton method to the F function that allows a multi-valued subdiffer-
ential F ′ . During iterations, we can choose any of the available directions in the subdifferential.
One the possibilities is then to replace the multi-valued sign function by a single-value one:

1 when x ⩾ 0

sg
gn(x) =
−1 when x < 0

File 4.28: sgn.icc


1 Float sgn ( Float x ) { return ( x >= 0) ? 1 : -1; }

File 4.29: navier_stokes_upw_dg.h


1 # include " navier_stokes_dg . h "
2 struct navier_stokes_upw_dg : navier_stokes_dg {
3 typedef Float float_type ;
4 typedef navier_stokes_dg :: value_type value_type ;
5 n a v i er_stokes_upw_dg ( Float Re , const geo & omega , string approx );
6 value_type residue ( const value_type & uh ) const ;
7 void update_derivative ( const value_type & uh ) const ;
8 };
9 # include " navier_stokes_upw_dg . icc "

File 4.30: navier_stokes_upw_dg.icc


1 # include " inertia_upw . icc "
2 n a v i e r _stokes_upw_dg :: navier_stokes_upw_dg (
3 Float Re1 , const geo & omega , string approx )
4 : navier_stokes_dg ( Re1 , omega , approx ) {}
5
6 n a v i e r _stokes_upw_dg :: value_type
7 n a v i e r _stokes_upw_dg :: residue ( const value_type & xh ) const {
8 trial u ( Xh ); test v ( Xh );
9 form a = a0 + Re *( inertia ( xh [0] , u , v , iopt )
10 + inertia_upw ( xh [0] , u , v , iopt ));
11 value_type mrh (2);
12 mrh [0] = a * xh [0] + b . trans_mult ( xh [1]) - lh ;
13 mrh [1] = b * xh [0] - c * xh [1] - kh ;
14 return mrh ;
15 }
16 void n avier_stokes_upw_dg :: update_derivative ( const value_type & xh ) const {
17 trial du ( Xh ); test v ( Xh );
18 a1 = a0 + Re *( inertia ( xh [0] , du , v , iopt )
19 + inertia_upw ( xh [0] , du , v , iopt )
20 + inertia ( du , xh [0] , v , iopt )
21 + d_inertia_upw ( xh [0] , du , xh [0] , v , iopt ));
22 stokes1 = problem_mixed ( a1 , b , c );
23 stokes1 . set_metric ( mp );
24 }

The program navier_stokes_cavity_newton_upw_dg.cc is obtained by replacing in


navier_stokes_taylor_newton_dg.cc the string navier_stokes_dg by navier_stokes_upw_dg
(two occurrences: in the includes and then in the definition of F). Also replace the include taylor.h
by cavity_dg.h that defines the boundary conditions. The compilation and run are similar.

make navier_stokes_cavity_newton_upw_dg streamf_cavity


mkgeo_grid -t 80 > square.geo
./navier_stokes_cavity_newton_upw_dg square P1d 500 1e-15 100 > square-500.field
field square-500.field -proj -field | ./streamf_cavity | \
field -bw -n-iso 30 -n-iso-negative 20 -

It could take about one minute of CPU, depending on your computer. Computations for higher
Reynolds numbers are performed by continuation. Starting from a previous computation at Re =
500, we compute it at Re = 1000 as:
198 Rheolef version 7.2

./navier_stokes_cavity_newton_upw_dg square P1d 1000 1e-15 100 square-500.field \


> square-1000.field
field square-1000.field -proj -field | ./streamf_cavity | \
field -bw -n-iso 30 -n-iso-negative 20 -

Then, for Re = 1500:

./navier_stokes_cavity_newton_upw_dg square P1d 1500 1e-15 100 square-1000.field \


> square-1500.field
field square-1500.field -proj -field | ./streamf_cavity | \
field -bw -n-iso 30 -n-iso-negative 20 -

By this way, computations of solutions can be performed until Re = 25 000 without problems.
Note that, from Re ≈ 10 000, these solution are no more stable with respect to time, but are
valid solution of the stationary Navier-Stokes problem and can be interpreted as timed averaged
solutions. See also at the end of section 2.5 page 84 for a discussion about the loss of stationary of
the solution. In conclusion, the discontinuous Galerkin method is much more robust and accurate
than the characteristic method of section 2.5 when looking at stationary solutions of the Navier-
Stokes equations. Moreover, this approach could also be easily exended to the non-stationary case
by adding a time derivative term and a BDF time approximation, associated to a fully implicit
approach.
Chapter 4. Discontinuous Galerkin methods 199

Re = 0 Re = 400

Re = 1000 Re = 2000

Figure 4.17: The discontinuous Galerkin method for the Navier-Stokes problem on the driven
cavity benchmark when k = 1 (80 × 80 grid): stream function isovalues for various Re.
200 Rheolef version 7.2

Re = 3200 Re = 5000

Re = 7500 Re = 10000

Figure 4.18: The discontinuous Galerkin method for the Navier-Stokes problem on the driven
cavity benchmark when k = 1 (80 × 80 grid): stream function isovalues for various Re (cont.).
Chapter 4. Discontinuous Galerkin methods 201

Re = 15000 Re = 20000

Re = 25000

Figure 4.19: The discontinuous Galerkin method for the Navier-Stokes problem on the driven
cavity benchmark when k = 1 (80 × 80 grid): stream function isovalues for various Re (cont.).
202 Rheolef version 7.2
Chapter 5

Complex fluids

This part presents in details the practical computational aspects of numerical modeling with
complex fluids. Most of the examples involve only few lines of code: the concision and readability
of codes written with Rheolef is certainly a major key-point of this environment. The theoretical
background for complex fluids an associated numerical methods can be found in the textbook by
the present author [Saramito, 2016b]. We start with yield slip boundary condition as a preliminary
problem. Slip at the wall occurs in many applications with complex fluids. This problem is solved
both by augmented Lagrangian and Newton methods. Then, viscoplastic fluids are introduced
and an augmented Lagrangian method is presented. A prelinary for viscoelastic fluids problems
is the linear tensor transport equation: it is solved by a discontinuous Galerkin method. Finally,
viscoelastic fluids problems are solved by an operator splitting algorithm, the θ-scheme.

5.1 Yield slip at the wall

5.1.1 Problem statement

The problem of a Newtonian fluid with yield slip at the wall and flowing in a pipe [Roquet and
Saramito, 2004] writes:
(P ): find u, defined in Ω, such that

−∆u = f in Ω (5.1a)

∂u
⩽S when u = 0 


∂n on ∂Ω (5.1b)
∂u u
= −Cf |u|−1+n u − S otherwise


∂n |u|

Here, S ⩾ 0 and Cf > 0 are respectively the yield slip and the friction coefficient while n > 0 is
a power-law index. The computational domain Ω represents the cross-section of the pipe and u
is the velocity component along the axis of the pipe. The right-hand side f is a given constant,
and without loss of generality, we can suppose f = 1 : the parameters are S, Cf and n. When
S = 0 and n = 1, the problem reduces to a Poisson problem with homogeneous Robin boundary
condition that depend upon Cf .

203
204 Rheolef version 7.2

5.1.2 The augmented Lagrangian algorithm


Principe of the algorithm

The problem writes as a minimization one:


min J(u)
u∈H 1 (Ω)

where Z Z Z Z
Cf 1
J(u) = |u|1+n ds + S |u| ds + |∇u|2 dx − f u dx
1 + n ∂Ω ∂Ω 2 Ω Ω
This problem is solved by using an augmented Lagrangian algorithm. The auxiliary variable
γ = u|∂Ω is introduced together with the Lagrangian multiplier λ associated to the constraints
u|∂Ω − γ = 0. For all r > 0, let:
Z Z Z Z
Cf 1
L((u, γ); λ) = |γ|1+n ds + S |γ| ds + |∇u|2 dx − f u dx
1 + n ∂Ω 2 Ω
Z Z ∂Ω Ω
r
+ λ (u − γ) ds + |u − γ|2 ds
∂Ω 2 ∂Ω
An Uzawa-like minimization algorithm writes:

• k = 0: let λ(0) and γ (0) arbitrarily chosen.


• k ⩾ 0: let λ(k) and γ (k) being known.
u(k+1) := arg min L((v, γ (k) ); λ(k) )
v∈H 1 (Ω)

γ (k+1) := arg min L((u(k+1) , δ); λ(k) )


δ∈L∞ (∂Ω)
 
λ(k+1) := λ(k) + ρ u(k+1) − γ (k+1) on ∂Ω

The descent step ρ is chosen as ρ = r for sufficiently large r. The Lagrangian L is quadratic in
u and thus the computation of u(k+1) reduces to a linear problem. The non-linearity is treated
when computing γ (k+1) . This operation is performed point-by-point on ∂Ω by minimizing:
Cf |δ|1+n r|δ|2
γ := arg min + + S|δ| − ξ δ
δ∈R 1+n 2
where ξ = λ + r u
(k) (k+1)
is given. This problem is convex and its solution is unique. The solution
has the form:
when |ξ| ⩽ S

0
(5.2)
def
γ = Pn,r (ξ) =
ϕn,r (|ξ| − S) sgn(ξ) otherwise
where ϕn,r (x) = fn,r
−1
(x) and fn,r is defined for all y > 0 by:
fn,r (y) = Cf y n + r y (5.3)

File 5.1: projection.h


1 # include " phi . h "
2 // p ( x ) = phi (| x | - a )* sgn ( x )
3 struct projection {
4 Float operator () ( const Float & x ) const {
5 if ( fabs ( x ) <= a ) return 0;
6 return ( x > 0) ? _phi (x - a ) : - _phi ( -x - a );
7 }
8 projection ( Float a1 , Float n =1 , Float c =1 , Float r =0)
9 : a ( a1 ) , _phi (n ,c , r ) {}
10 Float a ;
11 phi _phi ;
12 };
Chapter 5. Complex fluids 205

Observe that fn,r



(y) = nCf y −1+n + r > 0 when r > 0: the function is strictly increasing and is
thus invertible. When n = 1 then fn,r is linear and ϕ1,r (x) = x/(Cf + r). When n = 1/2 this
problem reduces to a second order polynomial equation and the solution is also explicit:
( )1/2 2
1 4r x
ϕ 21 ,r (x) = 1+ 2 − 1
4 Cf

1
When r = 0, for any n > 0 the solution is ϕn,0 (x) = (x/Cf ) n . In general, when n > 0 and r > 0,
the solution is no more explicit. We consider the Newton method:

• i = 0: Let y0 being given.

• i ⩾ 0: Suppose yi being known and compute yi+1 = yi − (fn,r (yi ) − x)/fn,r



(yi ).

File 5.2: phi.h


1 struct phi {
2 phi ( Float n1 =2 , Float c1 =1 , Float r1 =0) : n ( n1 ) , c ( c1 ) , r ( r1 ) {}
3 Float operator () ( const Float & x ) const {
4 if ( x <= 0) return 0;
5 if ( n == 1) return x /( c + r );
6 if ( r == 0) return pow ( x /c ,1/ n );
7 Float y = x /( c + r );
8 const Float tol = numeric_limits < Float >:: epsilon ();
9 for ( size_t i = 0; true ; ++ i ) {
10 Float ry = f ( y ) - x ;
11 Float dy = - ry / df_dy ( y );
12 if ( fabs ( ry ) <= tol && fabs ( dy ) <= tol ) break ;
13 if ( i >= max_iter ) break ;
14 if ( y + dy > 0) {
15 y += dy ;
16 } else {
17 y /= 2;
18 check_macro (1+ y != y , " phi : machine precision problem " );
19 }
20 }
21 return y ;
22 }
23 Float derivative ( const Float & x ) const {
24 Float phi_x = operator ()( x );
25 return 1/( r + n * c * pow ( phi_x , -1+ n ));
26 }
27 protected :
28 Float f ( Float y ) const { return c * pow (y , n ) + r * y ; }
29 Float df_dy ( Float y ) const { return n * c * pow (y , -1+ n ) + r ; }
30 Float n ,c , r ;
31 static const size_t max_iter = 100;
32 };

In the present implementation, in order to avoid too large steps, the Newton step is damped when
yi+1 becomes negative.
The Uzawa algorithm writes:

• k = 0: let λ(0) and γ (0) arbitrarily chosen.

• k ⩾ 0: let λ(k) and γ (k) being known, find u(k+1) , defined in Ω, such that

−∆u(k+1) = f in Ω
(k+1)
∂u
+ r u(k+1) = r γ (k) − λ(k) on ∂Ω
∂n
206 Rheolef version 7.2

and then compute explicitely γ (k+1) and λ(k+1) :


 
γ (k+1) := Pn,r λ(k) + r u(k+1) on ∂Ω
 
λ(k+1) := λ(k) + r u(k+1) − γ (k+1) on ∂Ω

This algorithm reduces the nonlinear problem to a sequence of linear and completely standard
Poisson problems with a Robin boundary condition and some explicit computations. At conver-
∂u
gence, λ = − and γ = u on ∂Ω.
∂n
Note that the solution satisfies the following variational formulation:
Z Z Z
∇u .∇v dx + v λ ds = f v dx, ∀v ∈ H 1 (Ω)
Ω ∂Ω Ω
Z Z
u γ ds − Pn,0 (λ) γ ds = 0, ∀γ ∈ L∞ (∂Ω)
∂Ω ∂Ω

This formulation is the base of the computation of the residual test used for the stopping criteria.

File 5.3: yield_slip_augmented_lagrangian.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 # include " y i e l d _ s l i p _ a u g m e n t e d _ l a g r a n g i a n . icc "
5 # include " poisson_robin . icc "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 dlog << noverbose ;
9 geo omega ( argv [1]);
10 string approx = ( argc > 2) ? argv [2] : " P1 " ;
11 Float S = ( argc > 3) ? atof ( argv [3]) : 0.6;
12 Float n = ( argc > 4) ? atof ( argv [4]) : 1;
13 Float Cf = ( argc > 5) ? atof ( argv [5]) : 1;
14 Float r = ( argc > 6) ? atof ( argv [6]) : 1;
15 Float tol = 1 e3 * numeric_limits < Float >:: epsilon ();
16 size_t max_iter = 100000;
17 space Xh ( omega , approx );
18 test v ( Xh );
19 field lh = integrate ( v );
20 field uh = poisson_robin ( Cf , omega [ " boundary " ] , lh );
21 space Wh ( omega [ " boundary " ] , Xh . get_approx ());
22 field lambda_h = Cf * uh [ " boundary " ];
23 int status = y i e l d _ s l i p _ a u g m e n t e d _ l a g r a n g i a n (S , n , Cf , omega [ " boundary " ] ,
24 lh , lambda_h , uh , tol , max_iter , r );
25 dout << setprecision ( numeric_limits < Float >:: digits10 )
26 << catchmark ( " S " ) << S << endl
27 << catchmark ( " n " ) << n << endl
28 << catchmark ( " Cf " ) << Cf << endl
29 << catchmark ( " r " ) << r << endl
30 << catchmark ( " u " ) << uh
31 << catchmark ( " lambda " ) << lambda_h ;
32 return status ;
33 }

File 5.4: poisson_robin.icc


1 field poisson_robin ( Float Cf , const geo & boundary , const field & lh ) {
2 const space & Xh = lh . get_space ();
3 trial u ( Xh ); test v ( Xh );
4 form a = integrate ( dot ( grad ( u ) , grad ( v ))) + Cf * integrate ( boundary , u * v );
5 field uh ( Xh );
6 problem p ( a );
7 p . solve ( lh , uh );
8 return uh ;
9 }
Chapter 5. Complex fluids 207

File 5.5: yield_slip_augmented_lagrangian.icc


1 # include " projection . h "
2 int y i e l d _ s l i p _ a u g m e n t e d _ l a g r a n g i a n ( Float S , Float n , Float Cf ,
3 geo boundary , field lh , field & lambda_h , field & uh ,
4 Float tol , size_t max_iter , Float r )
5 {
6 const space & Xh = uh . get_space ();
7 const space & Wh = lambda_h . get_space ();
8 trial u ( Xh ) , lambda ( Wh );
9 test v ( Xh ) , mu ( Wh );
10 form m = integrate ( u * v ) ,
11 a0 = integrate ( dot ( grad ( u ) , grad ( v ))) ,
12 a = a0 + integrate ( boundary , r * u * v ) ,
13 mb = integrate ( lambda * mu ) ,
14 b = integrate ( boundary , u * mu );
15 problem pa ( a );
16 derr << " # k residue " << endl ;
17 Float residue0 = 0;
18 for ( size_t k = 0; true ; ++ k ) {
19 field gamma_h = lazy_interpolate ( Wh ,
20 compose ( projection (S ,n , Cf , r ) , lambda_h + r * uh [ " boundary " ]));
21 field delta_lambda_h = r *( uh [ " boundary " ] - gamma_h );
22 lambda_h += delta_lambda_h ;
23 Float residue = delta_lambda_h . max_abs ();
24 derr << k << " " << residue << endl ;
25 if ( residue <= tol || k >= max_iter ) return ( residue <= tol ) ? 0 : 1;
26 field rhs = lh + b . trans_mult ( r * gamma_h - lambda_h );
27 pa . solve ( rhs , uh );
28 }
29 }

Observe also that the stopping criterion for breaking the loop bases on the max of the relative
error for the λh variable. For this algorithm, this stopping criterion guaranties that all residual
terms of the initial problem are also converging to zero, as it will be checked here. Moreover, this
stopping criterion is very fast to compute while the full set of residual terms of the initial problem
would take more computational time inside the loop.

Running the program

Figure 5.1: The yield slip problem for S = 0.6 and n = 1.

Assume that the previous code is contained in the file ‘yield-slip-augmented-lagrangian.cc’.


Compile and run the program as usual:
208 Rheolef version 7.2

make yield_slip_augmented_lagrangian
mkgeo_grid -a -1 -b 1 -c -1 -d 1 -t 50 > square.geo
./yield_slip_augmented_lagrangian square.geo P1 0.6 1 > square.field

Also you can replace P1 by P2. The solution can be represented in elevation view (see Fig. 5.1):

field square.field -elevation -stereo

As analysed by [Roquet and Saramito, 2004], when S ⩽ 0.382, the fluid slips at the wall, when
0.382 < S < 0.674, the fluid partially sticks and when S ⩾ 0.674 the fluid completely sticks.
Remark that the velocity is not zero along the boundary: there is a stick-slip transition point.
The velocity along along the 0x0 axis and the top boundary are available as (see Fig. 5.2):

0.4 0.04 1
u(x0 , 0) n = 1.5 u(x0 , 1) n = 1.5 ∂u
n = 1.0 n = 1.0 λ(x0 , 1) = − (x0 , 1)
n = 0.5 n = 0.5 ∂x1
0.3 0.03
0.6

0.2 0.02

0.1 0.01 n = 1.5


n = 1.0
n = 0.5
0
0 0
0 1 0 0.5 1 0 0.5 1
x0 x0 x0

Figure 5.2: The yield slip problem for S = 0.6: cut of the velocity (left) along the 0x0 axis ;
(center) along the boundary ; (right) cut of the normal stress λ along the boundary.

field square.field -normal 0 1 -origin 0 0 -cut -gnuplot


field square.field -domain top -elevation -gnuplot

The corresponding Lagrange multiplier λ on the boundary can also be viewed as:

field square.field -mark lambda -elevation

The file ‘yield_slip_residue.cc’ implement the computation of the full set of residual terms of
the initial problem. This file it is not listed here but is available in the Rheolef example directory.
The computation of residual terms is obtained by:

make yield_slip_residue
./yield_slip_residue < square.field

Observe that the residual terms of the initial problem are of about 10−10 , as required by the
stopping criterion. Fig. 5.3 plots the max of the relative error for the λh variable: this quantity
is used as stopping criterion. Observe that it behaves asymptotically as 1/k for larges meshes,
with a final acceleration to machine precision. Note that these convergence properties could be
dramatically improved by using a Newton method, as shown in the next section.
Chapter 5. Complex fluids 209

residue h = 1/10
1/20
1 1/30
1/40
1/50

−1

10−5

10−10
1 102 104
iteration k

Figure 5.3: The convergence of the augmented Lagrangian algorithm for the yield slip problem
with S = 0.6 and n = 1 and P1 polynomial approximation.
210 Rheolef version 7.2

5.1.3 Newton algorithm


Reformulation of the problem

The idea of this algorithm first proposed by Alart [1997] in the context of contact and friction
problems. At convergence, the augmented Lagrangian method solve the following problem:
(P )r : find u, defined in Ω, and λ, defined on ∂Ω, such that

−∆u = f in Ω
∂u
+ λ = 0 on ∂Ω
∂n
u − Pn,r (λ + r u) = 0 on ∂Ω

The solution is independent upon r ∈ R and this problem is equivalent to the original one. In order
to diagonalize the non-linearity in Pn,r (.), let us introduce β = λ + r u. The problem becomes:
(P )r : find u, defined in Ω, and β, defined on ∂Ω, such that

−∆u = f in Ω
∂u
− r u + β = 0 on ∂Ω
∂n
u − Pn,r (β) = 0 on ∂Ω

Variational formulation

Consider the following forms:


Z
m(u, v) = u v dx, , ∀u v ∈ L2 (Ω)
ZΩ Z
a(u, v) = ∇u .∇v dx − r u v ds, ∀u, v ∈ H 1 (Ω)
Ω ∂Ω
Z
b(v, γ) = v γ ds, ∀γ ∈ L2 (∂Ω), ∀v ∈ H 1 (Ω)

Z
c(β, γ) = Pn,r (β) γ ds, ∀β ∈ L∞ (∂Ω), ∀γ ∈ L2 (∂Ω)

Remark that, since Ω ⊂ R2 , from the Sobolev embedding theorem, if u ∈ H 1 (Ω) then u|∂Ω ∈
L∞ (∂Ω). Then, all integrals have sense. The variational formulation writes:
(F V ): find u ∈ H 1 (Ω) and β ∈ L∞ (∂Ω) such that

a(u, v) + b(v, β) = m(f, v), ∀v ∈ H 1 (Ω)


b(u, γ) − c(β, γ) = 0, ∀γ ∈ L2 (∂Ω)

Let M , A and B the operators associated to forms m, a, b and c. The problem writes also as:

A B∗
    
u Mf
=
B −C β 0

where B ∗ denotes the formal adjoint of B. The bilinear form a is symmetric positive definite
when r ∈]0, Cf [. Then A is non-singular and let A−1 denotes its inverse. The unknown u can be
eliminated: u = A−1 (M f − B T β) and the problem reduces to:

find β ∈ L∞ (∂Ω) such that F (β) = 0

where
F (β) = C(β) + BA−1 B ∗ β − BA−1 M f
Chapter 5. Complex fluids 211

This problem is a good candidate for a Newton method:

F ′ (β)δβ = C ′ (β)δβ + BA−1 B ∗ δβ

where C ′ (β) is associated to the bilinear form:

Z

c1 (β; γ, δ) = Pn,r (β) γ δ ds, ∀β ∈ L∞ (∂Ω), ∀γ, δ ∈ L2 (∂Ω)

where, for all ξ ∈ R:

when |ξ| ⩽ S

0

Pn,r (ξ) = (5.4)
ϕ′n,r (|ξ| − S) otherwise

Recall that, for all ζ > 0, ϕn,r (ζ) = fn,r


−1
(ζ) where fn,r (y) = Cf y n + r y, for all y > 0. Then

1 1 1
ϕ′n,r (ζ) = ′ (f −1 (ζ))
= ′ = −1+n (5.5)
fn,r n,r f n,r (ϕ n,r (ζ)) r + n Cf {ϕn,r (ζ)}

1
When n = 1 we simply have: ϕ′1,r (ζ) = . When r = 0, for any n > 0 we have
Cf + r
ζ −1+1/n
ϕ′n,0 (ζ) = 1/n
.
n Cf

File 5.6: d_projection_dx.h


1 # include " projection . h "
2 struct d_projection_dx {
3 Float operator () ( const Float & x ) const {
4 if ( fabs ( x ) <= a ) return 0;
5 if ( n == 1) return 1/( c + r );
6 if ( r == 0) return pow ( fabs ( x ) -a , -1+1/ n )/( n * pow (c ,1/ n ));
7 return 1/( r + n * c * pow ( _phi ( fabs ( x ) - a ) , -1+ n ));
8 }
9 d_projection_dx ( Float a1 , Float n1 =1 , Float c1 =1 , Float r1 =0)
10 : a ( a1 ) , n ( n1 ) , c ( c1 ) , r ( r1 ) , _phi ( n1 , c1 , r1 ) {}
11 Float a ,n ,c , r ;
12 phi _phi ;
13 };
212 Rheolef version 7.2

File 5.7: yield_slip_damped_newton.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 # include " yield_slip . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 string approx = ( argc > 2) ? argv [2] : " P1 " ;
9 Float S = ( argc > 3) ? atof ( argv [3]) : 0.6;
10 Float n = ( argc > 4) ? atof ( argv [4]) : 1;
11 Float Cf = ( argc > 5) ? atof ( argv [5]) : 1;
12 Float r = ( argc > 6) ? atof ( argv [6]) : 1;
13 domain boundary = omega [ " boundary " ];
14 yield_slip F (S , n , Cf , r , omega , boundary , approx );
15 field beta_h = F . initial ();
16 Float tol = 10* numeric_limits < Float >:: epsilon ();
17 size_t max_iter = 10000;
18 int status = damped_newton (F , beta_h , tol , max_iter , & derr );
19 field uh , lambda_h ;
20 F . post ( beta_h , uh , lambda_h );
21 dout << setprecision ( numeric_limits < Float >:: digits10 )
22 << catchmark ( " S " ) << S << endl
23 << catchmark ( " n " ) << n << endl
24 << catchmark ( " Cf " ) << Cf << endl
25 << catchmark ( " r " ) << r << endl
26 << catchmark ( " u " ) << uh
27 << catchmark ( " lambda " ) << lambda_h ;
28 return status ;
29 }

File 5.8: yield_slip.h


1 class yield_slip {
2 public :
3 typedef field value_type ;
4 typedef Float float_type ;
5 yield_slip ( Float S , Float n , Float Cf , Float r ,
6 const geo & omega , const geo & boundary , string approx = " P1 " );
7 field residue ( const field & beta_h ) const ;
8 void update_derivative ( const field & beta_h ) const ;
9 field derivative_solve ( const field & mrh ) const ;
10 field d erivat ive_t rans_m ult ( const field & mrh ) const ;
11 Float space_norm ( const field &) const ;
12 Float dual_space_norm ( const field &) const ;
13 field initial () const ;
14 void post ( const field & beta_h , field & uh , field & lambda_h ) const ;
15 protected :
16 Float S , n , Cf , r ;
17 geo boundary ;
18 space Xh , Wh , Yh ;
19 field lh , mkh ;
20 form m , mb , a , b ;
21 mutable form c1 ;
22 problem pmb , pa ;
23 mutable problem pA ;
24 };
25 # include " yield_slip1 . icc "
26 # include " yield_slip2 . icc "
Chapter 5. Complex fluids 213

File 5.9: yield_slip1.icc


1 # include " d_projection_dx . h "
2 yield_slip :: yield_slip ( Float S1 , Float n1 , Float Cf1 , Float r1 ,
3 const geo & omega , const geo & boundary1 , string approx )
4 : S ( S1 ) , n ( n1 ) , Cf ( Cf1 ) , r ( r1 ) , boundary ( boundary1 ) , Xh () , Wh () , Yh () ,
5 lh () , mkh () , m () , mb () , a () , b () , c1 () , pmb () , pa () , pA ()
6 {
7 Xh = space ( omega , approx );
8 Wh = space ( boundary , approx );
9 Yh = Xh * Wh ;
10 trial u ( Xh ) , lambda ( Wh );
11 test v ( Xh ) , mu ( Wh );
12 m = integrate ( u * v );
13 mb = integrate ( lambda * mu );
14 a = integrate ( dot ( grad ( u ) , grad ( v ))) - r * integrate ( boundary , u * v );
15 b = integrate ( boundary , u * mu );
16 lh = integrate ( v );
17 pmb = problem ( mb );
18 pa = problem ( a );
19 field vh ( Xh );
20 pa . solve ( lh , vh );
21 mkh = b * vh ;
22 }
23 field yield_slip :: residue ( const field & beta_h ) const {
24 field vh ( Xh );
25 field rhs = b . trans_mult ( beta_h );
26 pa . solve ( rhs , vh );
27 test mu ( Wh );
28 field c0h = integrate ( mu * compose ( projection (S ,n , Cf , r ) , beta_h ));
29 field mrh = b * vh + c0h - mkh ;
30 return mrh ;
31 }
32 void yield_slip :: update_derivative ( const field & beta_h ) const {
33 trial lambda ( Wh ); test mu ( Wh );
34 c1 = integrate ( lambda * mu * compose ( d_projection_dx (S ,n , Cf , r ) , beta_h ));
35 form A = { { a , trans ( b ) } ,
36 { b, - c1 } };
37 A . set_symmetry ( c1 . is_symmetric ());
38 pA = problem ( A );
39 }
214 Rheolef version 7.2

File 5.10: yield_slip2.icc


1 # include " poisson_robin . icc "
2 field yield_slip :: derivative_solve ( const field & mrh ) const {
3 field mryh ( Yh , 0.);
4 mryh [1] = - mrh ;
5 field delta_yh ( Yh );
6 pA . solve ( mryh , delta_yh );
7 return delta_yh [1];
8 }
9 field yield_slip :: deri vative _trans _mult ( const field & mrh ) const {
10 field rh ( Wh );
11 pmb . solve ( mrh , rh );
12 field rhs = b . trans_mult ( rh );
13 field delta_vh ( Xh , 0.);
14 pa . solve ( rhs , delta_vh );
15 field mgh = b * delta_vh + c1 * rh ;
16 field gh ( Wh );
17 pmb . solve ( mgh , gh );
18 return gh ;
19 }
20 Float yield_slip :: space_norm ( const field & rh ) const {
21 return sqrt ( mb ( rh , rh ));
22 }
23 Float yield_slip :: dual_space_norm ( const field & mrh ) const {
24 field rh ( Wh ,0);
25 pmb . solve ( mrh , rh );
26 return sqrt ( dual ( mrh , rh ));
27 }
28 field yield_slip :: initial () const {
29 field uh = poisson_robin ( Cf , boundary , lh );
30 return ( Cf + r )* uh [ " boundary " ];
31 }
32 void yield_slip :: post ( const field & beta_h , field & uh , field & lambda_h ) const {
33 field rhs = lh - b . trans_mult ( beta_h );
34 uh = field ( Xh , 0.);
35 pa . solve ( rhs , uh );
36 lambda_h = beta_h - r * uh [ " boundary " ];
37 }

Running the program

make ./yield_slip_damped_newton
./yield_slip_damped_newton square.geo P1 0.6 1 > square.field
field square.field -elevation -stereo
field square.field -mark lambda -elevation

Observe on Fig. 5.4.a and 5.4.b that the convergence is super-linear and mesh-independent when
n = 1/2 and n = 0.9. For mesh-independent convergence of the Newton method, see e.g. the
p-Laplacian example, Fig. 3.15, page 129. When n = 0.9, observe that the convergence depends
slightly upon the mesh for rough meshes while it becomes asymptotically mesh independent for
fine meshes. When n ⩾ 1, the convergence starts to depend also upon the mesh (Fig. 5.4.bottom-
left and 5.4.bottom-right). Recall that when n ⩾ 1, the problem becomes non-differentiable and
the convergence of the Newton method is no more assured. Nevertheless, in that case, the con-
vergence is clearly faster (about 100 times faster) than the corresponding one with the augmented
Lagrangian algorithm on the same problem (see Fig. 5.3, page 209) and moreover there is no
saturation of the residual terms on large meshes.

5.1.4 Error analysis


Assume that the previous code is contained in the file ‘yield-slip-augmented-lagrangian.cc’.
When Ω is the unit circle, the exact solution is known. In polar coordinates (r, θ), as the solution
Chapter 5. Complex fluids 215

100 10 × 10 100 10 × 10
20 × 20 20 × 20
30 × 30 30 × 30
40 × 40 40 × 40
50 × 50 50 × 50
10−5 10−5

∥rh ∥L2 (∂Ω) ∥rh ∥L2 (∂Ω)


10−10 10−10

10−15 10−15
0 2 4 6 8 10 0 2 4 6 8 10
n n
100 10 × 10 100 10 × 10
20 × 20 20 × 20
30 × 30 30 × 30
40 × 40 40 × 40
50 × 50 50 × 50
10−5 10−5

∥rh ∥L2 (∂Ω) ∥rh ∥L2 (∂Ω)


10−10 10−10

10−15 10−15
0 2 4 6 8 10 0 2 4 6 8 10 12 14 16 18 20
n n

Figure 5.4: The convergence of the damped Newton algorithm for the yield slip problem (S = 0.6):
(top-left) n = 0.5; (top-right) n = 0.9; (bottom-left) n = 1; (bottom-right) n = 1.5.

u depends only of r, equation (5.1a) becomes:


1
− ∂r (r∂r u) = 1
r
and then, from the symmetry, u(r) = c − r2 /4, where c is a constant to determine from the
boundary condition. On the boundary r = 1 we have ∂n u = ∂r u = −1/2. When S ⩾ 1/2 the fluid
sticks at the wall and when S > 1/2 we have from (5.1b):
1
−Cf un − S =
2
From the previous expression u(r), taken for r = 1 we obtain the constant c and then:
1/n
1 − r2

max(0, 1/2 − S)
u(r) = + , 0⩽r⩽1
4 Cf

The error computation is implemented in the files ‘yield_slip_error.cc’ and


‘yield_slip_circle.h’: it is not listed here but is available in the Rheolef example di-
rectory.
The error can be computed (see Fig. 5.5):
216 Rheolef version 7.2

∥u − uh ∥0,2,Ω ∥u − uh ∥0,∞,Ω
10−2 10−2

2=k+1
2=k+1
10−4 10−4

10−6 10−6 3

3
4 4
k=1 k=1
10−8 k=2 10−8 k=2
k=3 k=3
10−2 10−1 1 10−2 10−1 1
h h
1
|∇(u − uh )|0,2,Ω ∥λ − λh ∥0,2,∂Ω

10−2
10−2 1=k
2=k+1
10−4

10−4 3
2 10−6
k=1 k=1
k=2 4 k=2
3 k=3
k=3
10−6 10−8
10−2 10−1 1 10−2 10−1 1
h h
Figure 5.5: The yield slip problem: error analysis.

make yield_slip_error
mkgeo_ball -t 20 -order 2 > circle-20-P2.geo
./yield_slip_damped_newton circle-20-P2.geo P2 0.6 1 | ./yield_slip_error

It appears that the discrete formulation develops optimal convergence properties versus mesh
refinement for k ⩾ 1 in H 1 , L2 and L∞ norms.
Chapter 5. Complex fluids 217

5.2 Viscoplastic fluids


5.2.1 Problem statement
Viscoplastic fluids develops an yield stress behavior (see e.g. Saramito, 2016b, Saramito and Wachs,
2017). Mosolov and Miasnikov [1965, 1966, 1967] first investigated the flow of a viscoplastic in
a pipe with an arbitrarily cross section. Its numerical investigation by augmented Lagrangian
methods was first performed by Saramito and Roquet [2001], Roquet and Saramito [2008]. The
Mosolov problem [Saramito and Roquet, 2001] writes:
(P ): find σ and u, defined in Ω, such that

div σ = −f in Ω (5.6a)
u = 0 on ∂Ω (5.6b)
when ∇u = 0 

|σ| ⩽ Bi
∇u in Ω (5.6c)
σ = |∇u|−1+n ∇u + Bi otherwise
|∇u|

where Bi ⩾ 0 is the Bingham number and n > 0 is a power-law index. The computational domain
Ω represents the cross-section of the pipe. In the bidimensional case d = 2 and when f is constant,
this problem describes the stationary flow of an Herschel-Bulkley fluid in a general pipe section Ω.
Let Ox2 be the axis of the pipe and Ox0 x1 the plane of the section Ω. The vector-valued field σ
represents the shear stress components (σ0,1 , σ0,2 ) while u is the axial component of velocity along
Ox3 . When Bi = 0, the problem reduces to the nonlinear p-Laplacian problem. When n = 1 the
fluid is a Bingham fluid. When n = 1 and Bi = 0, the problem reduces to the linear Poisson one
with σ = ∇u.

5.2.2 The augmented Lagrangian algorithm


This problem writes as a minimization of an energy:

u = arg min J(v) (5.7a)


v∈W01,p (Ω)

where Z Z Z
1
J(v) = |∇v| 1+n
dx + Bi |∇v| dx − f v dx (5.7b)
1+n Ω Ω Ω
This problem is solved by using an augmented Lagrangian algorithm. The auxiliary variable
γ = ∇u is introduced together with the Lagrangian multiplier σ associated to the constraint
∇u − γ = 0. For all r > 0, let:
Z Z Z Z Z
1 1+n r
L((v, γ); σ) = |γ| dx+Bi |γ| dx− f v dx+ σ.(∇u−γ) dx+ |∇u−γ|2 dx
1+n Ω Ω Ω Ω 2 Ω

An Uzawa-like minimization algorithm writes:

• k = 0: let λ(0) and γ (0) arbitrarily chosen.


• k ⩾ 0: let λ(k) and γ (k) being known.

u(k+1) := arg min L((v, γ (k) ); σ (k) )


v∈W 1,p (Ω)

γ (k+1) := arg min L((u(k+1) , δ); σ (k) )


δ∈L2 (Ω)d
 
σ (k+1) := σ (k) + ρ ∇u(k+1) − γ (k+1) in Ω
218 Rheolef version 7.2

The descent step ρ is chosen as ρ = r for sufficiently large r. The Lagrangian L is quadratic in
u and thus the computation of u(k+1) reduces to a linear problem. The non-linearity is treated
when computing γ (k+1) . This operation is performed point-by-point in Ω by minimizing:

|δ|1+n r|δ|2
γ := arg min + + Bi|δ| − ξ.δ
δ∈Rd 1+n 2

where ξ = σ (k) + r ∇u(k+1) is given. This problem is convex and its solution is unique. The
solution has the form:

when |ξ| ⩽ S

 0
(5.8)
def
γ = Pn,r (ξ) = ξ
 ϕn,r (|ξ| − S) otherwise
|ξ|

where ϕn,r (x) = fn,r


−1
(x) has been introduced in (5.2) page 204 in the context of the yield slip
problem together with the scalar projector.

File 5.11: vector_projection.h


1 # include " phi . h "
2 struct vector_projection {
3 Float operator () ( const Float & x ) const {
4 if ( x <= a ) return 0;
5 return _phi (x - a )/ x ;
6 }
7 vector_projection ( Float a1 , Float n =1 , Float c =1 , Float r =0)
8 : a ( a1 ) , _phi (n ,c , r ) {}
9 Float a ;
10 phi _phi ;
11 };

Finally, the Uzawa-like minimization algorithm [Saramito and Roquet, 2001] writes:

• k = 0: let σ (0) and γ (0) arbitrarily chosen.

• k ⩾ 0: let σ (k) and γ (k) being known, find u(k+1) such that

 
−r∆u(k+1) = f + div σ (k) − rγ (k) in Ω
u(k+1) = 0 on ∂Ω

and then compute explicitly γ (k+1) and σ (k+1) :

 
γ (k+1) := Pn,r σ (k) + r∇u(k+1) (5.9)
 
σ (k+1) := σ (k) + r ∇u(k+1) − γ (k+1)

Here r > 0 is a numerical parameter. This algorithm reduces the nonlinear problem to a se-
quence of linear and completely standard Poisson problems and some explicit computations. For
convenience, this algorithm is implemented as the solve function member of a class:
Chapter 5. Complex fluids 219

File 5.12: mosolov_augmented_lagrangian1.icc


1 # include " vector_projection . h "
2 int m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n :: solve ( field & sigma_h , field & uh ) const {
3 test v ( Xh );
4 derr << " # k residue " << endl ;
5 for ( size_t k = 0; true ; ++ k ) {
6 field grad_uh = inv_mt *( b * uh );
7 auto c = compose ( vector_projection ( Bi ,n ,1 , r ) , norm ( sigma_h + r * grad_uh ));
8 field gamma_h = lazy_interpolate ( Th , c *( sigma_h + r * grad_uh ));
9 field delta_sigma_h = r *( grad_uh - gamma_h );
10 sigma_h += delta_sigma_h ;
11 Float residue = delta_sigma_h . max_abs ();
12 derr << k << " " << residue << endl ;
13 if ( residue <= tol || k >= max_iter ) {
14 derr << endl << endl ;
15 return ( pow ( residue ,3) <= tol ) ? 0 : 1;
16 }
17 field rhs = (1/ r )*( lh - integrate ( dot ( sigma_h - r * gamma_h , grad ( v ))));
18 pa . solve ( rhs , uh );
19 }
20 }

For convenience, the order of the update of the three variables u, γ and σ has been rotated: by
this way, the algorithm starts with initial values for u and σ. instead of γ and σ. Observe that
the projection step (5.9) is implemented by using the interpolate operator: this projection step
interprets as point-wise at Lagrange nodes instead as a numerical resolution of the element-wise
minimization problem. Note that, for the lowest order k = 1, these two approaches are strictly
equivalent, while, when k ⩾ 2, the numerical solution obtained by this algorithm is no more
solution of the discrete version of the saddle-point problem for the Lagrangian L. Nevertheless,
the numerical solution is founded to converge to the exact solution of the initial problem (5.6a)-
(5.6c): this will be checked here in a forthcoming section, dedicated to the error analysis. Observe
also that the stopping criterion for breaking the loop bases on the max of the relative error for
the σ h variable. For this algorithm, this stopping criterion guaranties that all residual terms of
the initial problem are also converging to zero, as it will be checked here. Moreover, this stopping
criterion is very fast to compute while the full set of residual terms of the initial problem would
take more computational time inside the loop.
The class declaration contains all model parameters, loop controls, form and space variables,
together with some pre- and post-treatments:

File 5.13: mosolov_augmented_lagrangian.h


1 struct m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n : adapt_option {
2 m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n ();
3 void reset ( geo omega , string approx );
4 void initial ( field & sigma_h , field & uh ) const ;
5 int solve ( field & sigma_h , field & uh ) const ;
6 void put ( odiststream & out , field & sigma_h , field & uh ) const ;
7 // data :
8 Float Bi , n , r , tol ;
9 size_t max_iter ;
10 mutable space Xh , Th ;
11 mutable field lh ;
12 mutable form a , b , inv_mt ;
13 mutable problem pa ;
14 };
15 # include " m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n 1 . icc "
16 # include " m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n 2 . icc "
220 Rheolef version 7.2

File 5.14: mosolov_augmented_lagrangian2.icc


1 m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n :: m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n ()
2 : Bi (0) , n (1) , r (1) , tol (1 e -10) , max_iter (1000000) ,
3 Xh () , Th () , lh () , a () , b () , inv_mt () , pa ()
4 {}
5 void m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n :: reset ( geo omega , string approx ) {
6 Xh = space ( omega , approx );
7 Xh . block ( " boundary " );
8 string grad_approx = " P " + to_string ( Xh . degree () -1) + " d " ;
9 Th = space ( omega , grad_approx , " vector " );
10 trial u ( Xh ) , sigma ( Th );
11 test v ( Xh ) , tau ( Th );
12 lh = integrate (2* v );
13 a = integrate ( dot ( grad ( u ) , grad ( v )));
14 b = integrate ( dot ( grad ( u ) , tau ));
15 integrate_option iopt ;
16 iopt . invert = true ;
17 inv_mt = integrate ( dot ( sigma , tau ) , iopt );
18 pa = problem ( a );
19 }
20 void
21 m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n :: initial ( field & sigma_h , field & uh ) const {
22 uh = field ( Xh );
23 uh [ " boundary " ] = 0;
24 pa . solve ( lh , uh );
25 test tau ( Th );
26 field mt_grad_uh = integrate ( dot ( grad ( uh ) , tau ));
27 sigma_h = inv_mt * mt_grad_uh ;
28 }
29 void m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n :: put ( odiststream & out ,
30 field & sigma_h , field & uh ) const
31 {
32 out << catchmark ( " Bi " ) << Bi << endl
33 << catchmark ( " n " ) << n << endl
34 << catchmark ( " r " ) << r << endl
35 << catchmark ( " sigma " ) << sigma_h
36 << catchmark ( " u " ) << uh ;
37 }
Chapter 5. Complex fluids 221

File 5.15: mosolov_augmented_lagrangian.cc


1 # include " rheolef . h "
2 using namespace std ;
3 using namespace rheolef ;
4 # include " m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 m o s o l o v _ a u g m e n t e d _ l a g r a n g i a n pb ;
8 geo omega ( argv [1]);
9 string approx = ( argc > 2) ? argv [2] : " P1 " ;
10 pb . Bi = ( argc > 3) ? atof ( argv [3]) : 0.2;
11 pb . n = ( argc > 4) ? atof ( argv [4]) : 1;
12 size_t n_adapt = ( argc > 5) ? atoi ( argv [5]) : 0;
13 pb . max_iter = ( argc > 6) ? atoi ( argv [6]) : 10000;
14 pb . err = ( argc > 7) ? atof ( argv [7]) : 1e -4;
15 pb . r = 100;
16 pb . tol = 1e -10;
17 pb . hmin = 1e -4;
18 pb . hmax = 1e -1;
19 pb . ratio = 3;
20 pb . additional = " - AbsError " ;
21 field sigma_h , uh ;
22 for ( size_t i = 0; true ; i ++) {
23 pb . reset ( omega , approx );
24 pb . initial ( sigma_h , uh );
25 int status = pb . solve ( sigma_h , uh );
26 odiststream out ( omega . name () , " field " );
27 pb . put ( out , sigma_h , uh );
28 if ( i == n_adapt ) break ;
29 space T0h ( sigma_h . get_geo () , " P " + to_string ( sigma_h . get_space (). degree ())+ " d " );
30 field ch = lazy_interpolate ( T0h , sqrt ( abs ( dot ( sigma_h , grad ( uh )))));
31 omega = adapt ( ch , pb );
32 omega . save ();
33 }
34 }

The main program read parameters from the command line and performs an optional mesh adap-
tation loop. This implementation supports any n > 0, any continuous piecewise polynomial Pk ,
k ⩾ 1 and also isoparametric approximations for curved boundaries.

Running the program

Compile the program as usual:

make mosolov_augmented_lagrangian
mkgeo_grid -a -1 -b 1 -c -1 -d 1 -t 10 > square.geo
./mosolov_augmented_lagrangian square.geo P1 0.4 1
field -mark u square.field -elevation

Observe on Fig. 5.6.left the central region where the velocity is constant. A cut of the velocity
field along the first bisector is obtained by:

field -mark u square.field -cut -origin 0 0 -normal 1 1 -gnuplot

Observe on Fig. 5.6.right the small regions with zero velocity, near the outer corner of the square
pipe section. This region is really small but exists This question will be revisited in the next
section dedicated to auto-adaptive mesh refinement.
The file ‘mosolov_residue.cc’ implement the computation of the full set of residual terms of the
initial problem. This file it is not listed here but is available in the Rheolef example directory.
The computation of residual terms is obtained by:

make mosolov_residue
zcat square.field.gz | ./mosolov_residue
222 Rheolef version 7.2

0.3
u(x, y) h = 1/10
h = 1/20
h = 1/30
h = 1/40
h = 1/50
0.2

0.01

0.1

0 √
1.3 2
0 √
0 1.3 2
p
x2 + y 2

Figure 5.6: The augmented Lagrangian algorithm on the Mosolov problem with Bi = 0.4 and
n = 1: (left) the velocity field in elevation view (h = 1/30); (right) velocity cut along the first
bisector for various h.

Observe that the residual terms of problem (5.6a)-(5.6c) are of about 10−10 , as required by the
stopping criterion. Fig. 5.7 plots the max of the relative error for the σ h variable: this quantity
is used as stopping criterion. Observe that it behaves asymptotically as 1/k for large iteration
number k. Note that these convergence properties could be dramatically improved by using a
Newton method, as shown by Saramito [2016a].
Finally, computation can be performed for any n > 0, any polynomial order k ⩾ 1 and in a
distributed environment for enhancing performances on larger meshes:

mkgeo_grid -a -1 -b 1 -c -1 -d 1 -t 40 > square-40.geo


mpirun -np 8 ./mosolov_augmented_lagrangian square-40.geo P2 0.4 0.5
field -mark u square-40.field -elevation

The computation could take about ten minutes. The mpirun -np 8 prefix is optional and you
should omit it if you are running a sequential installation of Rheolef.

5.2.3 Mesh adaptation


An important improvement can be obtained by using mesh adaptation, as shown in [Saramito
and Roquet, 2001, Roquet and Saramito, 2003, 2008]: with a well chosen criterion, rigid regions,
where the velocity is constant, can be accurately determined with reasonable mesh sizes. This is
especially true for obtaining an accurate determination of the shape of the small regions with zero
velocity in the outer corner of the pipe section. In order to reduce the computational time, we
can reduce the pipe section flow to only a sector, thanks to symmetries (see Fig. 5.8):

mkgeo_sector
geo sector.geo

Then, the computation is run by indicating an adaptation loop with ten successive meshes:

make mosolov_augmented_lagrangian
mpirun -np 8 ./mosolov_augmented_lagrangian sector P2 0.5 1 10
Chapter 5. Complex fluids 223

residue h = 1/10
1 h = 1/20
h = 1/30
h = 1/40

−1

10−5

10−10
1 103 106
k

Figure 5.7: The augmented Lagrangian algorithm on the Mosolov problem with Bi = 0.4 and
n = 1: residue versus iteration k for various h.

This computation could take about one hour. The mpirun -np 8 prefix is optional and you should
omit it if you are running a sequential installation of Rheolef.
Fig. 5.9 shows the evolution of the mesh size and the minimal edge length during the adaptation
loop: observe the convergence of the meshes to an optimal one.

File 5.16: mosolov_yield_surface.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 Float tol = ( argc > 1) ? atof ( argv [1]) : 1e -15;
7 Float Bi ;
8 field sigma_h ;
9 din >> catchmark ( " Bi " ) >> Bi
10 >> catchmark ( " sigma " ) >> sigma_h ;
11 space Th = sigma_h . get_space ();
12 space Th1 ( Th . get_geo () , " P " + to_string (4*( Th . degree ()+1)) + " d " );
13 dout << lazy_interpolate ( Th1 , norm ( sigma_h ) - Bi );
14 }
The yield surface is the zero isosurface of the level set function ϕ(x = |σ h (x| − Bi. Its visualization
is easy to obtain by the following commands:

make mosolov_yield_surface
zcat sector-010.field.gz | ./mosolov_yield_surface | \
field - -proj P1 -n-iso 10 -n-iso-negative 5

The unyielded zones, associated to negative values, appear in cold colors. Conversely, the yielded
ones are represented by warm colors. A combined representation of the solution can be obtained
by the following command:

bash mkview_mosolov sector-010.field.gz

The shell script mkview_mosolov invokes mosolov_yield_surface and then directly builds the
view shown on Fig. 5.8 in the paraview graphic render. Please, click to deselect the show box
option for completing the view. A cut of the velocity field along the first bisector is obtained by:
224 Rheolef version 7.2

field -mark u sector-010.field.gz -mark u -domain bisector -elevation -gnuplot

Observe on Fig. 5.8.bottom the good capture of the small regions with zero velocity, near the
outer corner of the square pipe section. When compared with Fig. 5.6.right, the benefict of mesh
adaptation appears clearly.

5.2.4 Error analysis


Theoretical error bounds for this problem can be found in [Roquet et al., 2000]. In order to study
the error between the numerical solution and the exact solution of the Mosolov problem, let us
investigate a case for which the exact solution can be explicitly expressed. We consider the special
case of a flow of a viscoplastic fluid in a circular pipe. The pressure is expressed by p(z) = −f z.
The velocity has only one nonzero component along the 0z axis, denoted as u(r) for simplicity.
Conversely, the symmetric tensor σ has only one non-zero rz component, denoted as σ(r). Thanks
to the expression of the tensor-divergence operator in axisymmetric coordinates [Bird et al., 1987,
p. 588], the problem reduces to :
(P) : find σ(r) and u(r), defined in ] − R, R[ such that

u′ (r)

σ(r) = K|u′ (r)|−1+n u′ (r) + σ0 , when u′ (r) ̸= 0

|u′ (r)|
|σ(r)| ⩽ σ0 otherwise

1
− (rσ)′ − f = 0 in ] − R, R[
r
u(−R) = u(R) = 0

Let Σ = f R/2 be a representative stress and U a representative velocity such that K(U/R)n = Σ.
Then, we consider the following change of unknown:

r = Rr̃, u = U ũ, σ = Σσ̃

The system reduces to a problem with only two parameters n and the Bingham number Bi =
2σ0 /(f R), that measures the ratio between the yield stress and the load. Since there is no more
ambiguity, we omit the tildes :
(P) : find σ and u, defined in ] − 1, 1[ such that

u′ (r)

σ(r) = |u′ (r)|−1+n u′ (r) + Bi , when u′ (r) ̸= 0

|u′ (r)|
|σ(r)| ⩽ Bi otherwise

− 1r (rσ)′
−2 = 0 in ] − 1, 1[
u(−1) = u(1) = 0

Remark that the solution is even : u(−r) = u(r). We get σ(r) = −r and the yield stress criterion
leads to u(x) = 0 when |r| ⩽ Bi: the load is weaker than the yield stress and the flow is null.
When Bi > 1 the solution is u = 0. Otherwise, when |r| > Bi, we get |u′ (r)|n + Bi = |r| and
finally, with the boundary conditions and the continuity at r = ±Bi :
1 1
(1 − Bi)1+ n − max(0, |r| − Bi)1+ n
u(r) =
1 + n1

When n = 1, the second derivative of the solution is discontinuous at r = Bi and its third
derivative is not square integrable. For any n > 0, an inspection of the integrability of the square
of the solution derivatives shows that u ∈ H 1+1/n (] − 1, 1[, r dr) at the best.
Chapter 5. Complex fluids 225

File 5.17: mosolov_exact_circle.h


1 struct u {
2 Float operator () ( const point & x ) const {
3 return ( pow (1 - Bi ,1+1/ n ) - pow ( max ( Float (0) , norm ( x ) - Bi ) ,1+1/ n ))/(1+1/ n );
4 }
5 u ( Float Bi1 , Float n1 ) : Bi ( Bi1 ) , n ( n1 ) {}
6 protected : Float Bi , n ;
7 };
8 struct grad_u {
9 point operator () ( const point & x ) const {
10 Float r = norm ( x );
11 return ( r <= Bi ) ? point (0 ,0) : - pow (r - Bi , 1/ n )*( x / r );
12 }
13 grad_u ( Float Bi1 , Float n1 ) : Bi ( Bi1 ) , n ( n1 ) {}
14 protected : Float Bi , n ;
15 };
16 struct sigma {
17 point operator () ( const point & x ) const { return -x ; }
18 sigma ( Float =0 , Float =0) {}
19 };

When computing on a circular pipe section, the exact solution is known and it is also possible to
compute the error: this is implemented in the file ‘mosolov_error.cc’. This file it is not listed
here but is available in the Rheolef example directory. The error analysis is obtained by:

make mosolov_error
mkgeo_ball -order 2 -t 10 > circle-P2-10.geo
./mosolov_augmented_lagrangian circle-P2-10.geo P2 0.2 0.5
zcat circle-P2-10.field | ./mosolov_error

Note that we use an high order isoparametric approximation of the flow domain for tacking into
account the the curved boundaries. Observe on Fig. 5.10 that both the error in H 1 norm for the
velocity u behaves as O(hs ) with s = min(k, 2). This is optimal, as, from interpolation theory
[Brenner and Scott, 2002, p. 109], we have:

∥u − πh (u)∥1,2,Ω ⩽ Chs |u|s+1,2,Ω

with s = min(k, 1/n) when u ∈ H 1+1/n (Ω). Then, the convergence rate of the error in H 1 norm
versus the mesh size is bounded by 1/n for any polynomial order k. The rate for the error in L∞
norm for for the velocity u is min(k + 1, 1/n), for any k ⩾ 1 and n > 0. For the stress σ, the
error in L2 norm behaves as O(h) for any polynomial order and, in L∞ norm, the rate is weaker,
of about 3/4. Finally, for n = 1/2 there is no advantage of using polynomial order more than
k = 2 with quasi-uniform meshes. Conversely, for n = 1, we obtains that there is no advantage
of using polynomial order more than k = 1 with quasi-uniform meshes. This limitation can be
circumvented by combining mesh adaptation with high order polynomials [Roquet et al., 2000,
Saramito and Roquet, 2001].

5.2.5 Error analysis for the yield surface


The limit contour separating the rigid zones are expressed as a level set of the stress norm:

Γh = {x ∈ Ω ; |σ h (x)| = Bi}

This contour is called the yield surface and its exact value is known from the exact solution
σ(x) = x in the circle:
Γ = {x ∈ Ω ; |x| = Bi}
Recall the stress σ h converges in L∞ norm, thus, there is some hope that its point-wise values
converge. As these point-wise values appears in the definition of the yield surface, this suggests
226 Rheolef version 7.2

that Γh could converge to Γ with mesh refinement: our aim is to check this conjecture. Let us
introduce the area between Γh and Γ as a L1 measure of the distance between them:
Z
dist(Γ, Γh ) = δ (|σ h | − Bi, |σ| − Bi) dx

where
when ϕψ ⩾ 0

0
δ(ϕ, ψ) =
1 otherwise

File 5.18: mosolov_error_yield_surface.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " mosolov_exact_circle . h "
5 Float delta ( Float f , Float g ) { return ( f * g >= 0) ? 0 : 1; }
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 Float tol = ( argc > 1) ? atof ( argv [1]) : 1e -15;
9 Float Bi ;
10 field sigma_h ;
11 din >> catchmark ( " Bi " ) >> Bi
12 >> catchmark ( " sigma " ) >> sigma_h ;
13 space Th = sigma_h . get_space ();
14 geo omega = Th . get_geo ();
15 integrate_option iopt ;
16 iopt . set_family ( integrate_option :: gauss );
17 iopt . set_order (4*( Th . degree ()+1));
18 Float err_ys_l1 = integrate ( omega ,
19 compose ( delta , norm ( sigma_h ) - Bi , norm ( sigma ()) - Bi ) , iopt );
20 dout << " err_ys_l1 = " << err_ys_l1 << endl ;
21 return err_ys_l1 < tol ? 0 : 1;
22 }

The computation of the error for the yield surface prediction writes:

make mosolov_error_yield_surface
zcat circle-P2-10.field.gz | ./mosolov_error_yield_surface

Fig. 5.11.right shows the result: the yield surface error converges as O(h) for any k ⩾ 0.
Recall that the yield surface is the zero isosurface of the level set function ϕ(x = |σ h (x| − Bi. Its
visualization, shown on Fig. 5.11.left, is provided by the following commands:

make mosolov_yield_surface
zcat circle-P2-10.field.gz | ./mosolov_yield_surface | \
field - -proj P1 -n-iso 10 -n-iso-negative 5
Chapter 5. Complex fluids 227

0.2
u(x, y)
umax

0.1
0.006

0

1.3 2
0

0 1 1.3 2
p
x2 + y 2

Figure 5.8: Auto-adaptive meshes for the Mosolov problem with Bi = 0.5 and n = 1 and the P2
element for the velocity: (top) Yielded regions in gray and nine isovalues of the velocity inside
]0, umax [ with umax = 0.169865455242435. The auto-adaptive mesh is mirrored. (top-right) Zoom
in the outer corner of the square section. (bottom) Velocity cut along the first bisector.
228 Rheolef version 7.2

5000 10−1
mesh size hmin

4000
10−2

3000
10−3
2000

10−4
1000

0 10−5
0 5 10 0 5 10
adaptation iteration adaptation iteration

Figure 5.9: Auto-adaptive meshes for the Mosolov problem: evolution of the mesh size (left) and
the minimal edge length during the adaptation loop.

1 1
∥u − uh ∥1,2,Ω k=1 ∥u − uh ∥0,∞,Ω k=1
k=2 k=2
k=3 k=3

10−2 1 = min(k, 1/n) 10−2

2
2 = min(k + 1, 1/n)
10−4 10−4

2
10−6 10−6
10−2 10−1 10−2 10−1
h h
10−1 5 × 10−1
∥σ − σ h ∥0,2,Ω ∥σ − σ h ∥0,∞,Ω k=1
k=2
k=3

10−2
3/4

10−1
k=1
1 k=2
k=3
10−3 5 × 10−2
10−2 10−1 10−2 10−1
h h

Figure 5.10: The augmented Lagrangian algorithm on the Mosolov problem with Bi = 0.2 and
n = 1/2: error versus mesh paremeter h for various polynomial order k.
Chapter 5. Complex fluids 229

0.1
dist(Γ, Γh )
h = 1/10
h = 1/20
h = 1/40
exact

0.01
1

k=1
k=2
k=2
0.001
0.01 0.1
h

Figure 5.11: The augmented Lagrangian algorithm on the Mosolov problem with Bi = 0.2 and
n = 1/2: (left) convergence of the yield surface versus mesh parameter h for various polynomial
order k ; (right) visualization of the yield surface (P1 approximation).
230 Rheolef version 7.2

5.3 Viscoelastic fluids


5.3.1 A tensor transport equation
The aim of this chapter is to introduce to the numerical resolution of equations involving tensor
derivatives by using discontinuous approximations. See Saramito [2013b, chap. 4] for an intro-
duction to tensor derivatives and in section 4.1.1 of the present book, page 147, for discontinuous
Galerkin methods.
The tensor derivative of a symmetric tensor σ is defined by:

Da σ ∂σ
= + (u.∇)σ + σg a (u) + g Ta (u)σ, (5.10)
Dt ∂t
where u is a given velocity field,

g a (u) = (1 − a) ∇u − (1 + a) ∇uT /2 (5.11)




is a generalized velocity grandient and a ∈ [−1, 1] is the parameter of the tensorderivative. Prob-
lems involving tensor derivatives appear in viscoelasticity (polymer solution and polymer melt,
see e.g. Saramito, 2016b), in fluid-particle suspension modeling (see e.g. Ozenda et al., 2018), in
turbulence modeling (Rij −ϵ models) or in liquid crystals modeling. Let Ω ⊂ Rd be a bounded
open domain.
The time-dependent tensor transport problem writes:
(P ): find σ, defined in ]0, T [×Ω, such that

Da σ
+ νσ = χ in ]0, T [×Ω
Dt
σ = σ Γ on ]0, T [×∂Ω−
σ(0) = σ 0 in Ω

where σ is the tensor valued unknown and ν > 0 is a constant that represents the inverse of the
Weissenberg number. Also T > 0 is a given final time, the data χ, σ Γ and σ 0 are known and ∂Ω−
denotes the upstream boundary (see section 4.1.1, page 147).
The steady version of the tensor transport problem writes:
(S): find σ, defined in Ω, such that

u.∇)σ + σg a (u + g Ta (u)σ + νσ = χ in Ω


σ = σ Γ on ∂Ω−

A sufficient condition this problem to be well posed is [Saramito, 1994, 2013b]:

u ∈ W 1,∞ (Ω)d and 2ν − ∥div u∥0,∞,Ω − 2a∥D(u)∥0,∞,Ω > 0

Note that this condition is always satisfied when div u = 0 and a = 0. We introduce the space:

X = {τ ∈ L2 (Ω)sd×d ; (u.∇)τ ∈ L2 (Ω)d×d


s }

and, for all σ, τ ∈ X


Z Z
(u.∇)σ + σg a (u) + g Ta (u)σ + ν σ : τ dx

a(σ, τ ) = + max (0, −u.n) σ : τ ds
Ω Z Z∂Ω
l(τ ) = χ : τ dx + max (0, −u.n) σ Γ : τ ds
Ω ∂Ω

Then, the variational formulation of the steady problem writes:


Chapter 5. Complex fluids 231

(F V ): find σ ∈ X such that

a(σ, τ ) = l(τ ), ∀τ ∈ X

Note that the term max(0, −u.n) = (|u.n| − u.n)/2 is positive and vanishes everywhere except
on ∂Ω− . Thus, the boundary condition ϕ = ϕΓ is weakly imposed on ∂Ω− via the integrals on
the boundary. We aim at adapting the discontinuous Galerkin method to this problem. The
discontinuous finite element space is defined by:

Xh = {τ h ∈ L2 (Ω)sd×d ; τ h|K ∈ Pk , ∀K ∈ Th }

where k ⩾ 0 is the polynomial degree. Note that Xh ̸⊂ X and that the ∇τ h term has no more
sense for discontinuous functions τ h ∈ Xh . We introduce the broken gradient ∇h as a convenient
notation:

(∇h τ h )|K = ∇(τ h|K ), ∀K ∈ Th

Thus

Z X Z
((u.∇h )σ h ) : τ h dx = ((u.∇)σ h ) : τ h dx, ∀σ h , τ h ∈ Xh
Ω K∈Th K

This leads to a discrete version ah of the bilinear form a, defined for all σ h , τ h ∈ Xh by:

Z
ah (σ h , τ h ) = th (u; σ h , τ h ) + ν σ h : τ h dx

Z Z
T

th (u; σ h , τ h ) = (u.∇h )σ h + σg a (u) + g a (u)σ : τ h dx + max (0, −u.n) σ h : τ h ds
Ω ∂Ω
Z
X  α 
+ − u.n [[σ h ]] : {{τ h }} + |u.n| [[σ h ]] : [[τ h ]] ds (5.12)
(i) S 2
S∈Sh

(F V )h : find σ h ∈ Xh such that

ah (σ h , τ h ) = l(τ h ), ∀τ h ∈ Xh

The following code implement this problem in the Rheolef environment.


232 Rheolef version 7.2

File 5.19: transport_tensor_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " t ran sp ort _t ens or_ ex act . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh ( omega , argv [2] , " tensor " );
9 Float alpha = ( argc > 3) ? atof ( argv [3]) : 1;
10 Float nu = ( argc > 4) ? atof ( argv [4]) : 3;
11 Float t0 = ( argc > 5) ? atof ( argv [5]) : acos ( -1.)/8;
12 Float a = 0;
13 trial sigma ( Xh ); test tau ( Xh );
14 tensor ma = 0.5*((1 - a )* grad_u - (1+ a )* trans ( grad_u ));
15 auto beta_a = sigma * ma + trans ( ma )* sigma ;
16 form ah = integrate ( ddot ( grad_h ( sigma )* u + beta_a + nu * sigma , tau ))
17 + integrate ( " boundary " ,
18 max (0 , - dot (u , normal ()))* ddot ( sigma , tau ))
19 + integrate ( " internal_sides " ,
20 - dot (u , normal ())* ddot ( jump ( sigma ) , average ( tau ))
21 + 0.5* alpha * abs ( dot (u , normal ()))
22 * ddot ( jump ( sigma ) , jump ( tau )));
23 field lh = integrate ( ddot ( chi ( nu , t0 ) , tau ))
24 + integrate ( " boundary " ,
25 max (0 , - dot (u , normal ()))* ddot ( sigma_g ( nu , t0 ) , tau ));
26 field sigma_h ( Xh );
27 problem p ( ah );
28 p . solve ( lh , sigma_h );
29 dout << catchmark ( " nu " ) << nu << endl
30 << catchmark ( " t0 " ) << t0 << endl
31 << catchmark ( " sigma " ) << sigma_h ;
32 }

Running the program

Let d = 2 and Ω =] − 1/2, 1/2[2 . We consider the rotating field u = (−x2 , x1 ). A particular
solution of the time-dependent problem with zero right-hand side is given by:
(x1 − x1,c (t))2 + (x2 − x2,c (t))2
   
1 1 + cos(2t) sin(2t)
σ(x, t) = exp −νt − ×
2 r02 sin(2t) 1 − cos(2t)

where x1,c (t) = x̄1,c cos(t) − x̄2,c sin(t) and x2,c (t) = x̄1,c sin(t) + x̄2,c cos(t) with r0 > 0 and
(x̄1,c , x̄2,c ) ∈ R2 . The initial condition is chosen as σ 0 (x) = σ(0, x). This exact solution is
implemented in the file ‘transport_tensor_exact.icc’. This file it is not listed here but is
available in the Rheolef example directory. For the steady problem, the right-hand side could be
∂σ
chosen as χ = − and then t = t0 is fixed. The numerical tests correspond to ν = 3, r0 = 1/10,
∂t
(x̄1,c , x̄2,c ) = (1/4, 0) and a fixed time t0 = π/8.

make transport_tensor_dg
mkgeo_grid -t 80 -a -0.5 -b 0.5 -c -0.5 -d 0.5 > square2.geo
./transport_tensor_dg square2 P1d > square2.field
field square2.field -comp 00 -elevation
field square2.field -comp 01 -elevation

The computation could also be performed with any Pkd, with k ⩾ 0.

Error analysis

The file ‘transport_tensor_error_dg.cc’ implement the computation of the error between the
approximate solution σ h and the exact one σ. This file it is not listed here but is available in the
Chapter 5. Complex fluids 233

1
∥σ h − σ∥0,2,Ω k =0
k =1
k =2
10−2 k =3

1=k+1

10−4
2

10−6
4
3

10−8
10−3 10−2 10−1
h

Figure 5.12: Tranport tensor problem: convergence versus mesh size.

Rheolef example directory. The computation of the error is obtained by:

make transport_tensor_error_dg
./transport_tensor_error_dg < square2.field

The error is plotted on Fig. 5.12 for various mesh size h and polynomial order k: observe the
optimality of the convergence properties. For k = 4 and on the finest mesh, the error saturates
at about 10−8 , due to finite machine precision effects. In the next chapter, transport tensor
approximation are applied to viscoelastic fluid flow computations.

5.3.2 The Oldroyd model


We consider the following viscoelastic fluid flow problem (see e.g. Saramito, 2016b, chap. 4):
(P ): find τ , u and p defined in ]0, T [×Ω such that

Da τ
We + τ − 2αD(u) = 0 in ]0, T [×Ω (5.13a)
  Dt
∂u
Re + u.∇u − div ( τ + 2(1 − α)D(u) − p.I) = 0 in ]0, T [×Ω (5.13b)
∂t
−div u = 0 in ]0, T [×Ω (5.13c)
τ = τΓ on ]0, T [×∂Ω− (5.13d)
u = uΓ on ]0, T [×∂Ω (5.13e)
τ (0) = τ0 and u(0) = u0 in Ω (5.13f)

where τ0 , u0 , τΓ and uΓ are given. The first equation corresponds to a generalized Oldroyd model
[Oldroyd, 1950]: when a = −1 we obtain the Oldroyd-A model, when a = 1, the Oldroyd-B model,
and when a ∈ ] − 1, 1[ a generalization of these two models. The dimensionless number W e ⩾ 0 is
the Weissenberg number: this is the main parameter for this problem. The dimensionless Reynolds
number Re ⩾ 0 is often chosen small: as such fluids are usually slow, the u.∇u inertia term is
also neglected here for simplicity. The parameter α ∈ [0, 1] represent a retardation. When α = 1
we obtain the Maxwell model, that is a reduced version of the Oldroyd one. The total Cauchy
234 Rheolef version 7.2

stress tensor is expressed by:

σ tot = −p I + 2(1 − α)D(u) + τ (5.14)

5.3.3 The θ-scheme algorithm


The θ-scheme is considered for the time discretization [Saramito, 1994] (see also Saramito, 2016b,
chap. 4): this leads to a semi-implicit splitting algorithm that defines a sequence (τ (n) , u(n) , pn )n⩾0
as

• n = 0: set (τ (0) , u(0) ) = (τ0 , u0 ) and p0 arbitrarily chosen.


• n ⩾ 0: let (τ (n) , u(n) ) being known, then (τ (n+1) , u(n+1) , p(n+1 ) is defined in three sub-steps.
* sub-step 1: compute explicitly:
   
γ = u(n) .∇τ (n) + τ (n) Ma u(n) + MaT u(n) τ (n)
 
f̃ = λu(n) + div c1 τ (n) + c2 γ

where
We W eθ∆t
c1 = and c2 = −
W e + θ∆t W e + θ∆t
Then determine (u(n+θ) , p(n+θ) ) such that
  
λu(n+θ) − div 2ηD u(n+θ) + ∇p(n+θ) = f̃ in Ω (5.15a)
−div u(n+θ) = 0 in Ω (5.15b)
u = uΓ ((n + θ)∆t) on ∂Ω (5.15c)

and finaly, compute explictely


 
τ (n+θ) = c1 τ (n) + c2 γ + 2c3 D u(n+θ) (5.15d)

where
Re (1 − α)W e + θ∆t αθ∆t
λ= , η= and c3 =
θ∆t W e + θ∆t W e + θ∆t

* sub-step 2: (τ (n+θ) , u(n+θ) ) being known, compute explictely


1 − θ (n+θ) 1 − 2θ (n)
u(n+1−θ) = u − u
θ  θ 
ξ = c4 τn+θ + 2c5 D u(n+θ)

and then find τ (n+1−θ) such that


   
u(n+1−θ) .∇τ (n+1−θ) + τ (n+1−θ) Ma u(n+1−θ) + MaT u(n+1−θ) τ (n+1−θ)

+ ντ (n+1−θ) = ξ in Ω (5.16a)
τ (n+1−θ) = τΓ ((n + 1 − θ)∆t) on ∂Ω− (5.16b)

where
1 1 1 α
ν= , c4 = − and c5 =
(1 − 2θ)∆t (1 − 2θ)∆t W e We
Chapter 5. Complex fluids 235

* sub-step 3 is obtained by replacing n and n + θ by n + 1 − θ and n + 1, respectively.


Thus, sub-step 1 and 2 reduces to two similar generalized Stokes problems while sub-step 3 involves
a stress transport
√ problem. Here ∆t > 0 and θ ∈]0, 1/2[ are numerical parameters. A good choice is
θ = 1−1/ 2 [Saramito, 1997]. This algorithm was first proposed by Saramito [1990] and extended
[Saramito, 1995] to Phan-Thien and Tanner viscoelastic models. See also [Singh and Leal, 1993]
for another similar approach in the context of FENE viscoelastic models. Scurtu [2005] presented
some benchmarks of this algorithm while Chrispell et al. [2009] presented a numerical analysis of
its convergence properties. The main advantage of this time-depend algorithm is its flexibility:
while most time-dependent splitting algorithms for viscoelastic are limited to α ⩽ 1/2 (see e.g.
Pan et al., 2009), here the full range α ∈]0, 1] is available.
Let us introduce the finite element spaces:
Th = {τ h ∈ (L2 (Ω))sd×d ; τ h|K ∈ (P1 )sd×d , ∀K ∈ Th }
Xh = {v h ∈ (H 1 (Ω))d ; τ h|K ∈ (P2 )d , ∀K ∈ Th }
Qh = {qh ∈ L2 (Ω) ; qh|K ∈ P1 , ∀K ∈ Th }
Note the discontinuous approximation of pressure: it presents a major advantage, as div(Xh ) ⊂
Qh , it leadsR to an exact divergence-free approximation of the velocity: for any field v h ∈ Xh
satisfying Ω qh div(v h ) dx = 0 for all qh ∈ Qh , we have div vh = 0 point-wise, everywhere in
Ω. The pair (Xh , Qh ) is known as the Scott and Vogelius [1985] lowest-order finite element
approximation. This is a major advantage when dealing with a transport equation. The only
drawback is that the pair (Xh , Qh ) does not satisfy the inf-sup condition for an arbitrary mesh.
There exists a solution to this however: Arnold and Qin [1992] proposed a macro element technique
applied to the mesh that allows satisfying the inf-sup condition: for any triangular finite element
mesh, it is sufficient to split each triangle in three elements from its barycenter (see also Saramito,
2014). Note that the macro element technique extends to quadrilateral meshes [Arnold and Qin,
1992] and to the three-dimensional case [Zhang, 2005]. By this way, the approximate velocity
field satisfies exactly the incompressibility constraint: this is an essential property for the operator
splitting algorithm to behave correctly, combining stress transport equation with a divergence-free
velocity approximation.
The tensor transport term is discretized as in the previous chapter, by using the th trilinear form
introduced in (5.12) page 231. The bilinear forms b, c, d are defined by:
Z
b(σ h , v h ) = σ h : D(v h ) dx

Z
c(uh , v h ) = D(uh ) : D(v h ) dx
ZΩ
d(uh , q h ) = div(uh ) qh dx

Let T , B, C, D and M be the discrete operators (i.e. the matrix) associated to the forms th , b, c,
d and the L2 scalar product in Th . Assume that a stationnary state is reached for the discretized
algorithm. Then, (5.15a)-(5.15d) writes
W e T τh + M τh − 2αB T uh = 0
B c1 τh + M −1 T τh + 2ηCuh + DT ph

= 0
Duh = 0
Note that (5.16a)-(5.16b) reduces also to the first equation of the previous system. Expanding
the coefficients, combining the two previous equations, and using C = BM −1 B T , we obtain the
system characterizing the stationary solution of the discrete version of the algorithm:
W e T τh + M τh − 2αB T uh = 0
T
Bτh + 2(1 − α)Cuh + D ph = 0
Duh = 0
236 Rheolef version 7.2

Note that this is exactly the discretized version of the stationary equation1 . In order to check
that the solution reaches a stationary state, the residual terms of this stationary equations are
computed at each iteration, together with the relative error between two iterates.

File 5.20: oldroyd_theta_scheme.h


1 template < class Problem >
2 struct oldroyd_theta_scheme {
3 o l d r oyd_theta_scheme ();
4 void initial ( const geo & omega , field & tau_h , field & uh , field & ph ,
5 string restart );
6 bool solve ( field & tau_h , field & uh , field & ph );
7 protected :
8 void step ( const field & tau_h0 , const field & uh0 , const field & ph0 ,
9 field & tau_h , field & uh , field & ph ) const ;
10 void sub_step1 ( const field & tau_h0 , const field & uh0 , const field & ph0 ,
11 field & tau_h , field & uh , field & ph ) const ;
12 void sub_step2 ( const field & uh0 , const field & tau_h1 , const field & uh1 ,
13 field & tau_h , field & uh ) const ;
14 Float residue ( field & tau_h , field & uh , field & ph ) const ;
15 void reset ( const geo & omega );
16 void u pd a te _t ra n sp or t _s tr es s ( const field & uh ) const ;
17 public :
18 Float We , alpha , a , Re , delta_t , tol ;
19 size_t max_iter ;
20 protected :
21 space Th , Xh , Qh ;
22 form b , c , d , mt , inv_mt , mu , mp ;
23 mutable form th ;
24 mutable field thb ;
25 Float theta , lambda , eta , nu , c1 , c2 , c3 , c4 , c5 ;
26 problem_mixed stokes ;
27 };
28 # include " old royd_ theta_ schem e1 . h "
29 # include " old royd_ theta_ schem e2 . h "
30 # include " old royd_ theta_ schem e3 . h "

1 The original θ-scheme presented by Saramito [1994] has an additional relaxation parameter ω. The present
version correspond to ω = 1. When ω ̸= 1, the stationary solution still depends slightly upon ∆t, as the stationary
system do not simplifies completely.
Chapter 5. Complex fluids 237

File 5.21: oldroyd_theta_scheme1.h


1 template < class P >
2 oldroyd_theta_scheme <P >:: oldroyd_theta_scheme ()
3 : We (0) , alpha (8./9) , a (1) , Re (1) , delta_t (0.025) , tol (1 e -6) , max_iter (500) ,
4 Th () , Xh () , Qh () , b () , c () , d () , mt () , inv_mt () , mu () , mp () , th () , thb () ,
5 theta () , lambda () , eta () , nu () , c1 () , c2 () , c3 () , c4 () , c5 () , stokes () {}
6 template < class P >
7 void oldroyd_theta_scheme <P >:: reset ( const geo & omega ) {
8 Th = space ( omega , " P1d " , " tensor " );
9 Xh = P :: velocity_space ( omega , " P2 " );
10 Qh = space ( omega , " P1d " );
11 theta = 1 -1/ sqrt (2.);
12 lambda = Re /( theta * delta_t );
13 eta = ((1 - alpha )* We + theta * delta_t )/( We + theta * delta_t );
14 nu = 1/((1 -2* theta )* delta_t );
15 c1 = We /( We + theta * delta_t );
16 c2 = - We * theta * delta_t /( We + theta * delta_t );
17 c3 = alpha * theta * delta_t /( We + theta * delta_t );
18 c4 = 1/((1 -2* theta )* delta_t ) - 1/ We ;
19 c5 = alpha / We ;
20 trial u ( Xh ) , tau ( Th ) , p ( Qh );
21 test v ( Xh ) , xi ( Th ) , q ( Qh );
22 mt = integrate ( ddot ( tau , xi ));
23 mu = integrate ( dot (u , v ));
24 mp = integrate ( p * q );
25 integrate_option iopt ;
26 iopt . invert = true ;
27 inv_mt = integrate ( ddot ( tau , xi ) , iopt );
28 b = integrate ( - ddot ( tau , D ( v )));
29 c = integrate ( lambda * dot (u , v ) + 2* eta * ddot ( D ( u ) , D ( v )));
30 d = integrate ( - div ( u )* q );
31 stokes = problem_mixed (c , d );
32 stokes . set_metric ( mp );
33 }
238 Rheolef version 7.2

File 5.22: oldroyd_theta_scheme2.h


1 template < class P >
2 bool oldroyd_theta_scheme <P >:: solve ( field & tau_h , field & uh , field & ph ) {
3 reset ( uh . get_geo ());
4 field tau_h0 = tau_h , uh0 = uh , ph0 = ph ;
5 derr << " # n t rel_err residue lambda_min " << endl ;
6 Float r = residue ( tau_h , uh , ph );
7 Float rel_err = 0;
8 derr << " 0 0 0 " << r << endl ;
9 for ( size_t n = 1; n <= max_iter ; ++ n ) {
10 step ( tau_h0 , uh0 , ph0 , tau_h , uh , ph );
11 Float rel_err_prec = rel_err , r_prec = r ;
12 r = residue ( tau_h , uh , ph );
13 rel_err = field ( tau_h - tau_h0 ). max_abs () + field ( uh - uh0 ). max_abs ();
14 derr << n << " " << n * delta_t << " " << rel_err << " " << r << endl ;
15 if ( rel_err < tol ) return true ;
16 if ( rel_err_prec != 0 && (( rel_err > 10* rel_err_prec && r > 10* r_prec ) ||
17 ( rel_err > 1 e5 && r > 1 e5 ) )) return false ;
18 tau_h0 = tau_h ; uh0 = uh ; ph0 = ph ;
19 }
20 return ( rel_err < sqrt ( tol ));
21 }
22 template < class P >
23 void oldroyd_theta_scheme <P >:: initial (
24 const geo & omega , field & tau_h , field & uh , field & ph , string restart ) {
25 reset ( omega );
26 ph = field ( Qh ,0);
27 if ( restart == " " ) {
28 uh = P :: velocity_field ( Xh );
29 trial u ( Xh ); test v ( Xh ) , xi ( Th );
30 form c0 = integrate (2* ddot ( D ( u ) , D ( v )));
31 problem_mixed s0 ( c0 , d );
32 s0 . set_metric ( mp );
33 s0 . solve ( field ( Xh ,0) , field ( Qh ,0) , uh , ph );
34 field Duh = inv_mt * integrate ( ddot ( D ( uh ) , xi ));
35 tau_h = 2* alpha * Duh ;
36 } else {
37 tau_h = field ( Th );
38 uh = field ( Xh );
39 idiststream in ( restart , " field " );
40 in >> catchmark ( " tau " ) >> tau_h
41 >> catchmark ( " u " ) >> uh
42 >> catchmark ( " p " ) >> ph ;
43 }
44 }
45 template < class P >
46 void oldroyd_theta_scheme <P >:: step (
47 const field & tau_h0 , const field & uh0 , const field & ph0 ,
48 field & tau_h , field & uh , field & ph ) const {
49 field tau_h1 = tau_h0 , uh1 = uh0 , ph1 = ph0 ;
50 sub_step1 ( tau_h0 , uh0 , ph0 , tau_h1 , uh1 , ph1 );
51 field tau_h2 = tau_h1 , uh2 = uh1 ;
52 sub_step2 ( uh0 , tau_h1 , uh1 , tau_h2 , uh2 );
53 sub_step1 ( tau_h2 , uh2 , ph1 , tau_h , uh , ph );
54 }
55 template < class P >
56 Float
57 oldroyd_theta_scheme <P >:: residue ( field & tau_h , field & uh , field & ph ) const {
58 u p d a t e_ t ra ns p or t_ st r es s ( uh );
59 test xi ( Th );
60 field Duh = inv_mt * integrate ( ddot ( D ( uh ) , xi ));
61 field gh = 2* Duh ;
62 field rt = We *( th * tau_h - thb ) + integrate ( ddot ( tau_h - alpha * gh , xi ));
63 field ru = b *( tau_h + (1 - alpha )* gh ) - d . trans_mult ( ph );
64 ru . set_b () = 0;
65 field rp = d * uh ;
66 return rt . u (). max_abs () + ru . u (). max_abs () + rp . u (). max_abs ();
67 }
Chapter 5. Complex fluids 239

File 5.23: oldroyd_theta_scheme3.h


1 template < class P >
2 void oldroyd_theta_scheme <P >:: sub_step1 (
3 const field & tau_h0 , const field & uh0 , const field & ph0 ,
4 field & tau_h , field & uh , field & ph ) const
5 {
6 u p d a t e_ t ra ns p or t_ st r es s ( uh0 );
7 field gamma_h = inv_mt *( th * tau_h0 - thb );
8 test v ( Xh ) , xi ( Th );
9 field lh = lambda * integrate ( dot ( uh0 , v ))
10 + b *( c1 * tau_h0 + c2 * gamma_h );
11 ph = ph0 ;
12 uh . set_u () = uh0 . u ();
13 stokes . solve ( lh , field ( Qh ,0) , uh , ph );
14 field Duh = inv_mt * integrate ( ddot ( D ( uh ) , xi ));
15 tau_h = c1 * tau_h0 + c2 * gamma_h + 2* c3 * Duh ;
16 }
17 template < class P >
18 void oldroyd_theta_scheme <P >:: sub_step2 (
19 const field & uh0 ,
20 const field & tau_h1 , const field & uh1 ,
21 field & tau_h , field & uh ) const
22 {
23 uh = (1 - theta )/ theta * uh1 - (1 -2* theta )/ theta * uh0 ;
24 test xi ( Th );
25 if ( We == 0) {
26 field Duh = inv_mt * integrate ( ddot ( D ( uh ) , xi ));
27 tau_h = 2* alpha * Duh ;
28 return ;
29 }
30 u p d a t e_ t ra ns p or t_ st r es s ( uh );
31 form th_nu = th + nu * mt ;
32 typename P :: tau_upstream tau_up ( Th . get_geo () , We , alpha );
33 field lh = integrate ( ddot ( c4 * tau_h1 + 2* c5 * D ( uh1 ) , xi ))
34 + integrate ( " boundary " ,
35 max (0 , - dot ( uh , normal ()))* ddot ( tau_up , xi ));
36 problem transport ( th_nu );
37 transport . solve ( lh , tau_h );
38 }
39 template < class P >
40 void
41 oldroyd_theta_scheme <P >:: up d at e_ t ra ns po r t_ st re s s ( const field & uh ) const {
42 typename P :: tau_upstream tau_up ( Th . get_geo () , We , alpha );
43 trial tau ( Th ); test xi ( Th );
44 auto ma = 0.5*((1 - a )* grad ( uh ) - (1+ a )* trans ( grad ( uh )));
45 auto beta_a = tau * ma + trans ( ma )* tau ;
46 th = integrate ( ddot ( grad_h ( tau )* uh + beta_a , xi ))
47 + integrate ( " boundary " , max (0 , - dot ( uh , normal ()))* ddot ( tau , xi ))
48 + integrate ( " internal_sides " ,
49 - dot ( uh , normal ())* ddot ( jump ( tau ) , average ( xi ))
50 + 0.5* abs ( dot ( uh , normal ()))* ddot ( jump ( tau ) , jump ( xi )));
51 thb = integrate ( " boundary " , max (0 , - dot ( uh , normal ()))* ddot ( tau_up , xi ));
52 }

5.3.4 Flow in an abrupt ontraction


Fig. 5.13 represents the contraction flow geometry. Let us denote by Γu , Γd , Γw and Γs the
upstream, downstream, wall and symmetry axis boundary domains, respectively. This geometry
has already been studied in section 2.2.2, in the context of a Newtonian fluid. Here, the fluid is
more complex and additional boundary conditions on the upstream domain are required for the
extra-stress tensor τ .
For the geometry, we assume that the lengths Lu and Ld are sufficiently large for the Poiseuille
flows to be fully developped at upstream and downstream. Assuming also a = 1, i.e. the Oldroyd-
B model, the boundary conditions at upstream are explicitely known as the solution of the fully
240 Rheolef version 7.2

x1
Γw
c

Γu

1
Γs Γd
−Lu (0, 0) Ld x0

Figure 5.13: The Oldroyd problem in the abrupt contraction: shematic view of the flow domain.

Poiseuille flow for a plane pipe with half width c:


  x 2 
1
u0 (x1 ) = ū 1 −
c
2ū x1
γ̇(x1 ) = u′0 (x1 ) = − 2
c
τ00 (x1 ) = 2αW e γ̇ 2 (x1 )
τ01 (x1 ) = τ10 (x1 ) = α γ̇(x1 )
τ11 (x1 ) = 0

where ū denotes the maximal velocity of the Poiseuille flow. Without loss of generality, thanks
to a dimensional analysis, it can be adjused with the contraction ratio c for obtaining a flow rate
equal to one:

3/(2c) for a planar geometry



ū =
4/c2 for an axisymmetric one

File 5.24: oldroyd_contraction.h


1 # include " contraction . h "
2 struct oldroyd_contraction : contraction {
3 struct tau_upstream : base {
4 tau_upstream ( geo omega , Float We1 , Float alpha1 )
5 : base ( omega ) , We ( We1 ) , alpha ( alpha1 ) {}
6 tensor operator () ( const point & x ) const {
7 tensor tau ;
8 Float dot_gamma = - 2* base :: umax * x [1]/ sqr ( base :: c );
9 tau (0 ,0) = 2* alpha * We * sqr ( dot_gamma );
10 tau (0 ,1) = tau (1 ,0) = alpha * dot_gamma ;
11 tau (1 ,1) = 0;
12 return tau ;
13 }
14 Float We , alpha ;
15 };
16 };

The class contraction, already used for Newtonian fluids, is here reused and extended with the
boundary condition function tau_upstream, as a derived class oldroyd_contraction. We are
now able to write the main program for solving a viscoelastic fluid flow problem in a contraction.
Chapter 5. Complex fluids 241

File 5.25: oldroyd_contraction.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " oldroyd_theta_scheme . h "
5 # include " oldroyd_contraction . h "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 cin >> noverbose ;
9 oldroyd_theta_scheme < oldroyd_contraction > pb ;
10 geo omega ( argv [1]);
11 Float We_incr = ( argc > 2) ? atof ( argv [2]) : 0.1;
12 Float We_max = ( argc > 3) ? atof ( argv [3]) : 0.1;
13 Float delta_t0 = ( argc > 4) ? atof ( argv [4]) : 0.005;
14 string restart = ( argc > 5) ? argv [5] : "";
15 pb . tol = 1e -3;
16 pb . max_iter = 50000;
17 pb . alpha = 8./9;
18 pb . a = 1;
19 pb . delta_t = delta_t0 ;
20 Float delta_t_min = 1e -5;
21 Float We_incr_min = 1e -5;
22 dout << catchmark ( " alpha " ) << pb . alpha << endl
23 << catchmark ( " a " ) << pb . a << endl ;
24 branch even ( " We " , " tau " , " u " , " p " );
25 field tau_h , uh , ph ;
26 pb . initial ( omega , tau_h , uh , ph , restart );
27 dout << even ( pb . We , tau_h , uh , ph );
28 bool ok = true ;
29 do {
30 if ( ok ) pb . We += We_incr ;
31 derr << " # We = " << pb . We << " delta_t = " << pb . delta_t << endl ;
32 field tau_h0 = tau_h , uh0 = uh , ph0 = ph ;
33 ok = pb . solve ( tau_h , uh , ph );
34 if ( ok ) {
35 dout << even ( pb . We , tau_h , uh , ph );
36 } else {
37 pb . delta_t /= 2;
38 tau_h = tau_h0 ; uh = uh0 ; ph = ph0 ;
39 if ( pb . delta_t < delta_t_min ) {
40 derr << " # solve failed : decreases We_incr and retry ... " << endl ;
41 pb . delta_t = delta_t0 ;
42 We_incr /= 2;
43 pb . We -= We_incr ;
44 if ( We_incr < We_incr_min ) break ;
45 } else {
46 derr << " # solve failed : decreases delta_t and retry ... " << endl ;
47 }
48 }
49 derr << endl << endl ;
50 } while ( true );
51 }

The splitting element technique for the Scott-Vogelius element is implemented as an option by
the command mkgeo_contraction. This file it is not listed here but is available in the Rheolef
example directory. The mesh generation for an axisymmetric contraction writes:

mkgeo_contraction 3 -c 4 -zr -Lu 20 -Ld 20 -split


geo contraction.geo

This command generates a mesh for the axisymmetric 4:1 abrupt contraction with upstream and
downstream length Lu = Ld = 20. Such high lengths are required for the Poiseuille flow to be
fully developped at upstream and downstream for large values of W e. The 3 first argument of
mkgeo_contraction is a number that characterizes the mesh density: when increasing, the average
edge length decreases. Then, the program is started:

make oldroyd_contraction
242 Rheolef version 7.2

mpirun -np 8 ./oldroyd_contraction contraction.geo 0.1 10 0.01 > contraction.branch

The program computes stationnary solutions by increasing W e with the time-dependent algorithm.
It performs a continuation algorithm, using solution at a lowest W e as initial condition. The
reccurence starts from an the Newtonian solution associated to W e = 0. The others model
parameters for this classical benchmark are fixed here as α = 8/9 and a = 1 (Oldroyd-B model).
The computation can take a while as there are two loops: one outer, on W e, and the other inner
on time, and there are two generalized Stokes subproblem and one tensorial transport one to solve
at each time iteration. The inner time loop stops when the relative error is small enough. Thus,
the parallel run, when available, is a major advantage: it is obtained by adding mpirun -np 8 at
the begining of the command line. Recall that the time scheme is conditionnaly stable: the time
step should be small enough for the algorithm to converge to a stationnary solution. When the
solver fails, it restarts with a smaller time step. Note that the present solver can be dramatically
improved: by using a Newton method, as shown by Saramito [2014], it is possible to directly reach
the stationnary solution, but such more sophisticated implementation is out of the scope of the
present documentation.
The visualization of the stream function writes:

branch contraction.branch -toc


branch contraction.branch -extract 3 -branch > contraction-We-0.3.field
make streamf_contraction
field -mark u contraction-We-0.3.field -field | ./streamf_contraction > psi.field
field psi.field -n-iso 15 -n-iso-negative 10
field psi.field -n-iso 15 -n-iso-negative 10 -bw

The file ‘streamf_contraction.cc’ has already been studied in section 2.2.2, in the context of
a Newtonian fluid. The result is shown on Fig. 5.14. The vortex growths with W e: this is the
major effet observed on this problem. Observe the two color maps representation: positive values
of the stream functions are associated to the vortex and negatives values to the main flow region.
The last command is a black-and-white variant view.
The vortex activity is obtained by:

field psi.field -max

Recall that the minimal value of the stream function is −1, thanks to the dimensionless procedure
used here.
Cuts along the axis of symmetry are obtained by:

field contraction-We-0.3.field -domain axis -mark u -comp 0 -elevation -gnuplot


field contraction-We-0.3.field -domain axis -mark tau -comp 00 -elevation -gnuplot
field contraction-We-0.3.field -domain axis -mark tau -comp 11 -elevation -gnuplot
field contraction-We-0.3.field -domain axis -mark tau -comp 22 -elevation -gnuplot

These cuts are plotted on Fig. 5.16. Observe the overshoot of the velocity along the axis when
W e > 0 while this effect is not perceptible when W e = 0. Also, the normal extra stress τzz growth
dramatically in the entry region.
Chapter 5. Complex fluids 243

W e = 0.1

W e = 0.3

W e = 0.7

Figure 5.14: The Oldroyd problem in the axisymmetric contraction: stream function for W e =
0.1, 0.3 and 0.7, from top to bottom.
244 Rheolef version 7.2

0.02
ψmax

0.01

0
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7
We

Figure 5.15: The Oldroyd-B problem in the axisymmetric contraction: vortex activity vs W e
(α = 8/9).
Chapter 5. Complex fluids 245

τzz (z, 0) W e = 0.7


uz (z, 0) 20 W e = 0.5
W e = 0.3
4 We = 0
16

12

W e = 0.7 4
W e = 0.5
W e = 0.3
0.25 We = 0 0
0
−4 0 4 8 −4 −2 0 2 4
z z
1 1
τrr (z, 0) τθθ (z, 0)

0 0

−1 −1

W e = 0.7 W e = 0.7
−2 W e = 0.5 −2 W e = 0.5
W e = 0.3 W e = 0.3
We = 0 We = 0
−4 −2 0 2 4 −4 −2 0 2 4
z z

Figure 5.16: The Oldroyd problem in the axisymmetric contraction: velocity and stress compo-
nents along the axis.
246 Rheolef version 7.2

5.3.5 [New] Stress diffusion


The tensor diffusion variant of a viscoelastic model introduces an additional second order diffusive
term in the differential equation for σ, see section 5.3.1. It develops nice theoretical properties, see
e.g. Lukáčová-Medvidová et al. [2017], Málek et al. [2018]. Let us consider the following variant
of the tensor transport problem previously introduced in section 5.3.1, page 230, by adding a
diffusion term:
(P ): find σ, defined in ]0, T [×Ω, such that
Da σ
+ νσ − ε∆σ = χ in ]0, T [×Ω
Dt
σ = σ Γ on ]0, T [×∂Ω−
σ(0) = σ 0 in Ω
where ε ⩾ 0 is the diffusion parameter. Observe that when ε = 0, this problem reduces to the
usual one, without diffusion, as introduced in section 5.3.1.
The steady version of the tensor transport problem writes:
(S): find σ, defined in Ω, such that
u.∇)σ + σg a (u + g Ta (u)σ + νσ − ε∆σ = χ in Ω


σ = σ Γ on ∂Ω−
We introduce the forms defined for all σ, τ ∈ H by 1
(Ω)sd×d
Z Z
(u.∇)σ + σg a (u) + g Ta (u)σ + ν σ : τ dx

a(σ, τ ) = + max (0, −u.n) σ : τ ds
Ω Z ∂Ω
+ ε∇σ : ∇τ dx

Z Z
l(τ ) = χ : τ dx + max (0, −u.n) σ Γ : τ ds
Ω ∂Ω
Then, the variational formulation of the steady problem writes:
(F V ): find σ ∈ X such that
a(σ, τ ) = l(τ ), ∀τ ∈ H 1 (Ω)sd×d
The discontinuous finite element space is then defined by:
Xh = {τ h ∈ H 1 (Ω)sd×d ; τ h|K ∈ Pk , ∀K ∈ Th }
where k ⩾ 0 is the polynomial degree. The discrete version ah of the bilinear form a is defined for
all σ h , τ h ∈ Xh by:
Z
ah (σ h , τ h ) = th (u; σ h , τ h ) + εdh (u; σ h , τ h ) + ν σ h : τ h dx

Z Z
(u.∇h )σ h + σg a (u) + g Ta (u)σ : τ h dx +

th (u; σ h , τ h ) = max (0, −u.n) σ h : τ h ds
Ω ∂Ω
Z
X  α 
+ − u.n [[σ h ]] : {{τ h }} + |u.n| [[σ h ]] : [[τ h ]] ds
(i) S 2
S∈Sh
Z
dh (u; σ h , τ h ) = ∇h σ h : ∇h τ h dx

X Z
+ (κs [[σ h ]] : [[τ h ]] − [[σ h ]] : {{∇h τ h n}} − [[τ h ]] : {{∇h σ h n}}) ds
S∈Sh S
Z Z
lh (σ h ) = χ : τ dx + max (0, −u.n) σ Γ : τ ds
Ω ∂Ω
Z
+ε (κs σ Γ : τ − σ Γ : (∇h τ n) + τ : (∇σ Γ n)) ds
∂Ω
Chapter 5. Complex fluids 247

where σ Γ is given and κs = β ϖs is the stabilisation parameter, with β = (k + 1)(k + d)/d and ϖs
is the penalisation, see section 4.3.1, page 167.
(F V )h : find σ h ∈ Xh such that

ah (σ h , τ h ) = lh (τ h ), ∀τ h ∈ Xh

The following code implement this problem in the Rheolef environment.

File 5.26: diffusion_transport_tensor_dg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " d i f f u s i o n _ t r a n s p o r t _ t e n s o r _ e x a c t . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 space Xh ( omega , argv [2] , " tensor " );
9 Float alpha = ( argc > 3) ? atof ( argv [3]) : 1;
10 Float nu = ( argc > 4) ? atof ( argv [4]) : 3;
11 Float t0 = ( argc > 5) ? atof ( argv [5]) : acos ( -1.)/8;
12 Float eps = ( argc > 6) ? atof ( argv [6]) : 1e -2;
13 Float a = 0;
14 size_t d = omega . dimension ();
15 size_t k = Xh . degree ();
16 Float eta = ( k +1)*( k + d )/ Float ( d );
17 trial sigma ( Xh ); test tau ( Xh );
18 tensor ma = 0.5*((1 - a )* grad_u - (1+ a )* trans ( grad_u ));
19 auto beta_a = sigma * ma + trans ( ma )* sigma ;
20 form ah = integrate ( ddot ( grad_h ( sigma )* u + beta_a + nu * sigma , tau )
21 + eps * dddot ( grad_h ( sigma ) , grad_h ( tau )))
22 + integrate ( " boundary " ,
23 max (0 , - dot (u , normal ()))* ddot ( sigma , tau ))
24 + integrate ( " internal_sides " ,
25 - dot (u , normal ())* ddot ( jump ( sigma ) , average ( tau ))
26 + 0.5* alpha * abs ( dot (u , normal ()))
27 * ddot ( jump ( sigma ) , jump ( tau )))
28 + integrate ( " sides " ,
29 eps * eta * penalty ()* ddot ( jump ( sigma ) , jump ( tau ))
30 - eps * ddot ( jump ( sigma ) , average ( grad_h ( tau )* normal ()))
31 - eps * ddot ( jump ( tau ) , average ( grad_h ( sigma )* normal ())));
32 field lh = integrate ( ddot ( chi ( eps , nu , t0 ) , tau ))
33 + integrate ( " boundary " ,
34 max (0 , - dot (u , normal ()))* ddot ( sigma_g ( nu , t0 ) , tau )
35 + eps * eta * penalty ()* ddot ( sigma_g ( nu , t0 ) , tau )
36 - eps * ddot ( sigma_g ( nu , t0 ) , grad_h ( tau )* normal ())
37 + eps * ddot ( grad_sigma_g ( nu , t0 )* normal () , tau ));
38 field sigma_h ( Xh );
39 problem p ( ah );
40 p . solve ( lh , sigma_h );
41 dout << catchmark ( " nu " ) << nu << endl
42 << catchmark ( " t0 " ) << t0 << endl
43 << catchmark ( " sigma " ) << sigma_h ;
44 }

Running the program

Let d = 2 and Ω =] − 1/2, 1/2[2 . We consider the rotating field u = (−x2 , x1 ). A particular
solution of the time-dependent problem with zero right-hand side is given by:
(x1 − x1,c (t))2 + (x2 − x2,c (t))2
   
1 1 + cos(2t) sin(2t)
σ(x, t) = exp −νt − ×
2 r02 sin(2t) 1 − cos(2t)

where x1,c (t) = x̄1,c cos(t) − x̄2,c sin(t) and x2,c (t) = x̄1,c sin(t) + x̄2,c cos(t) with r0 > 0 and
(x̄1,c , x̄2,c ) ∈ R2 . The form h is chosen with σ Γ = σ. This exact solution is implemented in
the file ‘diffusion_transport_tensor_exact.icc’. This file it is not listed here but is available
248 Rheolef version 7.2

in the Rheolef example directory. For the steady problem, the right-hand side could be chosen
∂σ
as χ = − and then t = t0 is fixed. The numerical tests correspond to ν = 3, r0 = 1/10,
∂t
(x̄1,c , x̄2,c ) = (1/4, 0) and a fixed time t0 = π/8.

make diffusion_transport_tensor_dg
mkgeo_grid -t 80 -a -0.5 -b 0.5 -c -0.5 -d 0.5 > square2.geo
./diffusion_transport_tensor_dg square2 P1d > square2.field
field square2.field -comp 00 -elevation
field square2.field -comp 01 -elevation

The computation could also be performed with any Pkd, with k ⩾ 0.

Error analysis

10−2
∥σ h − σ∥L2

10−4
2

3
4

10−6

10−3 10−2 10−1


h

Figure 5.17: Diffusion-diffusion tensor problem: convergence versus mesh size.

The file ‘diffusion_transport_tensor_error_dg.cc’ implement the computation of the error


between the approximate solution σ h and the exact one σ. This file it is not listed here but is
available in the Rheolef example directory. The computation of the error is obtained by:

make diffusion_transport_tensor_error_dg
./diffusion_transport_tensor_error_dg < square2.field

The error is plotted on Fig. 5.17 for various mesh size h and polynomial order k: observe the
optimality of the convergence properties.
Chapter 6

[New] Hybrid discontinuous methods

This chapter is still in active development.

6.1 The Raviart-Thomas element


The aim of this chapter is to introduce to the Raviart-Thomas element Raviart and Thomas [1977]
for building an approximation of the H(div, Ω) space.
There is a subtle issue. The Rheolef implementation choice for this element bases on internal
interpolation nodes instead of moments. This choice leads to more efficient computation of degrees
of freedom, but the standard Lagrange interpolation πh no more satisfies the comutation diagram
and optimal error in the H(div, Ω) norm. Instead of the Lagrange interpolation πh , we propose
a projection operator, that satisfies these desired properties. We start building this projection
operator for a piecewise discontinuous version of this element: it allows one to build a projection
that requires only local operations and converges optimaly. Moreover, the piecewise discontinuous
Raviart-Thomas approximation is used during the post-processing stage of the hybrid discontin-
uous Galekin method, that will be developped in a forthcoming chapter.
Let
n d o
Vh = v h ∈ L2 (Ω) ; v h|K ∈ RTk (K), ∀K ∈ Th
qh ∈ L2 (Ω) ; qh|K ∈ Pk , ∀K ∈ Th

Qh =

Here, Vh represents the space of discontinuous and piecewise k-th order Raviart-Thomas RTk
vector-valued functions while Qh is the space of piecewise discontinuous polynomials.
Let πQh denote the L2 projection from L2 (Ω) into Qh . For all p ∈ L2 (Ω), it is defined as
πQh (p) = ph ∈ Qh , where qh is the solution of the following quadratic minimization problem:
Z
ph = arg inf (p − qh )2 dx
qh ∈Qh Ω

Its solution is characterized as the unique solution of the following linear system, expressed in
variational form:
(P1 ): find ph ∈ Qh such that
Z Z
ph qh dx = p qh dx, ∀qh ∈ Qh
Ω Ω

Following Roberts and Thomas [Roberts and Thomas, 1991, p. 551-552], our aim is to define πVh
as the L2 projection from H(div, Ω) into Vh and satisfying the following commuting property:

249
250 Rheolef version 7.2

div
H(div, Ω) L2 (Ω)
πVh π Qh

div
Vh Qh

i.e.

div(πVh (u)) = πQh (div u), ∀u ∈ H(div, Ω) (6.1a)

In [Roberts and Thomas, 1991, p. 553], theorem 6.3, this projection operator then satisfies an
optimal error bound in the H(div, Ω) norm, i.e.:

∥u − πVh (u)∥0,2,Ω + ∥div(u − πVh (u))∥0,2,Ω = O(hk+1 ) (6.1b)

Remark that the Lagrange interpolation operator πh from H(div, Ω) to Vh do not necessarily satisfy
the commuting property (6.1a). Indeed, this depends upon the way the Raviart-Thomas internal
degrees of freedom are chosen and implemented. When internal degrees of freedom are chosen as
integrals over polynomials of degree ℓ ⩽ k − 1, e.g. as in [Roberts and Thomas, 1991, p. 551],
eqn (6.8), then the Lagrange interpolation πh satisfies both (6.1a) and (6.1b), as shown Roberts
and Thomas [1991], theorem 6.1 and 6.3.
In practice, it is more efficient to choose for all the internal degrees of freedom of the Raviart-
Thomas some values of the function on a set of internal points: this implementation choice has
been chosen in Rheolef. In that case, the Lagrange interpolation πh neither satisfies the com-
mutation (6.1a) nor the bound (6.1b). More precisely, the Lagrange interpolation error is optimal
in L2 norm only while its divergence converges sub-optimally. Thus, with the present choice of the
internal degrees of freedom, there is a need to explicitly compute the projection πVh that satisfies
both (6.1a) and (6.1b).
For all u ∈ H(div, Ω), this projection is defined as the L2 projection of u under the con-
straint (6.1a):

πVh (u) = arg inf ∥u − v h ∥20,2,Ω


v h ∈Vh
subject to div(v v ) = πQh (div u)

Then, uh = πVh (u) ∈ Vh is equivalently characterized as the solution of the following saddle-point
problem:

(uh , ph ) = arg inf arg sup L(v h , qh )


v h ∈Vh qh ∈Qh

where the Lagrangian L is defined, for all (v h , qh ) ∈ Vh × Qh , by


Z
|u − v h |2 + qh div(u − v h ) dx

L(v h , qh ) =

The saddle-point of L is characterized as the unique solution of a linear system, expressed in


variational form. Moreover, since both Vh and Qh are spaces of piecewise discontinuous functions,
the linear system writes as a collection of local linear systems on each element.
(P ): find (uh , ph ) ∈ Vh × Qh such that, on each element K ∈ Th , we have
Z Z
(uh .v h + ph div v h )dx = u.v h dx (6.2a)
K
Z ZK
qh div uh dx = qh div u dx (6.2b)
K K

for all (v h , qh ) ∈ Vh × Qh . Observe that qh represents the Lagrange multiplier associated to the
commutation constraint (6.2b), which is equivalent to (6.1a).
Chapter 6. [New] Hybrid discontinuous methods 251

Let us introduce the following forms:


Z
a(uh , ph ; v h , qh ) = (uh .v h + ph div v h + qh div uh ) dx
ZΩ
ℓ(v h , qh ) = (u.v h + qh div u) dx

The previous problem writes equivalently in abstract form:


(P ): find (uh , ph ) ∈ Vh × Qh such that
a(uh , ph ; v h , qh ) = ℓ(v h , qh ), ∀(v h , qh ) ∈ Vh × Qh
Note that the matrix associated to the bilinear form a is symmetric and block-diagonal: it can thus
be efficiently inverted on the fly at the element level during the assembly process. The following
code implement this efficient approach.

File 6.1: commute_rtd.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " cosinus_vector . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 size_t d = omega . dimension () ,
9 k = ( argc > 2) ? atoi ( argv [2]) : 0;
10 space Vh ( omega , " RT " + to_string ( k )+ " d " ) ,
11 Lh ( omega , " P " + to_string ( k )+ " d " ) ,
12 Xh = Vh * Lh ;
13 trial x ( Xh ); test y ( Xh );
14 auto u = x [0] , lambda = x [1];
15 auto v = y [0] , mu = y [1];
16 integrate_option iopt ;
17 iopt . invert = true ;
18 form inv_a = integrate ( dot (u , v ) + div_h ( v )* lambda + div_h ( u )* mu , iopt );
19 field lh = integrate ( dot ( u_exact ( d ) , v ) + div_u_exact ( d )* mu ) ,
20 xh = inv_a * lh ,
21 p_Vh_u = xh [0] ,
22 pi_h_u = lazy_interpolate ( Vh , u_exact ( d ));
23 dout << catchmark ( " p_Vh_u " ) << p_Vh_u
24 << catchmark ( " pi_h_u " ) << pi_h_u ;
25 }

How to run the program ?


make commute_rtd commute_rtd_error
mkgeo_grid -t 10 > square.geo
./commute_rtd square.geo | ./commute_rtd_error

The program ‘commute_rt.cc’ compute both the projection πVh (u) and the standard Lagrange in-
terpolation πh (u), while ‘commute_rtd_error.cc’ performs the computation of the corresponding
errors. The file ‘cosinus_vector.h’ furnishes the function used for the present test:
 
cos(x0 + 2x1 )
u(x) =
sin(x0 − 2x1 )
These two last files are not listed here but are available in the Rheolef example directory. Observe
on Fig. 6.1 that the error for the projection πVh (u) and its divergence behave as O(hk+1 ), which
is optimal. Conversely, the error for the Lagrange interpolation πh (u) is sub-optimal for the
divergence.
In conclusion, the projection πVh should be used instead of the interpolation πh when we want to
build an optimal Raviart-Thomas approximation.
252 Rheolef version 7.2

1 ∥u − PVh (u)∥0,2,Ω 1 ∥div(u − PVh (u))∥0,2,Ω

1 1

10−5 10−5 2
2

3 3

10−10 10−10
4=k+1 k =0 4=k+1 k =0
k =1 k =1
k =2 k =2
k =3 k =3
10−15 10−15
10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h
1 ∥u − πh (u)∥0,2,Ω 1 ∥div(u − πh (u))∥0,2,Ω

1
1

10−5 2
10−5 2

3 3=k

10−10 10−10
4=k+1 k =0 k =0
k =1 k =1
k =2 k =2
k =3 k =3
10−15 10−15
10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h

Figure 6.1: Raviart-Thomas approximation: πVh projection (top) and πh interpolation (bottom)
errors in L2 norm for the approximation and its divergence.

6.2 Hybrid discontinuous Galerkin (HDG) methods


The aim of this chapter is to introduce to hybridization of discontinuous Galerkin methods
within the Rheolef environment. For a review of hybridizable discontinuous Galerkin methods,
see Nguyen et al. [2011]. The hybridization technique allows an efficient finite element implemen-
tation of many problems of importance, such as the Navier-Stokes one. Let us start by some model
problems.

6.2.1 The Poisson problem


Let us consider the Poisson problem with mixed Dirichlet and Neumann boundary conditions:
(P ): find u, defined in Ω, such that

−∆u = f in Ω
u = gd on Γd
∂u
= gn on Γn
∂n
where ∂Ω = Γd ∪ Γn , and the interior of the two boundary domains Γd and Γn are disjoints. The
Chapter 6. [New] Hybrid discontinuous methods 253

data f , gd and gn are given. Let us introduce the gradient σ = ∇u as an independent variable.
The problem writes equivalently (see e.g. Nguyen et al. [2011]):
(P ): find σ and u, defined in Ω, such that

σ − ∇u = 0 in Ω (6.3a)



 div(σ) = −f in Ω

(6.3b)

 u = gd on Γd (6.3c)

σ.n = gn on Γn (6.3d)

Let us multiply (6.3a) by a test function τ and integrate by parts on any element K:
Z Z
(σ.τ + div(τ ) u) dx − (τ .n) u ds = 0
K ∂K

In the discontinuous Galerkin method, the trace of u on ∂K will be discontinuous across the
inter-element boundaries. The hybridization replaces this trace by a new independent variable,
denoted as λ, that is only definied on sides of the mesh:
Z Z
(σ.τ + div(τ ) u) dx − (τ .n) λ ds = 0 (6.4a)
K ∂K

The λ variable will be uni-valued on boundary inter-elements and accounts strongly for the bound-
ary condition: λ = gd on Γd . Then, let us multiply (6.3b) by a test function v with a zero average
value on K:
Z Z
div(σ) v dx = − f v dx
K K

In order to weakly impose the condition u = λ on ∂K, we add a penalization term:


Z Z Z
div(σ) v dx − βhn hn (u − λ) v ds = − f v dx (6.4b)
K ∂K K

Here, h denotes the local mesh size in the element K. The two constants β > 0 and n ∈ R
are respectively a penalization coefficient and power index. Following Cockburn et al. [2009], we
consider the three cases n = 0, 1 and −1. We have three unknowns σ, u and λ and only the two
equations (6.4a) and (6.4b). For the problem to be complete, we add an equation for λ. Observe
that (6.4b) writes equivalently, after a second integration by part on K:
Z Z Z
n
− σ.∇v dx + (σ.n − βh (u − λ)) v ds = − f v dx
K ∂K K

The quantity involved in the integral on ∂K is denoted as σ b = σ − βhn (u − λ) n and refered


as the numerical flux across inter-element boundaries. As an additional equation, we impose
the continuity of the normal component of this numerical flux. On the Γn boundary domain,
the normal component of the numerical flux is imposed to be the prescribed flux gn , while the
Dirichlet condition λ = gd is precribed on the Γd boundary domain:
(i)
[[σ − βhn (u − λ) n]].n = 0 on S, ∀S ∈ Sh (6.5a)
n
σ.n − βh (u − λ) = gn on Γn (6.5b)
λ = gd on Γd (6.5c)
(i)
For an internal side S = ∂K+ ∩ ∂K− ∈ Sh between two elements K1 , K2 ∈ Th , relation (6.5a)
writes:

σ + .n+ − βhn (u+ − λ) + σ − .n− − βhn (u− − λ) = 0 on S


254 Rheolef version 7.2

where σ ± and u± are the trace on S of σ and u in K± and n± are the outer normal of K± on S.
Since S is oriented, let us choose without loss of generality n = n− = −n+ . Then, the previous
relation writes:

(σ − − σ + ) .n − βhn (u− + u+ ) + 2βhn λ = 0 on S


⇐⇒ [[σ]].n − 2βhn ({{u}} − λ) = 0 on S

where we have used the jump and average across the internal side S. The previous relation,
together with (6.5c) and (6.5c), writes in variational form:
Z Z Z
([[σ]].n − 2βhn ({{u}} − λ)) µ ds + (σ.n − βhn (u − λ)) µ ds = gn µ ds (6.6)
(i)
Sh ∂Ω Γn

(i)
for all test function µ, defined on all internal sides of Sh and on all sides of the boundary domain
Γn and that vanishes on all sides of the boundary domain Γd .
Grouping (6.4a), (6.4b) and (6.6), we obtain the discrete variational formulation:
(F V )h : find (σ h , uh , λh ) ∈ Th × Xh × Mh (gd ) such that
Z Z Z Z
σ.τ dx + divh (τ ) u dx − ([[τ ]].n) λ ds − (τ .n) λ ds = 0
(i)
Ω Ω Sh ∂Ω
Z X Z Z Z Z
divh (σ) v dx − β hn u v ds + 2βhn {{v}} λ ds + βhn v λ ds = − f v dx
(i)
Ω K∈Th ∂K Sh ∂Ω Ω
Z Z Z Z Z
(2βhn {{u}} − [[σ]].n) µ ds + (βhn u − σ.n) µ ds − 2βhn λ µ ds − βhn λ µ ds = − gn µ ds
(i) (i)
Sh ∂Ω Sh ∂Ω Γn

for all (τ h , vh , τh ) ∈ Th × Xh × Mh (0), where


n d o
Th = τ ∈ L2 (Ω) ; τ |K ∈ (Pk )d , ∀K ∈ Th
v ∈ L2 (Ω) ; v|K ∈ Pk , ∀K ∈ Th

Xh =
µ ∈ L2 (Sh ) ; µ|S ∈ Pk , ∀S ∈ Sh

Mh =

Mh (gd ) = µ ∈ Mh ; µ|S = πh (gd ), ∀S ⊂ Γd

and k ⩾ 0 is the polynomial degree. This is a symmetric system with a mixed structure. Let:
Z X Z
ah ((σ, u), (τ , v)) = (σ.τ + divh (τ ) u + divh (σ) v) dx − β hn u v ds
Ω K∈Th ∂K
Z Z
bh ((τ , v), µ) = (−[[τ ]].n + 2βhn {{v}}) µ ds + (−τ .n + βhn {{v}}) µ ds
(i)
Sh ∂Ω
Z Z
ch (λ, µ) = 2βhn λ µ ds + βhn λ µ ds
(i)
Sh ∂Ω
Z
ℓh (τ , v) = − f v dx

Z
kh (µ) = − gn µ ds
Γn

Then, the variational formulation writes equivalently:


(F V )h : find (σ h , uh , λh ) ∈ Th × Xh × Mh (gd ) such that

ah ((σ, u), (τ , v)) + bh ((τ , v), λ) = ℓh (τ , v)


bh ((σ, u), µ) − ch (λ, µ) = kh (µ)
Chapter 6. [New] Hybrid discontinuous methods 255

for all (τ h , vh , τh ) ∈ Th × Xh × Mh (0). Let χh = (σ h , uh ). This linear symmetric system admits


the following matrix structure:
A BT
    
χh F
=
B −C λh G
A carreful study shows that the A matrix has a block-diagonal structure at the element level
and can be easily inverted on the fly during the assembly process. The matrix structure writes
equivalently:
Aχh + B T λh = F

⇐⇒
Bχh − Cλh = G
χh = A−1 (F − B T λh )

⇐⇒
(C + BA−1 B T )λh = BA−1 F − G
The second equation is solved first: the only remaining unknown is the Lagrange multiplier λh , in a
linear system involving the Schur complement matrix S = C + BA−1 B T . Then, the two variables
χh = (σ h , uh ) are simply obtained by a direct computation.

File 6.2: dirichlet_hdg.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sinusprod_dirichlet . h "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 geo omega ( argv [1]);
8 string approx = ( argc > 2) ? argv [2] : " P1d " ;
9 Float n = ( argc > 3) ? atof ( argv [3]) : 1;
10 Float beta = ( argc > 4) ? atof ( argv [4]) : 1;
11 space Th ( omega , approx , " vector " ) ,
12 Xh ( omega , approx ) ,
13 Yh = Th * Xh ,
14 Mh ( omega [ " sides " ] , approx );
15 Mh . block ( " boundary " );
16 space Wh ( Mh . get_geo ()[ " boundary " ] , approx );
17 size_t d = omega . dimension ();
18 size_t k = Xh . degree ();
19 trial x ( Yh ) , lambda ( Mh );
20 test y ( Yh ) , mu ( Mh );
21 auto sigma = x [0] , u = x [1];
22 auto tau = y [0] , v = y [1];
23 integrate_option iopt ;
24 iopt . invert = true ;
25 auto coef = beta * pow ( h_local () , n );
26 form inv_a = integrate ( dot ( sigma , tau ) + u * div_h ( tau ) + v * div_h ( sigma )
27 - on_local_sides ( coef * u * v ) , iopt );
28 form b = integrate ( " internal_sides " ,
29 ( - dot ( jump ( sigma ) , normal ()) + 2* coef * average ( u ))* mu )
30 + integrate ( " boundary " , ( - dot ( sigma , normal ()) + coef * u )* mu );
31 form c = integrate ( " internal_sides " , 2* coef * lambda * mu )
32 + integrate ( " boundary " , coef * lambda * mu );
33 field lh = integrate ( - f ( d )* v );
34 field kh ( Mh ,0) , lambda_h ( Mh ,0);
35 lambda_h [ " boundary " ] = lazy_interpolate ( Wh , g ( d ));
36 form s = c + b * inv_a * trans ( b );
37 field rh = b *( inv_a * lh ) - kh ;
38 problem p ( s );
39 p . solve ( rh , lambda_h );
40 field xh = inv_a *( lh - b . trans_mult ( lambda_h ));
41 dout << catchmark ( " n " ) << n << endl
42 << catchmark ( " beta " ) << beta << endl
43 << catchmark ( " u " ) << xh [1]
44 << catchmark ( " lambda " ) << lambda_h
45 << catchmark ( " sigma " ) << xh [0];
46 }
256 Rheolef version 7.2

∥u − uh ∥0,2,Ω ∥u − uh ∥0,2,Ω ∥u − uh ∥0,2,Ω


1 1 1

10−2 10−2 1 10−2


1

10−4 10−4 10−4


2
2 2
10−6 10−6 10−6
3
10−8 3=k n=1 10−8 n=0 10−8 3 n = −1
k=0 k=0 k=0
k=1 k=1 k=1
10−10 k=2 10−10 4=k+1
k=2 10−10 4=k+1 k=2
k=3 k=3 k=3
10−12 10−12 10−12
10−3 10−2 10−1 1 10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h h
∥∇u − σ h ∥0,2,Ω ∥∇u − σ h ∥0,2,Ω ∥∇u − σ h ∥0,2,Ω
1 1 1

10−2 1 10−2 1 10−2


1
10−4 2
10−4 2
10−4
2
10−6 10−6 10−6
3 3
10−8 n=1 10−8 n=0 10−8 3=k
n = −1
k=0 k=0 k=0
k=1 k=1 k=1
10−10 4=k+1
k=2 10−10 4=k+1
k=2 10−10 k=2
k=3 k=3 k=3
10−12 10−12 10−12
10−3 10−2 10−1 1 10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h h

Figure 6.2: Hybrid discontinuous Galerkin method: Poisson problem with Dirichlet boundary
conditions in 2D geometry. Convergence vs h and k for the approximation of the solution uh and
of its gradient σ h . (left) n = 1 ; (center) n = 0 ; (right) n = −1.

The right-hand-side f and the Dirichlet boundary condition g has been chosen such that the exact
solution is given by:
d−1
Y
u(x) = sin(πxi )
i=0

The files ‘sinusprod_dirichlet.h’, that defines the data f and g,


and ‘sinusprod_error_hdg.cc’, that compute the error in various norms, are not listed
here but are available in the Rheolef example directory.

How to run the program

The compilation and run write:

make dirichlet_hdg
mkgeo_grid -t 10 > square.geo
./dirichlet_hdg square.geo P1d > square.field
field square.field -elevation
field square.field -elevation -mark lambda
field square.field -velocity -mark sigma
make sinusprod_error_hdg
./sinusprod_error_hdg < square.field

Fig. 6.2 plots the errors vs h and k for a two dimensional geometry. Observe that the approxima-
tion uh converges optimaly, as hk+1 , in the L2 norm for any k ⩾ 0 when the power index n = 1.
When n = 1, the convergence is suboptimal, as hk only and note that the lowest order approxima-
tion k = 0 is not convergent. When n = −1, the convergence is optimal, as hk+1 , only when k ⩾ 1,
Chapter 6. [New] Hybrid discontinuous methods 257

while the lowest order approximation k = 0 is not convergent. The approximation σ h of the gradi-
ent converges optimaly, as hk+1 , for both n = 0 and 1, while it is suboptimal, as hk , when n = −1.
All these results are consistent with the approxiamtion theory of the HDG method, see Cockburn
et al. [2009] and tables 2, 3, 6.

6.2.2 Superconvergence of the Lagrange multiplier

1 1 1
∥πMh (u) − λh ∥0,2,Sh ∥πMh (u) − λh ∥0,2,Sh ∥πMh (u) − λh ∥0,2,Sh
1
10−4 10−4 10−4
2
2

3 3
10−8 10−8 10−8
4 3
5=k+2
4 4=k+1
n=1 n=0 n = −1
10−12 k=0 10−12 k=0 10−12 k=0
k=1 5=k+2 k=1 k=1
k=2 k=2 k=2
k=3 k=3 k=3
10−16 10−16 10−16
10−3 10−2 10−1 1 10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h h

Figure 6.3: Hybrid discontinuous Galerkin method: Poisson problem with Dirichlet boundary
conditions in 2D geometry. Super-convergence vs h and k for the Lagrange multiplier λh to the
L2 projection on Mh of the exact solution. (left) n = 1 ; (center) n = 0 ; (right) n = −1.

Let πMh denote the L2 projection from L2 (Sh ) into Mh . We consider the special case of computing
the projection πMh (u) ∈ Mh of the restriction to Sh of an element u ∈ L2 (Sh ). It is defined as
Z
1 X
πMh (u) = arg inf (u − µh )2 ds
µh ∈Mh 2 S S∈Sh

The following bilinear forms are introduced:


X Z
ms (λ, µ) = λ µ ds
S∈Sh S
X Z
kh (µ) = u µ ds
S∈Sh S

Then, the projection reduces to:


find λ̄h = πMh (u) ∈ Mh such that
ms (λ̄h , µh ) = kh (µh ), ∀µh ∈ Mh
The result of projection operator can be obtained by a resolution of a linear system. Follow-
ing [Cockburn et al., 2008, p. 1896], in order to measure the error on the set of sides Sh of the
mesh, we introduce the mesh-dependent norm ∥.∥0,2,Sh , defined for all µ ∈ L2 (Sh ) by
X Z
∥µ∥20,2,Sh = hS µ2 ds
S∈Sh S

where hS is a characteristic length on the side S ∈ Sh . Fig. 6.3 plots the difference πMh (u) − λh
in this mesh-dependent norm. In agrement with the theoretical results [Cockburn et al., 2008,
p. 1896], we observe the superconvergence of the multiplier λh to the L2 projection πMh (u). More
precisely, when n = 1 the order of convergence is k + 2 for any k ⩾ 0. When n = 0, the
superconvergence occurs only when k ⩾ 1 and when n = −1 there is no superconvergence. This
error is also computed by the code ‘sinusprod_error_hdg.cc’.
258 Rheolef version 7.2

6.2.3 Superconvergence of the piecewise averaged solution

File 6.3: dirichlet_hdg_average.icc


1 field d irichl et_hd g_aver age ( field uh , field lambda_h ) {
2 size_t k = uh . get_space (). degree ();
3 size_t d = uh . get_geo (). dimension ();
4 space Zh ( uh . get_geo () , " P0 " );
5 trial zeta ( Zh ); test xi ( Zh );
6 integrate_option iopt ;
7 iopt . invert = true ;
8 if ( k >= 1) {
9 form inv_mz = integrate ( zeta * xi , iopt );
10 iopt . set_order (2* k +2);
11 field lh = integrate ( uh * xi , iopt );
12 return inv_mz * lh ;
13 }
14 const space & Mh = lambda_h . get_space ();
15 trial lambda ( Mh ); test mu ( Mh );
16 form inv_ms = integrate ( " sides " , lambda * mu , iopt );
17 field inv_sh = inv_ms * field ( Mh ,1);
18 field lh = integrate ( on_local_sides ( inv_sh * lambda_h * xi ));
19 return (1./( d +1))* lh ;
20 }

File 6.4: dirichlet_hdg_average.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " dir ichle t_hdg_ avera ge . icc "
5 int main ( int argc , char ** argv ) {
6 environment rheolef ( argc , argv );
7 field uh , lambda_h ;
8 din >> catchmark ( " u " ) >> uh
9 >> catchmark ( " lambda " ) >> lambda_h ;
10 field bar_uh = diric hlet_ hdg_av erage ( uh , lambda_h );
11 dout << catchmark ( " u " ) << uh
12 << catchmark ( " bar_u " ) << bar_uh ;
13 }

∥π̄h (u) − ūh ∥0,2,Ω ∥π̄h (u) − ūh ∥0,2,Ω ∥π̄h (u) − ūh ∥0,2,Ω
1 1 1

1
10−4 10−4 10−4
2 2

10−8 10−8 3 10−8


3
3
n=1 n=0 4
5 6=k+2
10−12 4
6=k+2 10−12 10−12 n = −1 5=k+1
k =0 k =0 k=0
k =1 k =1 k=1
10−16 k =2 10−16 k =2 10−16 k=2
k =3 k =3 k=3
k =4 k =4 k=4
10−3 10−2 10−1 1 10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h h

Figure 6.4: Hybrid discontinuous Galerkin method: Poisson problem with Dirichlet boundary
conditions in 2D geometry. Super-convergence vs h and k for the piecewise average ūh of the
approximate solution. (left) n = 1 ; (center) n = 0 ; (right) n = −1.

The superconvergence of the piecewise average values of the solution obtained by the hy-
brid discontinuous Galerkin method was first observed in Cockburn et al. [2008, 2009] and
then exploited for many postprocessing application (see e.g. Nguyen et al. [2011]). The file
‘dirichlet_hdg_average.icc’ compute ūh = π̄h (uh , λh ), the averaged solution, defined by
Chapter 6. [New] Hybrid discontinuous methods 259

(see Cockburn et al. [2008], eqn (2.9b)):


1 X


 λh when k = 0
 d S⊂∂K


π̄h (uh , λh ) =
Z
1


uh dx when k ⩾ 1



meas(K) K
The file ‘sinusprod_error_hdg_average.cc’ that compute the error ūh − π̄h (u) is not listed here
but is available in the Rheolef example directory. The computation of the error is obtained by:

make dirichlet_hdg dirichlet_hdg_average sinusprod_error_hdg_average


mkgeo_grid -t 10 > square.geo
./dirichlet_hdg square.geo P1d | dirichlet_hdg_average | \
./sinusprod_error_hdg_average

Observe on Fig. 6.4 that, when n = 1 and for any k ⩾ 0, we obtain ūh − π̄h (u) of order k + 2. This
is a remarkable result since uh is piecewise polynomial of order k and is expected to converge at
order k + 1. When n = 0, for any k ⩾ 1 we also observe this superconvergence while, when k = 0,
the average value converges only at first order. Finaly, when n = −1, for any k ⩾ 1 we do no
more observe These observations are consistent with those of [Cockburn et al., 2009, p. 16] (see
also [Cockburn et al., 2008, p. 1613]).

6.2.4 Improving the solution by local postprocessing


By combining the approximation σ h of the gradient ∇u, that converges at rate k + 1 (see Fig. 6.2),
with the average ūh that super-converges at rate k + 2 (see Fig. 6.4), it is then possible, by a local
integration inside each element, to obtain a new approximation u∗h , that is piecewise discontinuous
polynomial of order k + 1, and that converges at rate k + 2.
The postprocess step is nothing than the resolution of the following local Neumann problem in
any element K ∈ Th (see [Cockburn et al., 2010b, p. 1360], eqn (5.1)):
(P ∗ ): find u∗ , defined in K, such that


 −∆u∗ = f in K

∂u∗


= σ h .n on ∂K

 Z ∂n Z

 u∗ dx =

 ūh dx

K K

where σ h and ūh are given by the previous resolution.


For any β ∈ R, let us introduce the following functional space:
 Z 
∗ 1
XK (β) = v ∈ H (K) ; v dx = β
K

Then, the variational formulation of the problem writes:


(F V ∗ ): find u∗ ∈ XK K ūh dx such that
R 

Z Z Z
∇u∗ .∇v ∗ dx = f v ∗ dx + σ h .n v ∗ ds, ∀v ∗ ∈ XK (0)
K K ∂K

The linear constraint for the imposition of the average value is not easy to impose in XK (β).
Indeed, XK (β) is not a vectorial space when β ̸= 0. Following the methodology previously intro-
duced for the Neumann boundary conditions for the Laplace operator, we introduce a Lagrange
multiplier denoted here as ζ, which is constant inside each element K.
260 Rheolef version 7.2

Finally, the postprocessing step compute the approximation u∗h , when σ h and uh are known,as:
(F V ∗ )h : find (u∗h , ζh ) ∈ Xh∗ × Zh such that, on each element K ∈ Th , we have

Z Z Z Z
∗ ∗ ∗ ∗
∇u σ h .n vh∗ ds, ∀vh∗ ∈ Xh∗



 h .∇v h dx + v ζ
h h dx = f v h dx +
K K
Z ZK ∂K

ūh ξh dx, ∀ξh ∈ Xh∗



 uh ξh dx =
K K

where

Xh∗ v ∈ L2 (Ω) ; v|K ∈ Pk+1 , ∀K ∈ Th



=
ξ ∈ L2 (Ω) ; ξ|K ∈ P0 , ∀K ∈ Th

Zh =

Let

Z
a∗h (u, ζ; v, ξ) = (∇h u.∇h v + v ζ + u ξ) dx
ZΩ X Z
ℓ∗h (v, ξ) = (f v + ūh ξ) dx + (σ h .n) v ds
Ω K∈Th ∂K

The second step writes in this abstract setting:


(F V ∗ )h : find (u∗h , ζh∗ ) ∈ Xh∗ × Zh such that

a∗h (u∗h , ζh ; vh∗ , ξh ) = ℓ∗h (vh∗ , ξh ), ∀(vh∗ , ξh ) ∈ Xh∗ × Zh

Note that the matrix associated to the bilinear form a∗h is symmetric and block-diagonal: it can
thus be easily be inverted on the fly at the element level. The present postprocessing stage was
first introduced in [Cockburn et al., 2010b, p. 1360], eqn (5.1) as a replacement and a simplification
of those previously introduced in Cockburn et al. [2008, 2009]. The following code implement this
postprocessing.
Chapter 6. [New] Hybrid discontinuous methods 261

File 6.5: dirichlet_hdg_post.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sinusprod_dirichlet . h "
5 # include " dir ichle t_hdg_ avera ge . icc "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 Float n , beta ;
9 field uh , lambda_h , sigma_h ;
10 din >> catchmark ( " n " ) >> n
11 >> catchmark ( " beta " ) >> beta
12 >> catchmark ( " u " ) >> uh
13 >> catchmark ( " lambda " ) >> lambda_h
14 >> catchmark ( " sigma " ) >> sigma_h ;
15 field bar_uh = diric hlet_ hdg_av erage ( uh , lambda_h );
16 const geo & omega = uh . get_geo ();
17 size_t d = omega . dimension ();
18 size_t k = uh . get_space (). degree ();
19 space Xhs ( omega , " P " + to_string ( k +1)+ " d " ) ,
20 Zhs ( omega , " P0 " ) ,
21 Yhs = Xhs * Zhs ;
22 trial x ( Yhs ); test y ( Yhs );
23 auto us = x [0] , zeta = x [1];
24 auto vs = y [0] , xi = y [1];
25 integrate_option iopt ;
26 iopt . invert = true ;
27 form inv_ahs = integrate ( dot ( grad_h ( us ) , grad_h ( vs )) + zeta * vs + xi * us , iopt );
28 field lhs = integrate ( f ( d )* vs + xi * bar_uh
29 + on_local_sides ( dot ( sigma_h , normal ())* vs ));
30 field xhs = inv_ahs * lhs ;
31 dout << catchmark ( " n " ) << n << endl
32 << catchmark ( " beta " ) << beta << endl
33 << catchmark ( " u " ) << xhs [0]
34 << catchmark ( " lambda " ) << lambda_h
35 << catchmark ( " sigma " ) << sigma_h
36 << catchmark ( " zeta " ) << xhs [1];
37 }

1 1 1
∥u − u∗h ∥0,2,Ω ∥u − u∗h ∥0,2,Ω ∥u − u∗h ∥0,2,Ω

1
10−4 10−4 10−4
2
2

3 3
10−8 10−8 10−8 3

4 n=1 4 n=0 n = −1
k=0 k=0 4=k+1 k=0
k=1 k=1 k=1
10−12 5=k+2 k=2 10−12 5=k+2 k=2 10−12 k=2
k=3 k=3 k=3
10−3 10−2 10−1 1 10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h h

Figure 6.5: Solution post-processing of the hybrid discontinuous Galerkin method: Poisson prob-
lem with Dirichlet boundary conditions in 2D geometry. Convergence vs h and k for the post-
treated approximate solution. (left) n = 1 ; (center) n = 0 ; (right) n = −1.

How to run the program

The compilation and run write:

make dirichlet_hdg dirichlet_hdg_post ./sinusprod_error_hdg


mkgeo_grid -t 10 > square.geo
262 Rheolef version 7.2

./dirichlet_hdg square.geo P1d > square.field


./dirichlet_hdg_post < square.field > square-post.field
field square-post.field -elevation

Note the dramatic improvement of the post-treated solution, when compared with the original
one, that could be observed with:

field square.field -elevation

This improvement could be accurately quantified by the error analysis:

./sinusprod_error_hdg < square.field


./sinusprod_error_hdg < square-post.field

The results are shown on Fig. 6.5. When n = 1, observe that, for any k ⩾ 0, the error for the
post-treated solution u∗h converges to zero at rate k + 2 in L2 norm, which is optimal, since u∗h is
a piecewise k + 1 degree polynomial. When n = 0, this result is obtained only for k ⩾ 1 while,
when n = −1, the convergence is suboptimal.

6.2.5 Improving the gradient with the Raviart-Thomas element


Let (σ h , uh , λh ) be the solution of problem (F V )h . This solution is considered here as known.
Observe that the normal component of the numerical flux σ b h .n = σ h .n − βhn (uh − λh ) is con-
tinuous across any inter-element boundaries. Indeed, relation (6.5a) is imposed weakely in the
variational formulation (F V )h , with a Lagrange multiplier µh ∈ Mh which is piecewise polyno-
mial of degree k. Since σ b h is also piecewise polynomial of degree k, the discrete version of (6.5a)
is also satisfied strongly:
(i)
b h .n = [[σ h − βhn (uh − λh ) n]].n
σ = 0 on S, ∀S ∈ Sh

Since any element of H(div, Ω) presents the continuity of its normal component across any inter-
element boundaries, is possible to define a new H(div, Ω) conform approximation of σ, denoted as
σ̃ h , which satisfies σ
e h .n = σ b h .n on any internal sides. Similarly to (6.5a), σ̃ h also should satisfy
a discrete version of (6.5c)-(6.5c) i.e. σ b h .n is equals to gn on Γn and to σ h .n − βhn (uh − gd )
on Γd . It is characterized as the unique element σ̃ h ∈ T̃h satisfying in any element K ∈ Th the
following local variational formulation [Cockburn et al., 2010b, p. 1360]:
Z Z
σ̃ h .γ̃ h dx = σ h .γ̃ h dx, ∀γ̃ h ∈ G̃h
K
Z ZK
(σ̃ h .n) µ̃h ds = (σ h .n − βhn (uh − λh )) µ̃h ds, ∀µ̃h ∈ M̃h
∂K ∂K

where we have introduced the finite dimensional spaces


n d o
T̃h = τ̃ h ∈ L2 (Ω) ; τ̃ h|K ∈ RTk (K), ∀K ∈ Th
n d o
W̃h = γ̃ h ∈ L2 (Ω) ; γ̃ h|K ∈ Pk−1 (K), ∀K ∈ Th
Y  Y Y
M̃h = τ̃ |∂K .n ; τ̃ ∈ RTk (K) = Pk (S) (6.7)
K∈Th K∈Th S⊂∂K

The space T̃h contains discontinuous and piecewise k-th order Raviart-Thomas RTk polynomial
vector-valued functions. The space M̃h contains, inside each element, the normal trace of functions
of T̃h . Note that elements of M̃h are piecewise Pk on all sides S ⊂ ∂K. Moreover, for all internal
(i)
side S ∈ Sh , the elements of M̃h are bi-valued, due to the discontinuity of elements of τ̃ ∈ T̃h .
Chapter 6. [New] Hybrid discontinuous methods 263

Finally, W̃h contains piecewise discontinuous polynomial of degree k−1. By convention, when k = 0,
then the space W̃h is empty. Observe that dim(T̃h ) = dim(W̃h ) + dim(M̃h ) and that the previous
local variational problem is well-posed. Let
Z X X Z
ãh (σ̃ h ; [γ̃ h , µ̃h ]) = σ̃ h .γ̃ h dx + (σ̃ h .n) µ̃h ds
Ω K∈Th S⊂∂K S
Z X Z
ℓ̃h ([γ̃ h , µ̃h ]) = σ h .γ̃ h dx + (σ h .n − βhn (uh − λh )) µ̃h ds
Ω K∈Th ∂K

For known (σ h , uh , λh ), the postprocessing of the gradient writes:


(F
g V )h : find σ̃ h ∈ T̃h such that ãh (σ̃ h , [γ̃ h , µ̃h ]) = ℓ̃h ([γ̃ h , µ̃h ]) for all (γ̃ h , µ̃h ) ∈ W̃h × M̃h .
A carreful study shows that the matrix associated with the ãh bilinear form has a block-diagonal
structure at the element level and can be efficiently inverted on the fly during the assembly process.
This property is due to the usage of discontinuous Raviart-Thomas approximation space T̃h . More-
over, since the normal component of σ̃ h is equal to the numerical flux σ b h .n which is continuous
across inter-element boundaries this is also the case for σ̃ h .n and finally σ̃ h ∈ H(div, Ω).

File 6.6: dirichlet_hdg_post_rt.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 int main ( int argc , char ** argv ) {
5 environment rheolef ( argc , argv );
6 Float n , beta ;
7 field sigma_h , uh , lambda_h ;
8 din >> catchmark ( " n " ) >> n
9 >> catchmark ( " beta " ) >> beta
10 >> catchmark ( " u " ) >> uh
11 >> catchmark ( " lambda " ) >> lambda_h
12 >> catchmark ( " sigma " ) >> sigma_h ;
13 const geo & omega = uh . get_geo ();
14 size_t d = omega . dimension ();
15 size_t k = uh . get_space (). degree ();
16 string approx = ( k == 0) ? " empty " : " P " + to_string (k -1)+ " d " ;
17 space Tht ( omega , " RT " + to_string ( k )+ " d " );
18 space Wht ( omega , approx , " vector " );
19 space Mht ( omega , " trace_n ( RT " + to_string ( k )+ " d ) " );
20 space Sht = Wht * Mht ;
21 trial sigma_t ( Tht ); test tau ( Sht );
22 auto tau_internal = tau [0] , tau_n = tau [1];
23 auto coef = beta * pow ( h_local () , n );
24 form aht = integrate ( dot ( sigma_t , tau_internal )
25 + on_local_sides ( dot ( sigma_t , normal ())* tau_n ));
26 field lht = integrate ( dot ( sigma_h , tau_internal )
27 + on_local_sides (( dot ( sigma_h , normal ())
28 + coef *( lambda_h - uh ))* tau_n ));
29 field sigma_ht ( Tht );
30 problem p ( aht );
31 p . solve ( lht , sigma_ht );
32 dout << catchmark ( " n " ) << n << endl
33 << catchmark ( " beta " ) << beta << endl
34 << catchmark ( " u " ) << uh
35 << catchmark ( " lambda " ) << lambda_h
36 << catchmark ( " sigma " ) << sigma_h
37 << catchmark ( " sigmat " ) << sigma_ht ;
38 }

How to run the program

The compilation and run write:

make dirichlet_hdg dirichlet_hdg_post_rt


264 Rheolef version 7.2

∥∇u − σ̃ h ∥div,2,Ω ∥∇u − σ̃ h ∥div,2,Ω ∥∇u − σ̃ h ∥div,2,Ω


1 1 1
1 1
10−2 10−2 10−2
1
10−4 2 10−4 2 10−4
2=k
10−6 10−6 10−6
3 3

10−8 n=1 10−8 n=0 10−8 n = −1


3
4=k+1
k=0 4=k+1
k=0 k=0
k=1 k=1 k=1
10−10 k=2 10−10 k=2 10−10 k=2
k=3 k=3 k=3
10−12 10−12 10−12
10−3 10−2 10−1 1 10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h h

Figure 6.6: Solution post-processing of the hybrid discontinuous Galerkin method: Poisson prob-
lem with Dirichlet boundary conditions in 2D geometry. Convergence vs h and k for the post-
treated gradient σ̃ h approximate solution. (left) n = 1 ; (center) n = 0 ; (right) n = −1.

mkgeo_grid -t 10 > square.geo


./dirichlet_hdg square.geo P1d > square.field
./dirichlet_hdg_post_rt < square.field > square-rt.field
field square-rt.field -velocity -mark sigmat
make sinusprod_error_hdg_post_rt
./sinusprod_error_hdg_post_rt < square-rt.field

Fig. 6.6 plots the errors vs h and k for a two dimensional geometry. Observe that, when n = 1,
the approximation σ̃ h of the gradient converges to ∇u with a k + 1 rate in H(div norm, with:

∥τ ∥2div,2,Ω = ∥τ ∥20,2,Ω + ∥div(τ )∥20,2,Ω

for any τ ∈ H(div, Ω). It means that div(σ̃ h ) converges to ∆u with a k + 1 rate in both L2
norm. This is optimal with respect to the classical interpolation results for the k-th order Raviart-
Thomas element (see section 6.1, page 249). When n = 0, the convergence is also optimal while,
when n = −1 it is suboptimal.

n uh σh u∗h σ̃ h
1 k k+1  k+2 k+1
1, k =0
0 k+1 k+1 k+1
  k + 2, k ⩾0
0, k=0 0, k =0
−1 k k
k + 1, k ⩾ 0 k + 1, k ⩾0

Table 6.1: Hybrid discontinuous Galerkin method: convergence order versus k for n ∈ {1, 0, −1}.

Fig. 6.1 summarizes the convergence orders versus the mesh size h in terms of k and n. Observe
that n = 1 allows one to obtain an optimal convergence of both the the post-processed solution u∗h
and its gradient σ̃ h for any polynomial degree k ⩾ 0. When n = 0, it is optimal only for k ⩾ 1
while when n = −1 it is suboptimal. Thus the HDG method and its postprocessed stage could
be considered as a whole. It allows, by solving a linear system for the k-th piecewise polynomial
approximation of the Lagrange multiplier only, to obtain a k+2 order approximation of the solution
in L2 and a k+1 approximation of its gradient in H(div).
Chapter 6. [New] Hybrid discontinuous methods 265

6.3 Hybrid high order (HHO) methods


The aim of this chapter is to introduce to hybrid high order (HHO) methods within the Rheolef
environment. For a review of hybrid high order methods, and its link to hybrid discontinuous
Galerkin (HDG) methods, see Cockburn et al. [2016]. Let us start by some model problems.

6.3.1 The diffusion problem


Let us consider an anisotropic diffusion problem with homogeneous Dirichlet conditions:
(P ): find u, defined in Ω, such that

−div(A∇u) = f in Ω (6.8a)


u = 0 on ∂Ω (6.8b)
where the right-hand-side f and the diffusion A are given functions. The diffusion is assumed
to be bounded, symmetric, uniformly positive definite matrix-valued function. Observe that
when A = I, the problem reduces to the usual Poisson problem with homogeneous Dirichlet
conditions.

6.3.2 The reconstruction operator


The cornerstone of the HHO method is the reconstruction operator. Let k ⩾ 0
and ℓ ∈ {k−1, k, k+1} be two integers and let us introduce the following finite element spaces:
vh ∈ L2 (Ω) ; vh|K ∈ Pℓ , ∀K ∈ Th

Xh =
µh ∈ L2 (Sh ) ; µh|S ∈ Pk , ∀S ∈ Sh

Mh =
n o
Xh∗ = vh∗ ∈ L2 (Ω) ; vh|K

∈ Pk+1 , ∀K ∈ Th

The reconstruction operator [Cockburn et al., 2016, p. 637] is defined by


rh : Xh × Mh −→ Xh∗
(uh , λh ) 7−→ u∗h = rh (uh , λh )
where u∗h ∈ Xh∗ is characterized by a collection of local constrained minimization problems, asso-
ciated to local Neumann problems similar to those previously introduced for the HDG method, in
subsection 6.2.4, page 259:
Z X Z
1
u∗h = arg inf |∇h (vh∗ − uh )|2A dx − (λh − uh ) (A∇vh∗ ) .n ds
∗ ∈X ∗ 2
vh h Ω K∈Th ∂K
Z Z
subject to vh∗ dx = uh dx, ∀K ∈ Th
K K

For any symmetric definite positive matrix c, we denote by |.|c the anisotropic norm in Rd defined
by |ξ|2c = ξ.(cξ) for all ξ ∈ Rd . The constraint corresponds to the closure for the local Neumann
problems, as otherwise the solution would be defined up to a constant in each element ∀K ∈ Th .
This constrained minimization problem is not convenient for the numerical resolution: we prefer
to express it as a saddle-point problem by introducing a Lagrange multiplier ζh ∈ Zh with
ξh ∈ L2 (Ω) ; ξh|K ∈ P0 , ∀K ∈ Th

Zh =
The corresponding Lagrangian writes
Z X Z
1
Lγ (u∗ , ζ) = |∇h (u∗ − uh )|2A dx − (λh − uh ) (A∇u∗ ) .n ds
2 Ω ∂K
K∈Th
Z Z
γ 2
+ (u∗ − uh ) ζ dx − ζ dx (6.9)
Ω Ω 2
266 Rheolef version 7.2

Observe that the first two terms of the Lagrangian correspond to the minimization functional. The
third term is associated to the constraint with its associated Lagrangian multiplier. When γ = 0,
the fourth term vanishes and thus, the saddle-point of the Lagrangian is equivalent to the previous
constrained minimization problem. The fourth term, when the augmentation parameter γ > 0
will be discussed later: in that case, Lγ is related to the augmented Lagrangian. The saddle-point
problem expresses:

(u∗h , ζh ) = arg inf sup Lγ (vh∗ , ξh )


∗ ∈X ∗ ξ Z
vh h h h

Since Lγ is differentiable, its saddle point is also characterized as the unique solution of the
following collection of local variational problems:
(F V ∗ )h : let (uh , λh ) ∈ Xh × Mh being given, find (u∗h , ζh ) ∈ Xh∗ × Zh such that
Z Z Z Z
∗ ∗ ∗ ∗
∇u ∇u (λh − uh ) (A∇vh∗ ) .n ds



 h . (A∇v h ) dx + v ζ
h h dx = h . (A∇v h ) dx +
K
Z Z K ZK ∂K

uh ξh dx −



 γζh ξh dx = uh ξh dx
K K K

for all K ∈ Th and all (vh∗ , ξh ) ∈ Xh∗ × Zh . Observe that, for any K ∈ Th , we can choose the test
function vh∗ as equal to 1 in K and zero elsewhere in Ω. Then, the first equation leads to ζh = 0
in K. Since this could be repeated for any K ∈ Th , we obtain ζh = 0. As a consequence, the
term factored by γ ⩾ 0 has no influence upon the solution (u∗h , ζh ) which is independent upon γ:
in this sense, this is not a penalization parameter, but an augmented Lagrangian parameter. Up
to now, we assume γ > 0: we will show how it facilitates the numerical resolution of the system.
Let, for all u∗ , v ∈ Xh∗ , ζ, ξ ∈ Zh∗ , u ∈ Xh and λ ∈ Mh :
Z
a∗ (u∗ , v ∗ ) = ∇h u∗ . (A∇h v ∗ ) dx (6.10a)

Z
b∗ (u∗ , ξ) = u∗ ξ dx (6.10b)

Z
c∗ (ζ, ξ) = γ ζ ξ dx (6.10c)

Z X Z
ǎ(u, v ∗ ) = ∇h u. (A∇h v ∗ ) dx − u (A∇h v ∗ ) .n ds
Ω K∈Th ∂K
Z
b(u, ξ) = u ξ dx

X Z
e∗ (u∗ , µ) = µ (A∇h u∗ ) .n ds
K∈Th ∂K

Then, the previous reconstruction problem writes:


(F V ∗ )h : let (uh , λh ) ∈ Xh × Mh being given, find (u∗h , ζh ) ∈ Xh∗ × Zh such that

a∗ (u∗h , vh∗ ) + b∗ (vh∗ , ζh ) = ǎ(uh , vh∗ ) + e∗ (vh∗ , λh ), ∀vh∗ ∈ Xh∗


b∗ (u∗h , ξh ) − c∗ (ζh , ξh ) = b(uh , ξh ), ∀ξh ∈ Zh

By identifying the bilinear forms and the associated matrix, the previous linear problem also
expreses in matrix-vector form:
 ∗ 
a∗ bT∗
   
uh ǎ e∗ uh
=
b∗ −c∗ ζh b 0 λh

A carreful study shows that the matrix on the left-hand-side has a block-diagonal structure at
the element level and its inverse can be easily constructed explicitly by inverting on the fly the
Chapter 6. [New] Hybrid discontinuous methods 267

element matrix during the assembly process. Then, the result of reconstruction operator can be
obtained at low computational cost, by simple matrix-vector multiplication.
A further improvement can be obtained. Observe that the matrix on the left-hand-side is sym-
metric but definite: at the element level, it admits both positive and negative eigenvalues, due to
the saddle point structure.
Expanding the previous linear system, we obtain

(
a∗ u∗h + bT∗ ζh = ǎuh + eT∗ λh
b∗ u∗h − c∗ ζh = buh

Since the c∗ matrix is diagonal, when choosing γ > 0, the ζ unknown could be eliminated at the
element level:

ζh = c−1 ∗
∗ (b∗ uh − buh )

u∗h = S∗−1 Sh uh + eT∗ λh




with S∗ = a∗ + bT∗ c−1


∗ b∗ (6.11)
Sh = ǎ + bT∗ c−1
∗ b

Since we known that ζh = 0, the corresponding computation has no importance, expect for
checking purpose, and let us turn to those of u∗h . The Schur complement S∗ is still block-diagonal at
the element level and, moreover, it is then symmetric definite positive. This is of major importance
when considering iterative solvers, e.g. for tridimensional geometries. Let us turn to the practical
choice of γ > 0. An investigation of (6.10a) shows that entries of a∗ scales as O(hd−2 ). The hd
comes from the integration measure dx and the h−2 ,from the two derivations ∇h . Conversely,
assuming γ independent upon the mesh size h, the entries for b∗ and c∗ , from (6.10b) and (6.10c),
respectively, scales as O(hd ) since there is no derivation in b∗ and c∗ , Then, bT∗ c−1
∗ b∗ scales as O(h ).
d

Then, the expression (6.11) for ss is not equilibrated. Choosing γ = αh , with α > 0 independent
2

upon h, leads to bT∗ c−1∗ b∗ that scales as O(h


d−2
), i.e. as a∗ . This h-dependent choice for γ
dramatically improves the condition number for S∗ . Note that [Pietro and Droniou, 2020, p. 454],
eqn (B.7), proposed a similar implementation, despites it based on different notations and concepts.
268 Rheolef version 7.2

File 6.7: reconstruction_hho.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sinusprod . h "
5 # include " diffusion_isotropic . h "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 geo omega ( argv [1]);
9 string Pkd = ( argc > 2) ? argv [2] : " P0 " ,
10 Pld = ( argc > 3) ? argv [3] : Pkd ;
11 space Xh ( omega , Pld ) ,
12 Mh ( omega [ " sides " ] , Pkd );
13 size_t l = Xh . degree () , k = Xh . degree () , d = omega . dimension ();
14 check_macro ( l == k -1 || l == k || l == k +1 ,
15 " invalid (k , l ) = ( " <<k < < " ," <<l < < " ) " );
16 space Xhs ( omega , " P " + to_string ( k +1)+ " d " ) ,
17 Zh ( omega , " P0 " );
18 trial u ( Xh ) , lambda ( Mh ) , us ( Xhs ) , zeta ( Zh );
19 test v ( Xh ) , mu ( Mh ) , vs ( Xhs ) , xi ( Zh );
20 auto as = lazy_integrate ( dot ( grad_h ( us ) , A ( d )* grad_h ( vs )));
21 auto bs = lazy_integrate ( us * xi );
22 auto cs = lazy_integrate ( pow ( h_local () ,2)* zeta * xi );
23 auto m = lazy_integrate ( u * v );
24 form ms = lazy_integrate ( lambda * mu );
25 auto inv_cs = inv ( cs );
26 auto inv_m = inv ( m );
27 auto inv_S = inv ( as + trans ( bs )* inv_cs * bs );
28 auto llh = lazy_integrate ( u_exact ( d )* v );
29 field rhs = lazy_integrate ( u_exact ( d )* mu );
30 field pi_Xh_u = inv_m * llh ;
31 field pi_Mh_lambda ( Mh );
32 problem pms ( ms );
33 pms . solve ( rhs , pi_Mh_lambda );
34 field lh = lazy_integrate ( dot ( grad_h ( pi_Xh_u ) , A ( d )* grad_h ( vs ))
35 + on_local_sides (( pi_Mh_lambda - pi_Xh_u )
36 * dot ( normal () , A ( d )* grad_h ( vs ))));
37 auto kh = lazy_integrate ( pi_Xh_u * xi );
38 auto rh = lh + bs . trans_mult ( inv_cs * kh );
39 field us_h = inv_S * rh ;
40 field zeta_h = inv_cs *( bs * us_h - kh );
41 dout << catchmark ( " us " ) << us_h
42 << catchmark ( " zeta " ) << zeta_h ;
43 }

1
∥u∗h − u∥0,2,Ω ∥∇(u∗h − u)∥0,2,Ω
1
10−4 1
2

10−4 2
3
10−8

4 3
10−8
10−12 k =0 k =0
5=k+2 k =1 4=k+1 k =1
k =2 k =2
k =3 k =3
10−16 10−12
10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h

Figure 6.7: Hybrid hight-order (HHO) method: error vs h and k for the reconstruction operator.
Chapter 6. [New] Hybrid discontinuous methods 269

How to run the program

The compilation and run write:

make reconstruction_hho sinusprod_error_hho_reconstruction


mkgeo_grid -t 10 > square.geo
./reconstruction_hho square.geo P1d P1d | ./sinusprod_error_hho_reconstruction

The code ‘reconstruction_hho.cc’ first computes uh and λh as L2 projections on Xh and Mh ,


respectively, of a given function:
d−1
Y
u(x) = sin(πxi )
i=0
Then, it builds the reconstruction u∗h= rh (uh , λh ) and output the result. Next,
the code ‘sinusprod_error_hho_reconstruction.cc’ shows the L2 and H 1 error
for the reconstruction u∗h . The files ‘sinusprod.h’, ‘diffusion_isotropic.h’, and
‘sinusprod_error_hho_reconstruction.cc’, that defines u and A, and that compute the
errors, respectively, are not listed here but is available in the Rheolef example directory. Fig. 6.7
plots the error versus mesh size h and the polynomial order k: the convergence order is k + 2
in the L2 norm and k + 1 in the H 1 one, as expected (see Cockburn et al. [2016], eqn (2.9)
or di Pietro and Ern [2015], eqn (3)).

6.3.3 The projections


Let πXh denotes the L2 projection from L2 (Ω) into Xh . For all u∗ ∈ L2 (Ω), it is defined as
πXh (u∗ ) = ǔh ∈ Xh , where ǔh is the solution of the following quadratic minimization problem:
Z
1
ǔh = arg inf (u∗ − v̌h )2 dx
v̌h ∈Xh 2 Ω

Let us consider the special case of computing the projection ǔh = πXh (u∗h ) ∈ Xh of an ele-
ment u∗h ∈ Xh∗ . The following bilinear forms are introduced:
Z
m(ǔ, v̌) = ǔ v̌ dx
ZΩ
d∗ (u∗ , v̌) = u∗ v̌ dx

Then, the projection reduces to:


let u∗h ∈ Xh∗ being given, find ǔh ∈ Xh such that
m(ǔh , v̌h ) = d∗ (u∗h , v̌h ), ∀v̌h ∈ Xh
Note that the matrix associated to the bilinear form m from Xh × Xh to R has a block-diagonal
structure at the element level and can be easily inverted on the fly during the assembly process.
Then, the result of projection operator can be obtained by simple matrix-vector multiplication.
Let us reintroduce the following space M̃h , already defined in (6.7), page 262, in the context of
the HDG method:
Y  Y Y
M̃h = τ̃ |∂K .n ; τ̃ ∈ RTk (K) = Pk (S)
K∈Th K∈Th S⊂∂K

Let πM̃h denote the L2 projection into M̃h . We consider the special case of computing the projec-
tion δ̃h = πM̃h (u∗h ) ∈ M̃h of an element u∗h ∈ Xh∗ . It is defined as
Z
1 X
δ̃h = arg inf (u∗h − φ̃h )2 ds
φ̃h ∈M̃h 2 K∈Th ∂K
270 Rheolef version 7.2

Note that u∗h ∈ Xh∗ is bi-valued and Pk+1 on all internal side while its projec-
tion δ̃h = πM̃h (u∗h ) ∈ M̃h is also bi-valued but only Pk . The following bilinear forms are intro-
duced:

X Z
m̃(δ̃, φ̃) = δ̃ φ̃ ds
K∈Th ∂K
X Z
d˜∗ (u∗ , φ̃) = u∗ φ̃ ds
K∈Th ∂K

Then, the projection reduces to:


let u∗h ∈ Xh∗ being given, find δ̃h ∈ M̃h such that

m̃(δ̃h , φ̃h ) ˜ ∗ , φ̃h ), ∀φ̃h ∈ M̃h


= d(uh

Here also, the matrix associated to the bilinear form ms from M̃h × M̃h to R has a block-diagonal
structure at the element level and can be easily inverted on the fly during the assembly process.
Then, the result of projection operator can be obtained by simple matrix-vector multiplication.

6.3.4 The discrete problem statement

The discretization of the diffusion problem (6.8a)-(6.8b) by the hybrid high-order (HHO)
method [Cockburn et al., 2016, p. 639] is expressed here as a minimization problem:

(uh , λh ) = arg min max L1 (vh , µh )


vh ∈Xh µh ∈Mh (0)

with
Z  
1 2
L1 (u, λ) = |∇h rh (u, λ)|A − f u dx
Ω 2
Z
1 X X 2
+ β h−1
S πM̃h{u − λ + (I − πXh ) rh (u, λ)} ds
2
K∈Th S⊂∂K S

Mh (0) = µh ∈ Mh ; µh|S = 0, ∀S ⊂ ∂Ω

In the definition of L1 , the first term is related to the consistency and the second one to the sta-
bilization, with a β h−1 S penalization factor (see [Cockburn et al., 2016, p. 637], eqns (2.4)-(2.5)),
while hS denotes a local mesh characteristic length in the side S ∈ Sh . Here β > 0 is the penal-
ization parameter, which is a constant independent of the mesh size h. The space Mh (0) for the
Lagrange multiplier λh is introduced in order to impose the homogeneous Dirichlet condition (6.8b)
as a constraint.
The expression of L1 is not convenient for practical implementation, as it involves both the re-
construction operator rh and the projections πXh and πM̃h . Let us introduce the reconstruction
u∗h = rh (uh , µh ) ∈ Xh∗ as an auxilliary variable. The constraint u∗h − rh (uh , µh ) = 0 is imposed
via a Lagrange multiplier vh∗ ∈ Xh∗ . Both the two additional variables u∗h and vh∗ have a con-
strained average value and we also introduce two associated Lagrangian multipliers: one is de-
noted by ζ1,h ∈ Zh , associated to the imposition of a zero element-wise average value for u∗h − uh .
the second, ζ2,h ∈ Zh , is associated to the imposition of a zero element-wise average value for vh∗ .
Chapter 6. [New] Hybrid discontinuous methods 271

Similarly to the augmented Lagrangian (6.9), we introduce a new Lagrangian:


Z  
∗ ∗ 1 ∗ 2
L2 (u, u ; λ, v , ζ1 , ζ2 ) = |∇h u |A − f u dx
Ω 2
Z
1 X X 2
+ β h−1
S πM̃h{u − λ + u∗ − πXh (u∗ )} ds
2
K∈Th S⊂∂K S
Z X Z
+ ∇h (u∗ − u). (A∇h v ∗ ) dx + (u − λ) (A∇v ∗ ) .n ds
Ω K∈Th ∂K

α h−2 2
Z  
(u∗ − u) ζ1 + v ∗ ζ2 − ζ1 − ζ22

+ dx
Ω 2

In the last term, also have introduced the augmented Lagrangian term with coefficient α > 0, as
introduced in the previous paragraph on the reconstruction operator. The previous problem in
terms of L1 can be equivalently expressed as a saddle point for L2 , with six unknowns:

(uh , u∗h ; λh , vh∗ , ζ1,h , ζ2,h ) = arg inf sup L2 (vh , vh∗ ; µh , wh∗ , ξ1,h , ξ2,h )
vh ∈ X h µh ∈ Mh (0)
∗ ∗ ∗ ∗
vh ∈ Xh wh ∈ Xh
ξ1,h ∈ Zh
ξ2,h ∈ Zh

The reconstruction operator rh has been eliminated, but the previous expression of L2 still involves
the two projectors πXh and πM̃h . These explicit calls to the projectors are not convenient for
the practical implementation. Let us introduce the two auxiliary variables ǔh = πXh (u∗h ) ∈ Xh
and δ̃h = πM̃h (u − λ + u∗h − ǔh ) ∈ M̃h as two constraints and the two corresponding Lagrangian
multipliers, v̌h ∈ Xh and γ̃h ∈ M̃h , respectively. Then, we obtain the following Lagrangian:
Z   X Z
1 1 X
L(u, u∗ , ǔ, δ̃; λ, v ∗ , ζ1 , ζ2 , v̌, γ̃) = |∇h u∗ |2A − f u dx + β h−1 2
S δ̃ ds
Ω 2 2 S
K∈Th S⊂∂K
Z X Z
+ ∇h (u∗ − u). (A∇h v ∗ ) dx + (u − λ) (A∇v ∗ ) .n ds
Ω K∈Th ∂K
Z  −2

αh
(u∗ − u) ζ1 + v ∗ ζ2 − ζ12 − ζ22

+ dx
2
ZΩ X X Z
+ (u∗ − ǔ) v̌ dx + (u − λ + u∗ − ǔ − δ̃) γ̃ ds
Ω K∈Th S⊂∂K S

and the previous minimization problem can be equivalently expressed as a saddle point one with
ten unknowns:

(uh , u∗h , ǔh , δ̃h ; λh , vh∗ , ζ1,h , ζ2,h , v̌h , γ̃h )


= arg inf sup L(wh , wh∗ , w̌h , φ̃h ; µh , yh∗ , ξ1,h , ξ2,h , y̌h , κ̃h )
w h ∈ Xh µh ∈ Mh (0)
∗ ∗ ∗ ∗
wh ∈ Xh yh ∈ Xh
w̌h ∈ Xh ξ1,h Zh
φ̃h ∈ M̃h ξ2,h Zh
y̌h ∈ Xh
κ̃h ∈ M̃h

Since L is differentiable and quadratic, its saddle point is characterized as the unique solution of
a variational and linear problem:
 2
(F V )h : find (u∗h , ζ1,h , δ̃h ; vh∗ , ζ2,h , γ̃h ; ǔh , v̌h , uh ; λ) ∈ Xh∗ × Zh × M̃h × Xh3 × Mh (0) such
272 Rheolef version 7.2

that
Z Z X X Z
∇h u∗ .(A∇h w∗ ) dx − f w dx + β h−1
S δ̃ φ̃ ds
Ω Ω K∈Th S⊂∂K S
Z
+ {∇h (u∗ − u).(A∇h y ∗ ) + ∇h (w∗ − w).(A∇h v ∗ )} dx

X X Z
+ {u (A∇y ∗ ).n + w (A∇v ∗ ).n} ds
K∈Th S⊂∂K S
Z
 ∗
+ (u − u)ξ1 + (w∗ − w)ζ1 + v ∗ ξ2 + y ∗ ζ2 − α h−2 (ζ1 ξ1 − ζ2 ξ2 ) dx
ZΩ
+ {(u∗ − ǔ)y̌ + (w∗ − w̌)v̌} dx

X X Z n o
+ (u + u∗ − ǔ − δ̃)κ̃ + (w + w∗ − w̌ − φ̃)γ̃ dx
K∈Th S⊂∂K S
X X Z
− {λ (A∇y ∗ ).n + µ (A∇v ∗ ).n} ds
K∈Th S⊂∂K S
X X Z
− (λ κ̃ + µ γ̃) ds
K∈Th S⊂∂K S

= 0
 2
for all (wh∗ , ξ1,h , φ̃h ; yh∗ , ξ2,h , κ̃h ; w̃h , y̌h , wh ; µ) ∈ Xh∗ × Zh × M̃h × Xh3 × Mh (0). The h sub-
scripts for the unknowns and test functions have been omitted for simplicity in the variational
expression. This system could appear as complex. But observe that all discretization are piecewise
discontinuous at the element level except those for λh ∈ Mh (0). Then, all variables except this
one could be eliminated at the element level. In order to process to this elimination, the previous
variational formulation is rearranged as a matrix block structure:

u∗ v∗
 
ζ1 ζ2 δ̃ γ̃ ǔ v̌ u λ   

 w∗ a
 ∗ bT∗ a∗ d˜T∗ dT∗  u   0 
 h  
 ζ 
 1 b∗ −c∗ −d
 ξ  0 
  1,h 
 ∗ vh∗   0 
  
 y a∗ bT∗ −ǎ −eT∗  

  
ζ2,h 

 ξ2 b∗ c∗   0 

  
 
 φ̃
 c̃ −m̃   δ̃h  =  0 

  

 κ̃ d˜∗ −m̃ −d˜ d˜ −ẽT  γ̃h   0 
  
 
−d˜T   ǔh   0 
  
 w̌
 −m  
 y̌ d∗ −m   v̌h   0 
 
    
d˜T uh   ℓh 
 
 w −dT −ǎT 
µ −e −ẽ λh 0

The matrix are identified to the corresponding forms and we have introduce thirteen bilinear forms
and a linear form. A first group of five bilinear forms is associated to symmetric positive matrix
plus the right-hand-side ℓh :
Z Z
a∗ (u∗ , w∗ ) = ∇h u∗ . (A∇h w∗ ) dx ℓh (w) = f w dx
ZΩ Ω
X Z
m(u, w) = u w dx m̃(δ̃, φ̃) = δ̃ φ̃ ds
Ω K∈Th ∂K
Z X Z
c∗ (ζ, ξ) = α h−2 ζ ξ dx c̃(δ̃, φ̃) = β h−1
S δ̃ φ̃ ds
Ω K∈Th ∂K
Chapter 6. [New] Hybrid discontinuous methods 273

All the associated matrix are symmetric definite positive, and then invertible, except a∗ , which is
only symmetric positive but has a zero eigenvalue. The second group corresponds to rectangular
matrix: Z
b∗ (u∗ , ξ) = u∗ ξ dx

Z X Z
d(u, ξ) = u ξ dx ˜ φ̃) =
d(u, u φ̃ ds
Ω K∈Th ∂K
Z X Z

d∗ (u , w) = ∗
u w dx d˜∗ (u∗ , φ̃) = u∗ φ̃ ds
Ω K∈Th ∂K

Z X Z
ǎ(u, w∗ ) = ∇h u. (A∇h w∗ ) dx − u (A∇h w∗ ) .n ds
Ω K∈Th ∂K

These two groups admit a block-diagonal structure at the element level, due to the piecewise
discontinuous character of the trial and test functions. In the following third group, bilinear forms
corresponds to rectangular matrix with non-block-diagonal structure, due to the inter-element
continuity of µ ∈ M h:
X Z X Z
e∗ (v ∗ , µ) = µ (A∇h v ∗ ) .n ds ẽ(γ̃, µ) = γ̃ µ ds
K∈Th ∂K K∈Th ∂K

Let us process to the elimination of all the unknowns except λh . For simplicity, the matrix
subequations are designated by the corresponding test-function surrounded by parenthesis, such
as (w∗ ) for the first one. Let us start by eliminating some variables related to projections: from (y̌),
(w̃) and (φ̃), we get, respectively:
ǔ = m−1 d∗ u∗ (6.12a)
v̌ = −m−1 d˜T γ̃ (6.12b)
γ̃ = m̃−1 c̃ δ̃ (6.12c)

The following subexpression will be frequently involved:

dT∗ v̌ + d˜T∗ γ̃ = DT δ̃
with D = c̃ m̃−1 D̃∗
D̃∗ = d˜∗ − d˜m−1 d∗

Next, let us turn to variables related to the reconstruction. from (ξ1 ) and (w∗ ), we get: we obtain
successively:
ζ1 = c−1 ∗
∗ (b∗ u − du) (6.12d)
 
u∗ = S∗−1 −a∗ v ∗ − DT δ̃ + bT∗ c−1
∗ du (6.12e)
with S∗ = a∗ + bT∗ c−1
∗ b∗

Conversely, from (ξ2 ), (y ∗ ) and (6.12e)

ζ2 = −c−1∗ b∗ v

(6.12f)
 
v ∗ = T −1 −a∗ S∗−1 DT δ̃ + Ru − eT∗ λ (6.12g)
with T = a∗ S∗−1 a∗ + bT∗ c−1
∗ b∗

R = a∗ S∗−1 bT∗ c−1


∗ d − ǎ
274 Rheolef version 7.2

Back-substituting in (6.12d)-(6.12e):

u∗ = − S∗−1 − S∗−1 a∗ T −1 a∗ S∗−1 DT δ̃ + S∗−1 bT∗ c−1 −1


R u + S∗−1 a∗ T −1 eT∗ λ (6.12h)
 
∗ d − a∗ T

ζ1 = −c−1 −1 −1 −1
a∗ S∗−1 DT δ̃

∗ b∗ S∗ − S∗ a∗ T

+ c−1 b∗ S∗−1 bT∗ c−1 −1


  
∗ ∗ d − a∗ T R −d u
+ c−1 −1
∗ b∗ S∗ a∗ T
−1 T
e∗ λ (6.12i)

Then, replacing (6.12c), (6.12g) and (6.12i) in (w), we obtain:

Ac u+Ẽ δ̃ − RT T −1 eT∗ λ = ℓ (6.12j)


with Ac = R T T −1 T
c−1 c−1 −1 T −1

R+d ∗ − ∗ b∗ S∗ b∗ c∗ d
T −1
=R T R
since c∗ = b∗ S∗−1 bT∗
Ẽ = d˜T m̃−1 c̃ + ǎT T −1 a∗ S∗−1 DT + dT c−1 −1 −1 −1
a∗ S∗−1 DT

∗ b∗ S∗ − S∗ a∗ T

It remains to express δ̃ in terms of u and λ. From (κ̃) and (6.12a):

˜ + ẽT λ = 0
m̃δ̃ − D̃∗ u∗ − du

Left-multiplying by c̃m̃−1 and substituting (6.12h), we get


 
δ̃ = M̃ −1 Ẽ T u + F T λ (6.12k)
M̃ = c̃ + D S∗−1 − S∗−1 a∗ T −1 a∗ S∗ −1
DT


F = e∗ T −1 a∗ S∗−1 DT − ẽ m̃−1 c̃

Replacing in (6.12j), we get

Au + B T λ = ℓ (6.12l)
with A = Ac + As
As = Ẽ M̃ −1 Ẽ T
B = F M̃ −1 Ẽ T − e∗ T −1 R

Here, Ac represents the consistency part of the operator A while As is the stabilization part, i.e.
the penalization. Finally, (µ) becomes

Bu + Cλ = 0 (6.12m)
with C = e∗ T −1 eT∗ + FM −1
F T
(6.12n)

From (6.12l)-(6.12m), we get


u = A−1 (ℓ − B T λ)
Sλ = f
with S = C − BA−1 B T
f = −BA−1 ℓ
The following code implements this technique:
Chapter 6. [New] Hybrid discontinuous methods 275

File 6.8: dirichlet_hho.cc


1 # include " rheolef . h "
2 using namespace rheolef ;
3 using namespace std ;
4 # include " sinusprod_dirichlet . h "
5 # include " diffusion_isotropic . h "
6 int main ( int argc , char ** argv ) {
7 environment rheolef ( argc , argv );
8 geo omega ( argv [1]);
9 string Pkd = ( argc > 2) ? argv [2] : " P1d " ,
10 Pld = ( argc > 3) ? argv [3] : Pkd ;
11 space Xh ( omega , Pld ) ,
12 Mh ( omega [ " sides " ] , Pkd );
13 Mh . block ( " boundary " );
14 size_t k = Mh . degree () , l = Xh . degree () , dim = omega . dimension ();
15 Float beta = ( argc > 4) ? atof ( argv [4]) : 10*( k +1)*( k + dim )/ Float ( dim );
16 check_macro ( l == k -1 || l == k || l == k +1 , " invalid (k , l ) " );
17 space Xhs ( omega , " P " + to_string ( k +1)+ " d " ) ,
18 Zh ( omega , " P0 " ) ,
19 Mht ( omega , " trace_n ( RT " + to_string ( max (k , l ))+ " d ) " );
20 trial us ( Xhs ) , u ( Xh ) , zeta ( Zh ) , deltat ( Mht ) , lambda ( Mh );
21 test ws ( Xhs ) , w ( Xh ) , xi ( Zh ) , phit ( Mht ) , mu ( Mh );
22 auto lh = lazy_integrate ( f ( dim )* w );
23 auto m = lazy_integrate ( u * w );
24 auto as = lazy_integrate ( dot ( grad_h ( us ) , A ( dim )* grad_h ( ws )));
25 auto cs = lazy_integrate ( pow ( h_local () ,2)* zeta * xi );
26 auto mt = lazy_integrate ( on_local_sides ( deltat * phit ));
27 auto ct = lazy_integrate ( on_local_sides ( beta * pow ( h_local () , -1)* deltat * phit ));
28 auto bs = lazy_integrate ( us * xi );
29 auto d = lazy_integrate ( u * xi );
30 auto ds = lazy_integrate ( us * w );
31 auto dt = lazy_integrate ( on_local_sides ( u * phit ));
32 auto dst = lazy_integrate ( on_local_sides ( us * phit ));
33 auto ac = lazy_integrate ( dot ( grad_h ( u ) , A ( dim )* grad_h ( ws ))
34 - on_local_sides ( u * dot ( A ( dim )* grad_h ( ws ) , normal ())));
35 auto et = lazy_integrate ( on_local_sides ( mu * deltat ));
36 auto es = lazy_integrate ( on_local_sides ( mu * dot ( A ( dim )* grad_h ( us ) , normal ())));
37 auto inv_cs = inv ( cs );
38 auto inv_Ss = inv ( as + trans ( bs )* inv_cs * bs );
39 auto inv_T = inv ( as * inv_Ss * as + trans ( bs )* inv_cs * bs );
40 auto R = as * inv_Ss * trans ( bs )* inv_cs * d - ac ;
41 auto Ac = trans ( R )* inv_T * R ;
42 auto D = ct * inv ( mt )*( dst - dt * inv ( m )* ds );
43 auto M0 = inv_Ss - inv_Ss * as * inv_T * as * inv_Ss ;
44 auto inv_M = inv ( ct + D * M0 * trans ( D ));
45 auto E = trans ( dt )* inv ( mt )* ct
46 + trans ( ac )* inv_T * as * inv_Ss * trans ( D )
47 + trans ( d )* inv_cs * bs * M0 * trans ( D );
48 auto As = E * inv_M * trans ( E );
49 auto inv_A = inv ( Ac + As );
50 auto F = es * inv_T * as * inv_Ss * trans ( D )
51 - et * inv ( mt )* ct ;
52 auto C = es * inv_T * trans ( es ) + F * inv_M * trans ( F );
53 auto B = F * inv_M * trans ( E ) - es * inv_T * R ;
54 form S = C - B * inv_A * trans ( B );
55 problem pS ( S );
56 field rhs = -B *( inv_A * lh );
57 field lambda_h ( Mh , 0);
58 pS . solve ( rhs , lambda_h );
59 auto uh = inv_A *( lh - B . trans_mult ( lambda_h ));
60 auto deltat_h = inv_M *( E . trans_mult ( uh ) + F . trans_mult ( lambda_h ));
61 auto vs_h = inv_T *( - as * inv_Ss * D . trans_mult ( deltat_h ) + R * uh - es . trans_mult ( lambda_h ));
62 field us_h = inv_Ss *( - as * vs_h - D . trans_mult ( deltat_h ) + trans ( bs )* inv_cs * d * uh );
63 dout << catchmark ( " us " ) << us_h
64 << catchmark ( " u " ) << field ( uh )
65 << catchmark ( " lambda " ) << lambda_h ;
66 }
276 Rheolef version 7.2

File 6.9: dirichlet_homogeneous.h


1 struct f {
2 Float operator () ( const point & x ) const { return 1; }
3 f ( size_t =0) {}
4 };
5 struct g {
6 Float operator () ( const point & x ) const { return 0; }
7 g ( size_t =0) {}
8 };

As usual, the right-hand-side f and the Dirichlet boundary condition g has been chosen such that
the exact solution is given by:
d−1
Y
u(x) = sin(πxi )
i=0

How to run the program

∥u∗h − u∥0,2,Ω ∥∇h (u∗h − u)∥0,2,Ω


1 k =0 1
k =1
k =2
k =3 1

10−5 10−5 2
2

k =0
3 k =1
4 = k+1
3
5=k+2 k =2
4
k =3
10−10 10−10
10−3 10−2 10−1 1 10−3 10−2 10−1 1
h h

Figure 6.8: Hybrid hight-order (HHO) method: error vs h and k for the Poisson problem.

The compilation and run write:

make dirichlet_hho
mkgeo_grid -t 10 > square.geo
./dirichlet_hho square.geo P1d > square.field
field square.field -elevation
field square.field -elevation -mark u
field square.field -elevation -mark lambda

Observe the better behavior of the u∗h approximate solution, obtained by reconstruction, when
compared with the original one u. The error versus the mesh refinement h and the polynomial
orer k is plotted on Fig. 6.8. These numerical experiments confirm the theoretical predictions (see
e.g. Pietro and Droniou [2020], pages 65 and 68):

∥u∗h − u∥0,2,Ω = O(hk+2 )


∥∇h (u∗h − u)∥0,2,Ω = O(hk+1 )

Fig. 6.9 plots the L2 error for u∗h versus the CPU time required to its computation for various
mesh refinement h and polynomial order k. It compares it with the L2 error for obtaining the Pk+1
Chapter 6. [New] Hybrid discontinuous methods 277

∥u∗h − u∥0,2,Ω k=0


1
1 2
3

10−5

10−10
0.01 0.1 1 10 100 1000 10000
tcpu (sec)

Figure 6.9: Comparison of hybrid hight-order (HHO) method (continuous lines) with classical
Lagrange continuous approximation (dotted lines). Error vs CPU time for various h and k for the
Poisson problem. The test bases on a bidimensional triangular mesh and a direct sequential solver
(no parallel computation).

continuous Lagrange solution. While increasing the polynomial order k, the slope of convergence
of the error versus h appears to be higher than its counterpart with the classical Lagrange con-
tinuous approximation (dotted lines). Thus, the HHO method appears to be asymptotically more
efficient when h → 0 i.e. large meshes and high CPU times. Nevertheless, the constant in front
of this asymptotical HHO method is higher than its counterpart with the classical Lagrange con-
tinuous approximation, and thus, when the mesh is medium or small, the classical method is still
faster. The constant of the HHO method is due to algebraic manipulations, such as assembly and
matrix-matrix products at the element level: it could certainly be decreased by a more efficient
implementation of the HHO method, and this perspective will be explorated in the future.
278 Rheolef version 7.2
Appendix A

Technical appendices

A.1 How to write a variational formulation ?


The major key-point for using Rheolef is to put the problem in variational form. Then this
variational form can be efficiently translated into C++ language. This appendix is dedicated to
readers who are not fluent with variational formulations and some related functional analysis tools.

A.1.1 The Green formula


Let us come back to the model problem presented in section 1.1.1, page 14, equations (1.1)-(1.2)
and details how this problem is transformed into (1.3).
Let H01 (Ω) the space of functions whose gradient square has a finite sum over Ω and that vanishes
on ∂Ω:
H01 (Ω) = {v ∈ L2 (Ω); ∇v ∈ L2 (Ω)d and v = 0 on ∂Ω}
We start by multiplying (1.1) by an arbitrarily test-function v ∈ H01 (Ω) and then integrate over Ω :
Z Z
− ∆u v dx = f v dx, ∀v ∈ H01 (Ω)
Ω Ω

The next step is to invoke an integration by part, the so-called Green formula:
Z Z Z
∂u
∆u v dx + ∇u.∇v dx = v ds, ∀u, v ∈ H 1 (Ω)
Ω Ω ∂Ω ∂n
Since our test-function v vanishes on the boundary, the integral over ∂Ω is zero and the problem
becomes: Z Z
∇u.∇v dx = f v dx, ∀v ∈ H01 (Ω)
Ω Ω

This is exactly the variational formulation (1.3), page 14.

A.1.2 The vectorial Green formula


In this section, we come back to the linear elasticity problem presented in section 2.1.1, page 43,
equations (2.1)-(2.2) and details how this problem is transformed into (2.3).
Let Γd (resp. Γn ) denotes the parts of the boundary ∂Ω related to the homogeneous Dirichlet
boundary condition u = 0 (resp. the homogeneous Neumann boundary condition σ(u) n = 0).
We suppose that ∂Ω = Γd ∩ Γn . Let us introduce the following functional space:

V = {v ∈ H 1 (Ω)d ; v = 0 on Γd }

279
280 Rheolef version 7.2

Then, multiplying the first equation of (2.2) by an arbitrarily test-function v ∈ V and then
integrate over Ω : Z Z
− div(σ(u)).v dx = f .v dx, ∀v ∈ V
Ω Ω
The next step is to invoke an integration by part:
Z Z Z
div τ.v dx + τ : D(v) dx = τ : (v ⊗ n) ds, ∀τ ∈ L2 (Ω)d×d , ∀v ∈ V
Ω Ω ∂Ω
P 
d−1
Recall that div τ denotes j=0 , i.e. the vector whose component are the diver-
∂j τi,j
0⩽i<d
Pd−1
gence of each row of τ . Also, σ : τ denote the double contracted product i,j=0 σi,j τi,j for any
tensors σ and τ , and that u ⊗ v dotes the τi,j = ui vj tensor, vectors u and v. Remark that
Pd−1
τ : (u ⊗ v) = (τ v).u = i,j=0 τi,j ui vj . Choosing τ = σ(u) in the previous equation leads to:
Z Z Z
σ(u) : D(v) dx = (σ(u) n).v ds + f .v dx, ∀v ∈ V
Ω ∂Ω Ω
Since our test-function v vanishes on Γd and the solution satisfies the homogeneous Neumann
boundary condition σ(u) n = 0 on Γn , the integral over ∂Ω is zero and the problem becomes:
Z Z
σ(u) : D(v) dx = f .v dx, ∀v ∈ V
Ω Ω

From the definition of σ(u) in (2.1) page 43 we have:


σ(u) : D(v) = λ div(u) (I : D(v)) + 2µD(u) : D(v)
= λ div(u) div(v) + 2µD(u) : D(v)
and the previous relation becomes:
Z Z Z
λdiv(u) div(v) dx + 2µD(u) : D(v) dx = f .v dx, ∀v ∈ V
Ω Ω Ω

This is exactly the variational formulation (2.3), page 43.

A.1.3 The Green formula on a surface


Let Γ a closed and orientable surface of Rd , d = 2, 3 and n its unit normal. From [Laadhari et al.,
2010], appendix C we have the following integration by part:
Z Z Z
divs v ξ ds + v.∇s ξ ds = v.n ξ div n ds
Γ Γ Γ

for all ξ ∈ H (Γ) and v ∈ H (Γ) . Note that div n represent the surface curvature. Next, we
1 1 d

choose v = ∇s φ, for any φ ∈ H 2 (Γ). Remaking that v.n = 0 and that divs v = ∆s φ. Then:
Z Z
∆s ξ ds + ∇s φ.∇s ξ ds = 0
Γ Γ
This formula is the starting point for all variational formulations of problems defined on a surface
(see chapter 3.1).

A.2 How to prepare a mesh ?


Since there is many good mesh generators, Rheolefdoes not provide a built-in mesh generator.
There are several ways to prepare a mesh for Rheolef.
We present here several procedures: by using the bamg bidimensional anisotropic mesh generator,
written by Hecht [2006], and the gmsh mesh generator, suitable when d = 1, 2 and 3, and written
by Geuzaine and Remacle [2009].
Appendix A. Technical appendices 281

A.2.1 Bidimensional mesh with bamg


We first create a ‘square.bamgcad’ file:

MeshVersionFormatted
0
Dimension
2
Vertices
4
0 0 1
1 0 2
1 1 3
0 1 4
Edges
4
1 2 101
2 3 102
3 4 103
4 1 104
hVertices
0.1 0.1 0.1 0.1

This is an uniform mesh with element size h = 0.1. We refer to the bamg documentation Hecht
[2006] for the complete file format description. Next, enter the mesh generator commands:

bamg -g square.bamgcad -o square.bamg

Then, create the file ‘square.dmn’ that associate names to the four boundary domains of the mesh.
Here, there is four boundary domains:

EdgeDomainNames
4
bottom
right
top
left

and enter the translation command:

bamg2geo square.bamg square.dmn > square.geo

This command creates a ‘square.geo’ file. Look at the mesh via the command:

geo square

This presents the mesh it in a graphical form, usually with paraview. You can switch to the
gnuplot render:

geo square -gnuplot

A finer mesh could be generated by:

bamg -coef 0.5 -g square.bamgcad -o square-0.5.bamg


282 Rheolef version 7.2

A.2.2 Unidimensional mesh with gmsh


The simplest unidimensional mesh is a line:

h_local = 0.1;
Point(1) = {0, 0, 0, h_local};
Point(2) = {1, 0, 0, h_local};
Line(3) = {1,2};
Physical Point("left") = {1};
Physical Point("right") = {2};
Physical Point("boundary") = {1,2};
Physical Line("interior") = {3};

The mesh generation command writes:

gmsh -1 line.mshcad -format msh2 -o line.msh

Then, the conversion to ‘.geo’ format and the visualization:

msh2geo line.msh > line.geo


geo line

A.2.3 Bidimensional mesh with gmsh

Figure A.1: Visualization of the gmsh meshes ‘square.geo’ and ‘cube.geo’.

We first create a ‘square.mshcad’ file:

n = 10.0;
hloc = 1.0/n;
Point(1) = {0, 0, 0, hloc};
Point(2) = {1, 0, 0, hloc};
Appendix A. Technical appendices 283

Point(3) = {1, 1, 0, hloc};


Point(4) = {0, 1, 0, hloc};
Line(1) = {1,2};
Line(2) = {2,3};
Line(3) = {3,4};
Line(4) = {4,1};
Line Loop(5) = {1,2,3,4};
Plane Surface(6) = {5} ;
Physical Point("left_bottom") = {1};
Physical Point("right_bottom") = {2};
Physical Point("right_top") = {3};
Physical Point("left_top") = {4};
Physical Line("boundary") = {1,2,3,4};
Physical Line("bottom") = {1};
Physical Line("right") = {2};
Physical Line("top") = {3};
Physical Line("left") = {4};
Physical Surface("interior") = {6};

This is an uniform mesh with element size h = 0.1. We refer to the gmsh documentation Geuzaine
and Remacle [2009] for the complete file format description. Next, enter the mesh generator
commands:

gmsh -2 square.mshcad -format msh2 -o square.msh

Then, enter the translation command:

msh2geo square.msh > square.geo

This command creates a ‘square.geo’ file. Look at the mesh via the command:

geo square

Remark that the domain names, defined in the .mshcad file, are included in the gmsh .msh input
file and are propagated in the .geo by the format conversion.

A.2.4 Tridimensional mesh with gmsh


First, create a ‘cube.mshcad’ file:

Mesh.Algorithm = 7; // bamg
Mesh.Algorithm3D = 7; // mmg3d
a = 0; c = 0; f = 0;
b = 1; d = 1; g = 1;
n = 10;
hloc = 1.0/n;
Point(1) = {a, c, f, hloc};
Point(2) = {b, c, f, hloc};
Point(3) = {b, d, f, hloc};
Point(4) = {a, d, f, hloc};
Point(5) = {a, c, g, hloc};
Point(6) = {b, c, g, hloc};
Point(7) = {b, d, g, hloc};
Point(8) = {a, d, g, hloc};
Line(1) = {1,2};
284 Rheolef version 7.2

Line(2) = {2,3};
Line(3) = {3,4};
Line(4) = {4,1};
Line(5) = {5,6};
Line(6) = {6,7};
Line(7) = {7,8};
Line(8) = {8,5};
Line(9) = {1,5};
Line(10) = {2,6};
Line(11) = {3,7};
Line(12) = {4,8};
Line Loop(21) = {-1,-4,-3,-2};
Plane Surface(31) = {21} ;
Line Loop(22) = {5,6,7,8};
Plane Surface(32) = {22} ;
Line Loop(23) = {1,10,-5,-9};
Plane Surface(33) = {23} ;
Line Loop(24) = {12,-7,-11,3};
Plane Surface(34) = {24} ;
Line Loop(25) = {2,11,-6,-10};
Plane Surface(35) = {25} ;
Line Loop(26) = {9,-8,-12,4};
Plane Surface(36) = {26} ;
Surface Loop(41) = {31,32,33,34,35,36};
Volume(51) = {41};
Physical Surface("bottom") = {31};
Physical Surface("top") = {32};
Physical Surface("left") = {33};
Physical Surface("front") = {35};
Physical Surface("right") = {34};
Physical Surface("back") = {36};
Physical Volume("internal") = {51};

Next, enter the mesh generator commands:

gmsh -3 cube.mshcad -format msh2 -o cube.msh

Then, enter the translation command:

msh2geo cube.msh > cube.geo

This command creates a ‘cube.geo’ file. Look at the mesh via the command:

geo cube
geo cube.geo -cut

The second command allows one to see inside the mesh.


Appendix B

GNU Free Documentation License

Version 1.1, March 2000

Copyright © 2000 Free Software Foundation, Inc.


59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies of this license document, but chang-
ing it is not allowed.

Preamble
The purpose of this License is to make a manual, textbook, or other written document “free”
in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially. Secondarily, this License
preserves for the author and publisher a way to get credit for their work, while not being considered
responsible for modifications made by others.
This License is a kind of “copyleft”, which means that derivative works of the document must
themselves be free in the same sense. It complements the GNU General Public License, which is
a copyleft license designed for free software.
We have designed this License in order to use it for manuals for free software, because free software
needs free documentation: a free program should come with manuals providing the same freedoms
that the software does. But this License is not limited to software manuals; it can be used for
any textual work, regardless of subject matter or whether it is published as a printed book. We
recommend this License principally for works whose purpose is instruction or reference.

Applicability and Definitions


This License applies to any manual or other work that contains a notice placed by the copyright
holder saying it can be distributed under the terms of this License. The “Document”, below, refers
to any such manual or work. Any member of the public is a licensee, and is addressed as “you”.
A “Modified Version” of the Document means any work containing the Document or a portion of
it, either copied verbatim, or with modifications and/or translated into another language.
A “Secondary Section” is a named appendix or a front-matter section of the Document that deals
exclusively with the relationship of the publishers or authors of the Document to the Document’s
overall subject (or to related matters) and contains nothing that could fall directly within that
overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary
Section may not explain any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal, commercial, philosophical, ethical
or political position regarding them.

285
286 Rheolef version 7.2

The “Invariant Sections” are certain Secondary Sections whose titles are designated, as being those
of Invariant Sections, in the notice that says that the Document is released under this License.
The “Cover Texts” are certain short passages of text that are listed, as Front-Cover Texts or
Back-Cover Texts, in the notice that says that the Document is released under this License.
A “Transparent” copy of the Document means a machine-readable copy, represented in a format
whose specification is available to the general public, whose contents can be viewed and edited
directly and straightforwardly with generic text editors or (for images composed of pixels) generic
paint programs or (for drawings) some widely available drawing editor, and that is suitable for
input to text formatters or for automatic translation to a variety of formats suitable for input to
text formatters. A copy made in an otherwise Transparent file format whose markup has been
designed to thwart or discourage subsequent modification by readers is not Transparent. A copy
that is not “Transparent” is called “Opaque”.
Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo
input format, LATEX input format, SGML or XML using a publicly available DTD, and standard-
conforming simple HTML designed for human modification. Opaque formats include PostScript,
PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML
or XML for which the DTD and/or processing tools are not generally available, and the machine-
generated HTML produced by some word processors for output purposes only.
The “Title Page” means, for a printed book, the title page itself, plus such following pages as are
needed to hold, legibly, the material this License requires to appear in the title page. For works
in formats which do not have any title page as such, “Title Page” means the text near the most
prominent appearance of the work’s title, preceding the beginning of the body of the text.

Verbatim Copying
You may copy and distribute the Document in any medium, either commercially or noncommer-
cially, provided that this License, the copyright notices, and the license notice saying this License
applies to the Document are reproduced in all copies, and that you add no other conditions what-
soever to those of this License. You may not use technical measures to obstruct or control the
reading or further copying of the copies you make or distribute. However, you may accept com-
pensation in exchange for copies. If you distribute a large enough number of copies you must also
follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and you may publicly display
copies.

Copying in Quantity
If you publish printed copies of the Document numbering more than 100, and the Document’s
license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and
legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify you as the publisher of these
copies. The front cover must present the full title with all words of the title equally prominent
and visible. You may add other material on the covers in addition. Copying with changes limited
to the covers, as long as they preserve the title of the Document and satisfy these conditions, can
be treated as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit legibly, you should put the first ones
listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.
If you publish or distribute Opaque copies of the Document numbering more than 100, you must
either include a machine-readable Transparent copy along with each Opaque copy, or state in
or with each Opaque copy a publicly-accessible computer-network location containing a complete
Transparent copy of the Document, free of added material, which the general network-using public
has access to download anonymously at no charge using public-standard network protocols. If you
Appendix B. GNU Free Documentation License 287

use the latter option, you must take reasonably prudent steps, when you begin distribution of
Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the
stated location until at least one year after the last time you distribute an Opaque copy (directly
or through your agents or retailers) of that edition to the public.
It is requested, but not required, that you contact the authors of the Document well before
redistributing any large number of copies, to give them a chance to provide you with an updated
version of the Document.

Modifications
You may copy and distribute a Modified Version of the Document under the conditions of sections
2 and 3 above, provided that you release the Modified Version under precisely this License, with
the Modified Version filling the role of the Document, thus licensing distribution and modification
of the Modified Version to whoever possesses a copy of it. In addition, you must do these things
in the Modified Version:

• Use in the Title Page (and on the covers, if any) a title distinct from that of the Document,
and from those of previous versions (which should, if there were any, be listed in the History
section of the Document). You may use the same title as a previous version if the original
publisher of that version gives permission.

• List on the Title Page, as authors, one or more persons or entities responsible for authorship
of the modifications in the Modified Version, together with at least five of the principal
authors of the Document (all of its principal authors, if it has less than five).

• State on the Title page the name of the publisher of the Modified Version, as the publisher.

• Preserve all the copyright notices of the Document.

• Add an appropriate copyright notice for your modifications adjacent to the other copyright
notices.

• Include, immediately after the copyright notices, a license notice giving the public permission
to use the Modified Version under the terms of this License, in the form shown in the
Addendum below.

• Preserve in that license notice the full lists of Invariant Sections and required Cover Texts
given in the Document’s license notice.

• Include an unaltered copy of this License.

• Preserve the section entitled “History”, and its title, and add to it an item stating at least
the title, year, new authors, and publisher of the Modified Version as given on the Title
Page. If there is no section entitled “History” in the Document, create one stating the title,
year, authors, and publisher of the Document as given on its Title Page, then add an item
describing the Modified Version as stated in the previous sentence.

• Preserve the network location, if any, given in the Document for public access to a Trans-
parent copy of the Document, and likewise the network locations given in the Document
for previous versions it was based on. These may be placed in the “History” section. You
may omit a network location for a work that was published at least four years before the
Document itself, or if the original publisher of the version it refers to gives permission.

• In any section entitled “Acknowledgements” or “Dedications”, preserve the section’s title, and
preserve in the section all the substance and tone of each of the contributor acknowledgements
and/or dedications given therein.
288 Rheolef version 7.2

• Preserve all the Invariant Sections of the Document, unaltered in their text and in their
titles. Section numbers or the equivalent are not considered part of the section titles.

• Delete any section entitled “Endorsements”. Such a section may not be included in the
Modified Version.

• Do not retitle any existing section as “Endorsements” or to conflict in title with any Invariant
Section.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary
Sections and contain no material copied from the Document, you may at your option designate
some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections
in the Modified Version’s license notice. These titles must be distinct from any other section titles.
You may add a section entitled “Endorsements”, provided it contains nothing but endorsements
of your Modified Version by various parties – for example, statements of peer review or that the
text has been approved by an organization as the authoritative definition of a standard.
You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as
a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage
of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made
by) any one entity. If the Document already includes a cover text for the same cover, previously
added by you or by arrangement made by the same entity you are acting on behalf of, you may not
add another; but you may replace the old one, on explicit permission from the previous publisher
that added the old one.
The author(s) and publisher(s) of the Document do not by this License give permission to use
their names for publicity for or to assert or imply endorsement of any Modified Version.

Combining Documents
You may combine the Document with other documents released under this License, under the terms
defined in section 4 above for modified versions, provided that you include in the combination all
of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant
Sections of your combined work in its license notice.
The combined work need only contain one copy of this License, and multiple identical Invariant
Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same
name but different contents, make the title of each such section unique by adding at the end of
it, in parentheses, the name of the original author or publisher of that section if known, or else a
unique number. Make the same adjustment to the section titles in the list of Invariant Sections in
the license notice of the combined work.
In the combination, you must combine any sections entitled “History” in the various original
documents, forming one section entitled “History”; likewise combine any sections entitled “Ac-
knowledgements”, and any sections entitled “Dedications”. You must delete all sections entitled
“Endorsements.”

Collections of Documents
You may make a collection consisting of the Document and other documents released under this
License, and replace the individual copies of this License in the various documents with a single
copy that is included in the collection, provided that you follow the rules of this License for
verbatim copying of each of the documents in all other respects.
You may extract a single document from such a collection, and distribute it individually under
this License, provided you insert a copy of this License into the extracted document, and follow
this License in all other respects regarding verbatim copying of that document.
Appendix B. GNU Free Documentation License 289

Aggregation With Independent Works


A compilation of the Document or its derivatives with other separate and independent documents
or works, in or on a volume of a storage or distribution medium, does not as a whole count
as a Modified Version of the Document, provided no compilation copyright is claimed for the
compilation. Such a compilation is called an “aggregate”, and this License does not apply to
the other self-contained works thus compiled with the Document, on account of their being thus
compiled, if they are not themselves derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if
the Document is less than one quarter of the entire aggregate, the Document’s Cover Texts may
be placed on covers that surround only the Document within the aggregate. Otherwise they must
appear on covers around the whole aggregate.

Translation
Translation is considered a kind of modification, so you may distribute translations of the Docu-
ment under the terms of section 4. Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include translations of some or all Invariant
Sections in addition to the original versions of these Invariant Sections. You may include a trans-
lation of this License provided that you also include the original English version of this License.
In case of a disagreement between the translation and the original English version of this License,
the original English version will prevail.

Termination
You may not copy, modify, sublicense, or distribute the Document except as expressly provided
for under this License. Any other attempt to copy, modify, sublicense or distribute the Document
is void, and will automatically terminate your rights under this License. However, parties who
have received copies, or rights, from you under this License will not have their licenses terminated
so long as such parties remain in full compliance.

Future Revisions of This License


The Free Software Foundation may publish new, revised versions of the GNU Free Documentation
License from time to time. Such new versions will be similar in spirit to the present version, but
may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft.
Each version of the License is given a distinguishing version number. If the Document specifies
that a particular numbered version of this License "or any later version" applies to it, you have the
option of following the terms and conditions either of that specified version or of any later version
that has been published (not as a draft) by the Free Software Foundation. If the Document does
not specify a version number of this License, you may choose any version ever published (not as
a draft) by the Free Software Foundation.

*
ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of the License in the document
and put the following copyright and license notices just after the title page:

Copyright © YEAR YOUR NAME. Permission is granted to copy, distribute and/or


modify this document under the terms of the GNU Free Documentation License, Ver-
sion 1.1 or any later version published by the Free Software Foundation; with the
290 Rheolef version 7.2

Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being
LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in
the section entitled “GNU Free Documentation License”.

If you have no Invariant Sections, write “with no Invariant Sections” instead of saying which
ones are invariant. If you have no Front-Cover Texts, write “no Front-Cover Texts” instead of
“Front-Cover Texts being LIST”; likewise for Back-Cover Texts.
If your document contains nontrivial examples of program code, we recommend releasing these
examples in parallel under your choice of free software license, such as the GNU General Public
License, to permit their use in free software.
Bibliography

E. M. Abdalass. Résolution performante du problème de Stokes par mini-éléments, maillages auto-


adaptatifs et méthodes multigrilles – applications. PhD thesis, Thèse de l’école centrale de Lyon,
1987.
L. Abouorm. Méthodes mathématiques pour les écoulements sur des surfaces. Master’s thesis,
M2R Université J. Fourier, Grenoble, 2010. URL http://www-ljk.imag.fr/membres/Pierre.
Saramito/Lara-Abouorm-m2r-2010.pdf.
P. Alart. Méthode de Newton généralisée en mécanique du contact. Journal de Mathématiques
Pures et Appliquées, 76(1):83–108, 1997.
P. R. Amestoy, I. S. Duff, J.-Y. L’Excellent, and J. Koster. A fully asynchronous multifrontal
solver using distributed dynamic scheduling. SIAM J. Matrix Anal. Appl., 23(1):15–41, 2001.
URL http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.40.4181&rep=rep1&
type=pdf.
P. R Amestoy, A. Guermouche, J.-Y. L’Excellent, and S. Pralet. Hybrid scheduling for the parallel
solution of linear systems. Parallel Comput., 32(2):136–156, 2006. URL http://hal.inria.
fr/inria-00070599/PDF/RR-5404.pdf.
D. N. Arnold and J. Qin. Quadratic velocity/linear pressure Stokes elements. Adv. Comput. Meth.
Partial Diff. Eqn., 7:28–34, 1992.
D. N. Arnold, F. Brezzi, and M. Fortin. A stable finite element for the Stokes equations. Calcolo,
21:337–344, 1984.
U. M. Ascher, S. J. Ruuth, and R. J. Spiteri. Implicit-explicit Runge-Kutta methods for time-
dependent partial differential equations. Appl. Numer. Math., 25(2):151–167, 1997.
C. Ashcraft and J. W. H. Liu. Robust ordering of sparse matrices using multisection. SIAM J.
Matrix Anal. Appl., 19(3):816–832, 1998.
F. Auteri, N. Parolini, and L. Quartapelle. Numerical investigation on the stability of singular
driven cavity flow. J. Comput. Phys., 183(1):1–25, 2002.
G. K. Batchelor. An introduction to fluid dynamics. Cambridge university press, UK, sixth edition,
1967.
R. Bird, R. C. Armstrong, and O. Hassager. Dynamics of polymeric liquids. Volume 1. Fluid
mechanics. Wiley, New-York, second edition, 1987.
H. Borouchaki, P. L. George, F. Hecht, P. Laug, B. Mohammadi, and E. Saltel. Mailleur bidi-
mensionnel de Delaunay gouverné par une carte de métriques. Partie II: applications. Technical
Report RR-2760, INRIA, 1995.
K. Boukir, Y. Maday, B. Metivet, and E. Razafindrakoto. A high-order characteristic/finite ele-
ment method for the incompressible Navier-Stokes equations. Int. J. Numer. Meth. Fluids, 25:
1421–1454, 1997.

291
292 Rheolef version 7.2

S. C. Brenner and L. R. Scott. The mathematical theory of finite element methods. Springer,
second edition, 2002.
H. Brezis. Analyse fonctionnelle. Théorie et application. Masson, Paris, 1983.
F. Brezzi and J. Pitkäranta. On the stabilization of finite element approximation of the Stokes
equations. In Efficient solutions of elliptic systems, Kiel, Notes on numerical fluid mechanics,
volume 10, pages 11–19, 1984.
A. Caglar and A. Liakos. Weak imposition of boundary conditions for the Navier-Stokes equations
by a penalty method. Int. J. Numer. Meth. Fluids, 61(4):411–431, 2009.
M. P. Calvo, J. de Frutos, and J. Novo. Linearly implicit Runge-Kutta methods for advection-
reaction-diffusion equations. Appl. Numer. Math., 37(4):535–549, 2001.
G. F. Carey and B. Jianng. Least-squares finite elements for first-order hyperbolic systems. Int.
J. Numer. Meth. Eng., 26(1):81–93, 1988.
P. Castillo. Performance of discontinuous Galerkin methods for elliptic PDEs. SIAM J. Sci.
Comput., 24(2):524–547, 2002.
M. J. Castro-Diaz, F. Hecht, B. Mohammadi, and O. Pironneau. Anisotropic unstructured mesh
adaption for flow simulations. Int. J. Numer. Meth. Fluids, 25(4):475–491, 1997.
J. C. Chrispell, V. J. Ervin, and E. W. Jenkins. A fractional step θ-method approximation of
time-dependent viscoelastic fluid flow. J. Comput. Appl. Math., 232(2):159–175, 2009.
B. Cockburn. An introduction to the discontinuous Galerkin method for convection-dominated
problems, chapter 2, pages 151–268. Springer, 1998.
B. Cockburn, S. Hou, and C.-W. Shu. The Runge-Kutta local projection discontinuous Galerkin
finite element method for conservation laws. IV. the multidimensional case. Math. Comput., 54
(190):545–581, 1990.
B. Cockburn, G. Kanschat, D. Schötzau, and C. Schwab. Local discontinuous Galerkin methods
for the Stokes system. SIAM J. Numer. Anal., 40(1):319–343, 2002.
B. Cockburn, G. Kanschat, and D. Schötzau. A locally conservative LDG method for the incom-
pressible Navier-Stokes equations. Math. Comput., 74(251):1067–1095, 2005.
B. Cockburn, B. Dong, and J. Guzmán. A superconvergent LDG-hybridizable Galerkin method
for second-order elliptic problems. Math. Comput., 77(264):1887–1916, 2008.
B. Cockburn, J. Guzmán, and H. Wang. Superconvergent discontinuous Galerkin methods for
second-order elliptic problems. Math. Comput., 78(265):1–24, 2009.
B. Cockburn, B. Dong, J. Guzmán, and J. Qian. Optimal convergence of the original DG method
on special meshes for variable transport velocity. SIAM J. Numer. Anal., 48(1):133–146, 2010a.
B. Cockburn, J. Gopalakrishnan, and F.-J. Sayas. A projection-based error analysis of HDG
methods. Math. Comput., 79(271):1351–1367, 2010b.
B. Cockburn, D. A. di Pietro, and A. Ern. Bridging the hybrid high-order and hybridizable
discontinuous Galerkin methods. ESAIM Math. Model. Numer. Anal., 50(3):635–650, 2016.
M. Crouzeix and J. Rappaz. On numerical approximation in bifurcation theory. Masson, Paris,
1990.
K. Deckelnick, G. Dziuk, C.M. Elliott, and C.-J. Heine. An h-narrow band finite element method
for elliptic equations on implicit surfaces. IMA Journal of Numerical Analysis, to appear:0,
2009.
Bibliography 293

D. A. di Pietro and A. Ern. Discrete functional analysis tools for discontinuous Galerkin methods
with application to the incompressible Navier-Stokes equations. Math. Comp., 79:1303–1330,
2010.

D. A. di Pietro and A. Ern. Mathematical aspects of discontinuous Galerkin methods. Springer,


2012.

D. A. di Pietro and A. Ern. Hybrid high-order methods for variable-diffusion problems on general
meshes. C. R. Math., 353(1):31–34, 2015.

D. A. di Pietro, S. Lo Forte, and N. Parolini. Mass preserving finite element implementations of


the level set method. Appl. Numer. Math., 56(9):1179–1195, 2006.

M. Dicko. Méthodes mathématiques pour les écoulements sur des surfaces. Master’s thesis,
M2P Université J. Fourier, Grenoble, 2011. URL http://www-ljk.imag.fr/membres/Pierre.
Saramito/mahamar-dicko-m2r.pdf.

J. Donea and A. Huerta. Finite element methods for flow problems. Wiley, New-York, 2003.

M. Dryja. On discontinuous Galerkin methods for elliptic problems with discontinuous coefficients.
Comput. Meth. Appl. Math., 3(1):76–85, 2003.

D. Enright, R. Fedkiw, J. Ferziger, and I. Mitchell. A hybrid particle level set method for improved
interface capturing. J. Comput. Phys., 183(1):83–116, 2002.

Y. Epshteyn and B. Rivière. Estimation of penalty parameters for symmetric interior penalty
Galerkin methods. J. Comput. Appl. Math., 206(2):843–872, 2007.

E. Erturk, T. C. Corke, and C. Gökçol. Numerical solutions of 2-D steady incompressible driven
cavity flow at high Reynolds numbers. Int. J. Numer. Meth. Fluids, 48:747–774, 2005.

G. Fourestey and S. Piperno. A second-order time-accurate ALE Lagrange-Galerkin method


applied to wind engineering and control of bridge profiles. Comput. Methods Appl. Mech. Engrg.,
193:4117–4137, 2004.

T. Gelhard, G. Lube, M. A. Olshanskii, and J. H. Starcke. Stabilized finite element schemes with
LBB-stable elements for incompressible flows. J. Comput. Appl. Math., 177:243–267, 2005.

A. George. Nested dissection of a regular finite element mesh. SIAM J. Numer. Anal., 10:345–363,
1973.

C. Geuzaine and J.-F. Remacle. Gmsh: a three-dimensional finite element mesh generator with
built-in pre- and post-processing facilities. Int. J. Numer. Meths Engrg., 79(11):1309–1331,
2009.

U. Ghia, K. N. Ghia, and C. T. Shin. High Re solutions for incompressible flow using the Navier-
Stokes equations and a multigrid method. J. Comput. Phys., 48:387–411, 1982.

V. Girault and P. A. Raviart. Finite element methods for the Navier-Stokes equations. Theory
and algorithms. Springer, 1986.

S. Gottlieb and C.-W. Shu. Total variation diminishing Runge-Kutta schemes. Math. Comput.,
67(221):73–85, 1998.

S. Gottlieb, Chi-W. Shu, and E. Tadmor. Strong stability-preserving high-order time discretization
methods. SIAM review, 43(1):89–112, 2001.

M. M. Gupta and J. C. Kalita. A new paradigm for solving Navier-Stokes equations:


streamfunction-velocity formulation. J. Comput. Phys., 207:52–68, 2005.
294 Rheolef version 7.2

A. Harten, B. Engquist, S. Osher, and S. R. Chakravarthy. Uniformly high order accurate essen-
tially non-oscillatory schemes, III. J. Comput. Phys., 71(2):231–303, 1987.
F. Hecht. BAMG: bidimensional anisotropic mesh generator, 2006. https://www.ljll.math.
upmc.fr/hecht/ftp/bamg.
J. S. Hesthaven and T. Warburton. Nodal discontinuous Galerkin methods. Algorithms, analysis
and applications. Springer, 2008.
A. J. Hoffman, M. S. Martin, and D. J. Rose. Complexity bounds for regular finite difference and
finite element grids. SIAM J. Numer. Anal., 10(2):364–369, 1973.
P. Hood and C. Taylor. A numerical solution of the Navier-Stokes equations using the finite
element technique. Comp. and Fluids, 1:73–100, 1973.
J. S. Howell. Computation of viscoelastic fluid flows using continuation methods. J. Comput.
Appl. Math., 225(1):187–201, 2009.
Jr. J. E. Dennis and R. B. Schnablel. Numerical methods for unconstraint optimization and
nonlinear equations. Prentice Hall, Englewood Cliff, N. J., 1983.
C. Johnson and J. Pitkäranta. An analysis of the discontinuous Galerkin method for a scalar
hyperbolic equation. Math. Comp., 46(173):1–26, 1986.
A. Klawonn. An optimal preconditioner for a class of saddle point problems with a penalty term.
SIAM J. Sci. Comput, 19(2):540–552, 1998.
A. Laadhari, C. Misbah, and P. Saramito. On the equilibrium equation for a generalized biological
membrane energy by using a shape optimization approach. Phys. D, 239:1568–1572, 2010.
R. J. Labeur and G. N. Wells. A Galerkin interface stabilisation method for the advection-diffusion
and incompressible Navier-Stokes equations. Comput. Meth. Appl. Mech. Engrg., 196(49–52):
4985–5000, 2007.
R. J. LeVeque. High-resolution conservative algorithms for advection in incompressible flow. SIAM
J. Numer. Anal., 33(2):627–665, 1996.
A. Liakos. Discretization of the Navier-Stokes equations with slip boundary condition. Numer.
Meth. Part. Diff. Eqn., 17(1):26–42, 2001.
M. Lukáčová-Medvidová, H. Mizerová, Š. Nečasová, and M. Renardy. Global existence result for
the generalized Peterlin viscoelastic model. SIAM J. Math. Anal., 49(4):2950–2964, 2017.
J. Málek, V. Pruša, T. Skřivan, and E. Süli. Thermodynamics of viscoelastic rate-type fluids with
stress diffusion. Phys. Fluids, 30(2):023101, 2018.
E. Marchandise, J.-F. Remacle, and N. Chevaugeon. A quadrature-free discontinuous Galerkin
method for the level set equations. J. Comput. Phys., 212:338–357, 2006.
S. Melchior, V. Legat, P. Van Dooren, and A. J. Wathen. Analysis of preconditioned iterative
solvers for incompressible flow problems. Int. J. Numer. Meth. Fluids, 68(3):269–286, 2012.
P. D. Minev and C. R. Ethier. A characteristic/finite element algorithm for the 3-D Navier-Stokes
equations using unstructured grids. Comput. Meth. in Appl. Mech. and Engrg., 178(1-2):39–50,
1998.
P. P. Mosolov and V. P. Miasnikov. Variational methods in the theory of the fluidity of a viscous-
plastic medium. J. Appl. Math. Mech., 29(3):545–577, 1965.
P. P. Mosolov and V. P. Miasnikov. On stagnant flow regions of a viscous-plastic medium in pipes.
J. Appl. Math. Mech., 30(4):841–853, 1966.
Bibliography 295

P. P. Mosolov and V. P. Miasnikov. On qualitative singularities of the flow of a viscoplastic medium


in pipes. J. Appl. Math. Mech., 31(3):609–613, 1967.

D. R. Musser and A. Saini. C++ STL tutorial and reference guide. Addison Wesley, Reading,
1996a.

D. R. Musser and A. Saini. STL tutorial and reference guide. Addison-Wesley, 1996b.

N. C. Nguyen, J. Peraire, and B. Cockburn. Hybridizable discontinuous Galerkin methods. In


Spectral and high order methods for partial differential equations, pages 63–84. Springer, 2011.

J. G. Oldroyd. On the formulation of rheological equations of states. Proc. R. Soc. Lond. A, 200:
523–541, 1950.

M. A. Olshanskii and A. Reusken. A finite element method for surface PDEs: matrix properties.
Numer. Math., 114:491–520, 2010. URL http://www.mathcs.emory.edu/~molshan/ftp/pub/
interfaceLA.pdf.

M. A. Olshanskii, A. Reusken, and J. Grande. A finite element method for elliptic equations on
surfaces. SIAM J. Num. Anal., 47(5):3339–3358, 2009. URL http://www.mathcs.emory.edu/
~molshan/ftp/pub/PaperInterface.pdf.

M. A. Olshanskii, A. Reusken, and X. Xu. On surface meshes induced by level set functions.
Computing and visualization in science, 15(2):53–60, 2012.

S. Osher and J. A. Sethian. Front propaging with curvature-dependent speed: agorithms based
on Hamilton-Jacobi formulations. J. Comput. Phys., 79(12), 1988. URL http://citeseerx.
ist.psu.edu/viewdoc/download?doi=10.1.1.46.1266&rep=rep1&type=pdf.

M. L. Ould Salihi. Couplage de méthodes numériques en simulation directe d’écoulements incom-


pressibles. PhD thesis, Université J. Fourier, Grenoble, 1998.

O. Ozenda, P. Saramito, and G. Chambon. A new rate-independent tensorial model for suspensions
of non-colloidal rigid particles in newtonian fluids. J. Rheol., 62:889–903, 2018.

C. C. Paige and M. A. Saunders. Solution of sparse indefinite systems of linear equations. SIAM J.
Numer. Anal., 12(4):617–629, 1975. URL http://www.stanford.edu/group/SOL/software.
html.

T.-W. Pan, J. Hao, and R. Glowinski. On the simulation of a time-dependent cavity flow of an
Oldroyd-B fluid. Int. J. Numer. Meth. Fluids, 60(7):791–808, 2009.

J.-C. Paumier. Bifurcation et méthodes numériques. Applications aux problèmes elliptiques semi-
linéaires. Masson, Paris, 1997.

F. Pellegrini. PT-Scotch and libscotch 5.1 user’s guide. Université de Bordeaux and INRIA,
France, 2010. URL http://www.labri.fr/~pelegrin/scotch.

T. E. Peterson. A note on the convergence of the discontinuous Galerkin method for a scalar
hyperbolic equation. SIAM J. Numer. Anal., 28(1):133–140, 1991.

D. A. Di Pietro and J. Droniou. The hybrid high-order method for polytopal meshes: design,
analysis, and applications. Springer, 2020.

O. Pironneau. Méthode des éléments finis pour les fluides. Masson, Paris, 1988.

O. Pironneau and M. Tabata. Stability and convergence of a galerkin-characteristics finite element


scheme of lumped mass type. Int. J. Numer. Meth. Fluids, 64:1240–1253, 2010.
296 Rheolef version 7.2

W. H. Press, S. A. Teulkolsky, W. T. Vetterling, and B. P. Flannery. Numerical recepies in C.


The art of scientific computing. Cambridge University Press, UK, second edition, 1997. Version
2.08.

P.-A. Raviart and J.-M. Thomas. A mixed finite element method for 2-nd order elliptic problems.
In Mathematical aspects of finite element methods, pages 292–315. Springer, 1977.

G. R. Richter. An optimal-order error estimate for the discontinuous galerkin method. Math.
Comput., 50(181):75–88, 1988.

J. E. Roberts and J.-M. Thomas. Mixed and hybrid methods. In P. G. Ciarlet and J.-L. Lions,
editors, Handbook of numerical analysis. Volume 2. Finite element methods (part 1), chapter 4,
pages 524–639. Elsevier, 1991.

N. Roquet and P. Saramito. An adaptive finite element method for Bingham fluid flows around
a cylinder. Comput. Meth. Appl. Mech. Engrg., 192(31-32):3317–3341, 2003. URL http://
www-ljk.imag.fr/membres/Pierre.Saramito/cylindre.pdf.

N. Roquet and P. Saramito. Stick-slip transition capturing by using an adaptive finite element
method. M2AN, 38(2):249–260, 2004. URL http://archive.numdam.org/article/M2AN_
2004__38_2_249_0.djvu.

N. Roquet and P. Saramito. An adaptive finite element method for viscoplastic flows in a square
pipe with stick-slip at the wall. J. Non-Newt. Fluid Mech., 155:101–115, 2008.

N. Roquet, R. Michel, and P. Saramito. Errors estimate for a viscoplastic fluid by using Pk finite
elements and adaptive meshes. C. R. Acad. Sci. Paris, ser. I, 331(7):563–568, 2000. URL
http://www-ljk.imag.fr/membres/Pierre.Saramito/cr2000.pdf.

H. Rui and M. Tabata. A second order characteristic finite element scheme for convection diffusion
problems. Numer. Math. (to appear), 2001.

P. Saramito. Simulation numérique d’écoulements de fluides viscoélastiques par éléments finis


incompressibles et une méthode de directions alternées; applications. PhD thesis, Institut Na-
tional Polytechnique de Grenoble, 1990. URL http://www-ljk.imag.fr/membres/Pierre.
Saramito/these.pdf.

P. Saramito. Numerical simulation of viscoelastic fluid flows using incompressible finite element
method and a θ-method. Math. Model. Numer. Anal., 28(1):1–35, 1994. URL http://archive.
numdam.org/article/M2AN_1994__28_1_1_0.pdf.

P. Saramito. Efficient simulation of nonlinear viscoelastic fluid flows. J. Non Newt. Fluid Mech.,
60:199–223, 1995. URL http://www-ljk.imag.fr/membres/Pierre.Saramito/jnnfm2.pdf.

P. Saramito. Operator splitting in viscoelasticity. Élasticité, Viscoélasticité et Contrôle Optimal,


Lyon, décembre 1995, ESAIM: Proceedings, 2:275–281, 1997. URL http://www.esaim-proc.
org/articles/proc/pdf/1997/01/saramito.pdf.

P. Saramito. Rheolef home page. https://www-ljk.imag.fr/membres/Pierre.Saramito/


rheolef, 2012a.

P. Saramito. Language C++ et calcul scientifique. College Publications, London, 2013a.

P. Saramito. Méthodes numériques en fluides complexes : théorie et algorithmes. CNRS-CCSD,


2013b. http://cel.archives-ouvertes.fr/cel-00673816.

P. Saramito. On a modified non-singular log-conformation formulation for Johnson-Segalman


viscoelastic fluids. J. Non-Newt. Fluid Mech., 211:16–30, 2014.
Bibliography 297

P. Saramito. A damped Newton algorithm for computing viscoplastic fluid flows. J. Non-Newt.
Fluid Mech., 238:6–15, 2016a.
P. Saramito. Complex fluids: modelling and algorithms. Springer, 2016b.
P. Saramito and N. Roquet. An adaptive finite element method for viscoplastic fluid flows in pipes.
Comput. Meth. Appl. Mech. Engrg., 190(40-41):5391–5412, 2001. URL http://www-ljk.imag.
fr/membres/Pierre.Saramito/publi-poiseuille.pdf.
P. Saramito and A. Wachs. Progress in numerical simulation of yield stress fluid flows. J. Rheol.,
56(3):211–230, 2017.
Pierre Saramito. Are curved and high order gmsh meshes really high order ?, 2012b. http:
//www.geuz.org/pipermail/gmsh/2012/006967.html.
L. R. Scott and M. Vogelius. Norm estimates for a maximal right inverse of the divergence operator
in spaces of piecewise polynomials. M2AN, 19(1):111–143, 1985.
N. D. Scurtu. Stability analysis and numerical simulation of non-Newtonian fluids of Oldroyd
kind. PhD thesis, U. Nürnberg, Deutschland, 2005.
J. Sethian. Level set methods and fast marching methods. Cambridge University Press, UK, 1999.
K. Shahbazi. An explicit expression for the penalty parameter of the interior penalty method. J.
Comput. Phys., 205(2):401–407, 2005.
J. Shen. Hopf bifurcation of the unsteady regularized driven cavity flow. J. Comp. Phys., 95:
228–245, 1991. http://www.math.purdue.edu/~shen/pub/Cavity.pdf.
C.-W. Shu. TVB boundary treatment for numerical solutions of conservation laws. Math. Comput.,
49(179):123–134, 1987.
C.-W. Shu and S. Osher. Efficient implementation of essentially non-oscillatory shock-capturing
schemes. J. Comput. Phys., 77(2):439–471, 1988.
P. Singh and L. G. Leal. Finite-element simulation of the start-up problem for a viscoelastic fluid
in an eccentric rotating cylinder geometry using a third-order upwind scheme. Theor. Comput.
Fluid Dyn., 5(2-3):107–137, 1993.
B. Stroustrup. C++ programming styles and libraries. InformIt.com, 0:0, 2002.
E. Süli and D. F. Mayers. An introduction to numerical analysis. Cambridge University Press,
UK, 2003.
M. Ta, F. Pigeonneau, and P. Saramito. An implicit high order discontinuous Galerkin level
set method for two-phase flow problems. In 9th international conference on multiphase flow
(ICMF), 2016.
G. I. Taylor. On the decay of vortices in a viscous fluid. Philos. Mag., 46:671–674, 1923.
M.-G. Vallet. Génération de maillages anisotropes adaptés, application à la capture de couches
limites. Technical Report RR-1360, INRIA, 1990.
R. Verfürth. Finite element approximation of steady Navier-Stokes equations with mixed boundary
conditions. ESAIM Math. Model. and Numer. Anal., 19(3):461–475, 1985.
R. Verfürth. Finite element approximation of incompressible Navier-Stokes equations with slip
boundary condition. Numer. Math., 50:697–721, 1987.
R. Verfürth. Finite element approximation of incompressible Navier-Stokes equations with slip
boundary conditions II. Numer. Math., 59:615–636, 1991.
298 Rheolef version 7.2

H. Wang, C.-W. Shu, and Q. Zhang. Stability and error estimates of local discontinuous Galerkin
methods with implicit-explicit time-marching for advection-diffusion problems. SIAM J. Numer.
Anal., 53(1):206–227, 2015a.
H. Wang, C.-W. Shu, and Q. Zhang. Stability analysis and error estimates of local discontinu-
ous Galerkin methods with implicit–explicit time-marching for nonlinear convection–diffusion
problems. Appl. Math. Comput., 2015b.
Wikipedia. The Stokes stream function. Wikipedia, 2012. http://en.wikipedia.org/wiki/
Stokes_stream_function.

N. Wirth. Algorithm + data structure = programs. Prentice Hall, NJ, USA, 1985.
S. T. Zalesak. Fully multidimensional flux-corrected transport algorithms for fluids. J. Comput.
Phys., 31(3):335–362, 1979.
S. Zhang. A new family of stable mixed finite elements for the 3D Stokes equations. Math. Comput,
74(250):543–554, 2005.

X. Zhong and C.-W. Shu. A simple weighted essentially nonoscillatory limiter for Runge-Kutta
discontinuous galerkin methods. J. Comput. Phys., 232(1):397–415, 2013.
List of example files

Makefile, 12 embankment.cc, 40
burgers.icc, 157 embankment.icc, 41
burgers_dg.cc, 159 embankment_adapt.cc, 46
burgers_diffusion_dg.cc, 173 eta.h, 110
burgers_diffusion_exact.h, 172 harten.h, 157
burgers_diffusion_operators.icc, 174 harten_show.cc, 158
burgers_flux_godunov.icc, 157 heat.cc, 73
cavity.h, 50 helmholtz_band.cc, 102
cavity_dg.h, 191 helmholtz_band_iterative.cc, 100
combustion.h, 127 helmholtz_s.cc, 90
combustion1.icc, 127 incompressible-elasticity.cc, 57
combustion2.icc, 128 inertia.h, 184
combustion_continuation.cc, 133 inertia_cks.icc, 187
combustion_error.cc, 131 inertia_upw.icc, 192
combustion_exact.icc, 131 lambda2alpha.h, 131
combustion_keller.cc, 139 lambda_c.cc, 130
combustion_keller_post.cc, 140 lambda_c.h, 130
combustion_newton.cc, 129 laplace_band.cc, 103
commute_rtd.cc, 247 laplace_s.cc, 94
contraction.h, 60 level_set_sphere.cc, 97
convect.cc, 75 leveque_dg.cc, 151
convect_error.cc, 77 mosolov_augmented_lagrangian.cc, 216
cosinusprod.h, 23 mosolov_augmented_lagrangian.h, 215
cosinusprod_error.cc, 23 mosolov_augmented_lagrangian1.icc, 214
cosinusprod_laplace.h, 21 mosolov_augmented_lagrangian2.icc, 215
cosinusrad.h, 24 mosolov_error_yield_surface.cc, 222
cosinusrad_laplace.h, 23 mosolov_exact_circle.h, 220
d_projection_dx.h, 207 mosolov_yield_surface.cc, 219
diffusion_transport_tensor_dg.cc, 242 navier_stokes_cavity.cc, 82
dirichlet-nh.cc, 20 navier_stokes_criterion.icc, 83
dirichlet.cc, 10 navier_stokes_dg.h, 189
dirichlet.icc, 110 navier_stokes_dg1.icc, 189
dirichlet_dg.cc, 164 navier_stokes_dg2.icc, 191
dirichlet_hdg.cc, 251 navier_stokes_solve.icc, 81
dirichlet_hdg_average.cc, 254 navier_stokes_taylor_dg.cc, 184
dirichlet_hdg_average.icc, 254 navier_stokes_taylor_newton_dg.cc, 189
dirichlet_hdg_post.cc, 256 navier_stokes_upw_dg.h, 193
dirichlet_hdg_post_rt.cc, 259 navier_stokes_upw_dg.icc, 193
dirichlet_hho.cc, 270 neumann-laplace.cc, 32
dirichlet_homogeneous.h, 271 neumann-nh.cc, 28
elasticity_criterion.icc, 47 neumann_dg.cc, 166
elasticity_solve.icc, 47 nu.h, 118
elasticity_taylor_dg.cc, 177 oldroyd_contraction.cc, 235

299
300 Rheolef version 7.2

oldroyd_contraction.h, 235 contraction.mshcad, 62, 66


oldroyd_theta_scheme.h, 231 convect.cc, 82
oldroyd_theta_scheme1.h, 232 cosinus_vector.h, 247
oldroyd_theta_scheme2.h, 232 cosinusprod_error_dg.cc, 166
oldroyd_theta_scheme3.h, 233 cosinusrad_error.cc, 25
p_laplacian.h, 116 cube.mshcad, 279
p_laplacian1.icc, 117 diffusion_isotropic.h, 264
p_laplacian2.icc, 117 diffusion_transport_tensor_error_dg.cc,
p_laplacian_circle.h, 125 243
p_laplacian_damped_newton.cc, 122 diffusion_transport_tensor_exact.icc,
p_laplacian_error.cc, 125 242
p_laplacian_fixed_point.cc, 108 dirichlet_nh_ball.cc, 25
p_laplacian_newton.cc, 116 elasticity_taylor_error_dg.cc, 178
phi.h, 201 harten0.h, 157
poisson_robin.icc, 202 helmholtz_s_error.cc, 93
proj_band.cc, 101 level_set_torus.cc, 98
projection.h, 200 leveque.h, 151
reconstruction_hho.cc, 263 line.mshcad, 49, 278
robin.cc, 30 mkview_mosolov, 219
rotating-hill.h, 77 mosolov_error.cc, 221
sgn.icc, 193 mosolov_residue.cc, 217
sinusprod_helmholtz.h, 29 navier_stokes_cavity_newton_dg.cc, 191
sphere.icc, 91 navier_stokes_cavity_newton_upw_dg.cc,
stokes_cavity.cc, 50 193
stokes_contraction_bubble.cc, 60 navier_stokes_taylor_error_dg.cc, 185
stokes_dirichlet_dg.icc, 180 neumann-nh.cc, 90
stokes_obstacle_slip_regul.cc, 70 runge_kutta_semiimplicit.icc, 173
stokes_taylor_dg.cc, 180 runge_kutta_ssp.icc, 154
streamf_cavity.cc, 55 sinusprod.h, 264
streamf_contraction.cc, 64 sinusprod_dirichlet.h, 251
streamf_obstacle_slip_move.cc, 70 sinusprod_error_hdg.cc, 251
stress.cc, 43 sinusprod_error_hdg_average.cc, 254
taylor.h, 178 sinusprod_error_hdg_post_rt.cc, 259
torus.icc, 94 sinusprod_error_hho_reconstruction.cc,
transmission.cc, 35 264
transmission_dg.cc, 168 sinusprod_grad.h, 251
transport_dg.cc, 145 square.bamgcad, 48, 84, 277
transport_tensor_dg.cc, 227 square.dmn, 48, 84, 277
vector_projection.h, 214 square.mshcad, 278
vortex_position.cc, 86 stokes_contraction.cc, 64
vorticity.cc, 53 stokes_taylor_error_dg.cc, 181
yield_slip.h, 208 streamf_cavity.cc, 85
yield_slip1.icc, 208 streamf_contraction.cc, 66, 237
yield_slip2.icc, 209 stress.cc, 68
yield_slip_augmented_lagrangian.cc, taylor.h, 181, 185
202 taylor_exact.h, 178
yield_slip_augmented_lagrangian.icc, torus.mshcad, 95
202 transport_tensor_error_dg.cc, 228
yield_slip_damped_newton.cc, 207 transport_tensor_exact.icc, 228
zalesak_dg.cc, 148 yield_slip_circle.h, 211
bdf.icc, 147 yield_slip_error.cc, 211
cavity.h, 83 yield_slip_residue.cc, 204
commute_rtd_error.cc, 247 zalesak.h, 148
contraction.h, 235
List of commands

bamg2geo, 277 geo, 13


bamg, 48, 84, 277 -cut, 15
-splitpbedge, 111 -fill, 15
branch, 77 -full, 15
-branch, 134, 140, 237 -gnuplot, 96
-bw, 150, 152 -paraview, 277
-extract, 134, 140, 237 -shrink, 15
-gnuplot, 175 -stereo, 15, 91
-iso, 150, 152 -subdivide, 92
-toc, 134, 140, 237 gmsh, 49, 63, 66, 91, 95, 111, 278
-umax, 175 gnuplot, 13–15, 36, 43, 74, 77, 277
-umin, 175 gzip, 48
-volume, 75, 80 library
-vtk, 150 boost, 11
convect, 77 CGAL, computational geometry, 75
field, 13 MPI, message passing interface, 11
-, 15 mumps, linear system direct solver, 17
-bw, 13, 64, 67, 85, 101 scotch, mesh partition library, 17
-comp, 43, 46, 67, 68, 85 STL, standard template library, 77
-cut, 67, 68, 85, 110, 204 make, 12
-domain, 204 man, 14
-elevation, 13, 45, 74, 101, 110, mkgeo_ball, 91
204 -e, 91
-fill, 43 -s, 91
-gnuplot, 13, 14, 67, 68, 85, -t, 25, 91
110, 204 mkgeo_contraction, 236
-gray, 13 -split, 236
-mark, 52 mkgeo_grid, 13, 77, 111
-max, 65, 86, 134 -H, 16
-min, 86 -T, 15, 80
-n-iso, 64, 219 -a, 77
-n-iso-negative, 64, 67, 85, 219, -b, 77
237 -c, 79
-noclean, 56 -d, 79
-noexecute, 56 -e, 15
-nofill, 13, 42, 48 -f, 80
-normal, 67, 68, 85, 110, 204 -g, 80
-origin, 67, 68, 85, 110, 204 -q, 16
-proj, 45, 219 -region, 36
-scale, 58 -t, 13
-stereo, 13, 15, 43, 45, 46, 91, -zr, 65
101 mkgeo_obstacle, 71
-velocity, 52, 85 mkgeo_sector, 218
-volume, 16, 22 mkgeo_ugrid, 23, 111

301
302 Rheolef version 7.2

mpirun, 18, 37, 85, 150, 152, 218,


219, 237
msh2geo, 63, 278, 279
-zr, 66
paraview, 13, 15, 45, 46, 56, 74, 79,
150, 219, 277
rheolef-config, 9
–check, 9
–docdir, 9
–exampledir, 9
sed, 25
time, 85
visualization
mesh, 13
deformed, 42
vlc, 74
zcat, 85
Index

approximation, 10 Robin, 30, 69, 199


P0, 45, 144 slip, 68
P1b-P1, 58 weakly imposed, 143, 163, 227, 235
P1d, 45 broken Sobolev space H 1 (Th ), 163, 172
P1, 11, 41, 45, 57, 58, 80, 181
P2-P1, Taylor-Hood, 50, 58, 59, 64, 70 class
P2-P1d, Scott-Vogelius, 230 Float, 21
P2, 11, 15, 41, 45, 57, 80, 181 adapt_option, 48
Pk, 11, 15 band, 100
Raviart-Thomas, 245, 258 branch, 73, 79
bubble, 58 characteristic, 76
discontinuous eye, 101
trace, 258, 265 field, 11
discontinuous, 35, 44, 45, 53, 143, 148, form, 11
150, 226, 241, 245 geo, 11
high-order, 11, 15, 37
integrate_option, 60, 66, 76, 126
isoparametric, 92
level_set_option, 97
mixed, 50
man, 12, 43, 51, 101, 116
geometry
odiststream, 47
curved, 23
pbl em, 12
high-order, 23, 221
point, 21
isoparametric, 23, 221
problem_mixed, 51, 57
Raviart-Thomas, 258
problem, 11
argc, argv, command line arguments, 11
reference manual, 12, 43, 51, 101, 116
Babuška paradox, 70 solver_abtb, 82
benchmark solver_option, 16, 169
driven cavity flow, 49, 80, 179, 181 solver, 12, 16
Dziuk-Elliott-Heine on a sphere, 91 space, 11
embankment, 39, 177 compilation, 12
flow in an abrupt contraction, 59, 66 convergence
Leveque vortex-in-box, 150 error
Olshanskii-Reusken-Grande on a torus, superconvergence, 253, 254
95 versus mesh, 22, 93, 124, 135, 145, 165
pipe flow, 107 versus polynomial degree, 22, 93, 124
rotating hill, 75 postprocessing, 255
Zalesak slotted disk, 148 residue
boundary condition rate, 111, 113
Dirichlet, 10, 20, 35, 39, 49, 56, 80, 107, super-linear, 118
115, 163, 166, 167, 179, 181, 199, coordinate system
261 axisymmetric, 62, 64, 65
mixed, 56, 59, 66 Cartesian, 21, 36
Neumann, 28, 31, 35, 39, 56 spherical, 91
Poiseuille flow, 62 torus, 95

303
304 Rheolef version 7.2

directory of example files, 9, 62, 64, 211 on the boundary, 29


distributed computation, 11, 18, 218, 237 interpolate, 22, 47, 78
ldlt, 33
element shape, 37 level_set, 97
error analysis, 22, 79, 93, 124, 135 newton, 116
norm2, 47, 83, 109
file format sqr, 47
‘.avi’ avi file (video), 74 class-function object, 21, 77, 118
‘.bamgcad’ bamg geometry, 48, 277 functor, 22, 126
‘.bamg’ bamg mesh, 48, 277
‘.branch’ family of fields, 74 geometry
‘.dmn’ domain names, 277 axisymmetric, 62, 64
‘.field’ field, 13 circle, 25, 91
‘.field’ multi-component field, 42 contraction, 59, 66, 234
‘.geo’ mesh, 13, 63, 65, 277–279 cube, 15, 279
‘.gz’ gzip compressed file, 48 curved, 92
‘.mshcad’ gmsh geometry, 49, 63, 66, 95, line, 15, 278
278 obstacle, 68
‘.msh’ gmsh mesh, 49, 63, 66, 278 pipe, 199, 213
‘.vtk’ vtk file, 56 sphere, 91
form square, 13, 278
(η∇u).∇v, 117 surface, 89
2D(u) : D(v), 50, 56 curvature, 276
2D(u) : D(v) + λdiv u div v, 41 torus, 95
2D(u) : D(v) + u.v, 81 Green formula, 90, 275
η∇u.∇v, 35, 109
[[u]]{{∇h v.n}}, 163, 166, 173 interface
[[u]]{{∇h v.n}}w , 168 moving, 146
[[u]]{{v}}, 144 yield surface, 219
[[u]][[v]], 144, 163, 166, 168, 173 internal sides of a mesh, 144
∇s u.∇s v + uv, 90
∇u.∇v, 10 Lagrange
∇u.∇v + uv, 29 interpolation, 20, 22, 28, 32
bcurl(u).ξ, 64 multiplier, 32, 246
curl(u).ξ, 53 node, 12
div(u) q, 50, 56 Lamé coefficients, 39
energy, 10, 35, 107
product, 60 Makefile, 12
weighted, 35, 109 matrix
quadrature formula, 117 bloc-diagonal
tensorial weight, 117 inverse, 60
formal adjoint, 206 block structure, 12
Fréchet derivative, 115 concatenation, 33
function diagonal, 101
adapt, 46, 48 factorization
catchmark, 41, 52, 73 Choleski, 12
compose, 76, 83, 109, 117 identity, 101
damped_newton, 122 indefinite, 33
diag, 101 Schur complement, 251, 263
dis_wall_time, 16 singular, 33, 101
grad, 109, 117 mesh, 11, 276
integrate, 11, 29, 60, 64, 76, 90, 109, adaptation, 218, 221
117, 126 anisotropic, 46, 83
on a band, 101 connected components, 102
Index 305

generation, 63, 66, 276 problem


method Bingham, 213
augmented Lagrangian, 199, 213 Helmholtz, 28
BDF(p), backward differentiation for- Herschel-Bulkley, 213
mula, 147 Mosolov, 213
BDF2 scheme, 81 Navier-Stokes, 80, 181
characteristic, 75, 80 Poisson, 10, 20, 30, 31, 73, 107, 109, 163,
conjugate gradient algorithm, 31, 51, 57 166, 167, 199
continuation, 133 Stokes, 58, 80, 179, 181
Euler explicit scheme, 154 combustion, 127
Euler implicit scheme, 72, 75 convection-diffusion, 75
fixed-point, 107, 118 diffusion, 261
relaxation, 108, 113 elasticity, 39, 177
level set, 89, 97, 146 heat, 72
banded, 100 linear tangent, 115
minres algorithm, 31, 101 nonlinear, 80, 181
Newton, 115 p-Laplacian, 107
damped, 121 stabilized Stokes, 60
newton, 127, 130, 133, 201, 204, 206, 218 transmission, 34, 167
regularization, 69 yield slip, 199
Runge-Kutta scheme, 147, 154, 173 Burgers equation, 157
upwind scheme, 192 elasticity
incompressible, 56
namespace hyperbolic nonlinear equation, 153
rheolef, 11 oldroyd, 229
std, 11 Poisson
norm non-constant tensorial coefficients,
in H(div), 260 115
in W −1,p , 111 Stokes, 49, 68
discrete version, 112 transport equation
in W 1,p , 107 evolution, 148, 150
in W01,p , 107 steady, 143
mesh-dependent on Sh , 253 tensor, 226
numerical flux, 249 unsteady, 75, 79
projection
operator commuting, 245
average, across sides, 144, 163, 250 in H(div), 245
jump, across sides, 144, 163, 250 in L2 norm, 45, 54, 265
adjoint, 121
curl, 53 quadrature formulae
divergence, 39 Gauss, 76
gradient, 39 Gauss-Lobatto, 76
symmetric part, 39
Helmholtz, 28 region, 34, 36
Helmholtz-Beltrami, 89 residual term, 111, 115
Laplace, 10
Laplace-Beltrami, 89 singular solution, 54
space
parallel computation, 11, 18, 237 W −1,p , 107
penalty parameter, 163 W −1,p , dual of W01,p , 111
polar coordinate system, 65 W 1,p , 107
preconditioner, 51 W01,p , 107
for nearly incompressible elasticity, 57 dual, 111
for Stokes problem, 51 duality bracket ⟨., .⟩, 112
306 Rheolef version 7.2

weighted (axisymmetric), 65
speedup, 18, 85
stabilization, 56
stream function, 54, 64, 85
axisymmetric, 65

tensor
Cauchy stress, 39, 68
field, 45
rate of deformation, 67
visualization as ellipsoid, 44

unknown and blocked components, 12


upstream boundary, 143
upwinding, 144

visualization
animation, 74
elevation view, 13, 110
stereoscopic anaglyph, 13, 43
vortex, 64, 67
vorticity, 53

You might also like