Sie sind auf Seite 1von 198

Diese Arbeit wurde vorgelegt am

Lehrstuhl für Mathematik (MathCCES)


The present work was submitted to the
Chair for Mathematics (MathCCES)

Simulation von Gasströmungen im Nichtgleichweicht


mit der FEniCS Computing Platform
Simulation of Non-Equilibrium Gas Flows Using the
FEniCS Computing Platform

Masterarbeit
Master Thesis
Computational Engineering Science

Aachen, September 2019

Vorgelegt von Lambert Theisen, B.Sc.


Presented by Hochbrück 4, 52070 Aachen
Matrikelnummer: 345612
lambert.theisen@rwth-aachen.de
Erstprüfer Prof. Dr. Manuel Torrilhon
First examiner Lehrstuhl für Mathematik (MathCCES)
RWTH Aachen University
Zweitprüfer Prof. Dr. Benjamin Stamm
Second examiner Lehrstuhl für Mathematik (MathCCES)
RWTH Aachen University
Eigenständigkeitserklärung / Affidavit

Eigenständigkeitserklärung
Hiermit versichere ich, dass ich die vorliegende Arbeit selbständig verfasst und keine
anderen als die angegebenen Quellen und Hilfsmittel benutzt habe. Die Stellen meiner
Arbeit, die dem Wortlaut oder dem Sinn nach anderen Werken entnommen sind, habe
ich in jedem Fall unter Angabe der Quelle als Entlehnung kenntlich gemacht. Dasselbe
gilt sinngemäß für Tabellen und Abbildungen. Diese Arbeit hat in dieser oder einer
ähnlichen Form noch nicht im Rahmen einer anderen Prüfung vorgelegen.

Affidavit / Statutory Declaration


I hereby declare that I wrote this thesis on my own and without the use of any other
than the cited sources and tools and all explanations that I copied directly or in their
sense are marked as such. The thesis has not been submitted to any examination
body in this, or similar, form.

Aachen, September 2019

Lambert Theisen
Zusammenfassung
Die vorliegende Masterarbeit stellt eine Simulationsumgebung für die Untersuchung
von Gasströmungen im Nichtgleichgewicht bereit, die auf der FEniCS Computing
Platform aufbaut [18]. Die R13 Gleichungen werden präsentiert, nachdem eine Diskus-
sion über die klassischen Modelle von Navier–Stokes und Fourier deren Schwächen
aufzeigt und somit die Modellierung mit erweiterten Modellgleichungen motiviert.
Das entsprechende System von Gleichungen wird vereinfacht, sodass ein stationäres
und linearisiertes System von Bilanzgleichungen für einen zweidimensionalen Anwen-
dungsfall formuliert werden kann.
Während einer Validierung der numerischen Methode mit exakten Lösungen, wird
insbesondere auf Aspekte bezüglich der Implementierung eingegangen, denn FEniCS
stellt erweiterte Funktionen für das Behandeln von Tensoren bereit. Dies erlaubt es,
eine fast eins-zu-eins ähnliche Implementierung der jeweiligen mathematischen For-
mulierungen im Quellcode. Ein dokumentierter und validierter Löser wurde implemen-
tiert und in [58] veröffentlicht. Dieser Löser erlaubt das Simulieren von willkürlichen
zweidimensionalen Geometrien mit einer Reihe von Randwerten.
Um die Benutzung von erweiterten Modellgleichungen für Gasströmungen erhöhter
Knudsen Zahl zu rechtfertigen, werden typische Beispiele mit auftreten Nichtgle-
ichgewichtseffekten betrachtet und gelöst. In diesen Anwendungsfällen, wird das
Knudsen Paradoxon und eine thermische Transpirationsströmung beobachtet, die mit
klassischen Lösern nicht vorhergesagt werden können.

Schlagwörter: R13 Gleichungen, FEniCS Projekt, Gasströmungen im Nichtgle-


ichgewicht, Finite Elemente Methode, CIP Stabilisierung
Abstract
This thesis provides a framework to simulate non-equilibrium gas flows using the
finite element method within the FEniCS computing platform [18]. The main model
equations, i.e. the R13 equations, are introduced after a motivational discussion about
the classical models, given by Navier–Stokes and Fourier. The resulting system of
equations is simplified to obtain a set of steady-state and linearized balance laws for
two-dimensional domains.
During a validation process of the numerical method with exact solutions, particular
focus is put on the intuitive implementation using the tensor capabilities of FEniCS.
This allows having an almost one-to-one correspondence between the mathematical
formulation and the implemented source code. A documented and validated solver is
developed and can be obtained from [58]. This solver allows simulating gas flows for
arbitrary shaped two-dimensional geometries using a variety of boundary conditions.
In order to justify the use of extended model equations for gas flows with moderate
Knudsen number, typical examples, with occurring rarefaction effects, are presented
and solved. In these application cases, the Knudsen paradox and a thermal transpi-
ration flow are observed.

Keywords: R13 equations, FEniCS project, Non-equilibrium gas flows, Finite ele-
ment method, CIP stabilization
Acknowledgments
This thesis was written between April and September 2019 at the MathCCES, RWTH
Aachen University.
First of all, I would like to thank my supervisor Prof. Manuel Torrilhon allowing me to
work on this project and for his motivational and valuable hints, fruitful discussions
as well as for the freedom to follow own ideas and to actively participate in the
determination of the direction of this thesis. The provided working environment in
the institute was extraordinary and undoubtedly contributed to the outcome of this
work. The topic was very well suited for the Computational Engineering Science
study program, involving many concepts, I learned during the last ten semesters,
while combining them all together in an interdisciplinary way.
I would further like to thank all my colleagues at the MathCCES group for including
me into the team and for all the on- and off-topic discussions during coffee and lunch
breaks. I really had a great time during the last months.
Special thanks are sent to my parents, my family, my girlfriend, and all others that
contributed to this work by supporting me and by accepting that one or two weekends
have been spent to finish this thesis in time.
Contents
List of Figures I

List of Tables III

List of Listings V

1. Introduction 1
1.1. Research Background and Context . . . . . . . . . . . . . . . . . . . 3
1.2. Thesis Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Thesis Outline and Scope . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4. The FEniCS Project . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2. Modeling Ideal Gas Flows Using the Classical Theory 9


2.1. Fundamental Balance Laws . . . . . . . . . . . . . . . . . . . . . . . 9
2.2. Classical Constitutive Theory . . . . . . . . . . . . . . . . . . . . . . 12
2.3. Discussion of Classical Models . . . . . . . . . . . . . . . . . . . . . . 15
2.3.1. Characterization of Gas Flows Using the Knudsen Number . . 15
2.3.2. Limitations of Classical Models: Rarefaction Effects . . . . . . 16

3. Kinetic Theory of Non-Equilibrium Gas Flows 19


3.1. Introduction to the Kinetic Theory of Gases . . . . . . . . . . . . . . 19
3.2. Derivation of Extended Macroscopic Models . . . . . . . . . . . . . . 21
3.2.1. Chapman–Enskog Expansion . . . . . . . . . . . . . . . . . . 21
3.2.2. Method of Moments . . . . . . . . . . . . . . . . . . . . . . . 22
3.3. Regularized 13-Moment Equations . . . . . . . . . . . . . . . . . . . . 23
3.3.1. R13 Equations . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.3.2. R13 Boundary Conditions . . . . . . . . . . . . . . . . . . . . 25
3.3.3. Steady-State Linearization . . . . . . . . . . . . . . . . . . . . 25

4. Introduction to the FEniCS Computing Platform 31


4.1. The Poisson Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.2. The Stokes Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

5. Decoupled Linearized Heat System 41


5.1. Weak Formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
xii Contents

5.2. Stabilization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.3. Convergence Study Based on Mesh Refinement . . . . . . . . . . . . . 46
5.3.1. Computational Domain and Input Parameters . . . . . . . . . 46
5.3.2. Error Discussion . . . . . . . . . . . . . . . . . . . . . . . . . 48

6. Decoupled Linearized Stress System 53


6.1. Weak Formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.2. Stabilization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.3. Convergence Study Based on Mesh Refinement . . . . . . . . . . . . . 59
6.3.1. Computational Domain and Input Parameters . . . . . . . . . 59
6.3.2. Error Discussion . . . . . . . . . . . . . . . . . . . . . . . . . 60

7. Full Linearized R13 System 65


7.1. Weak Formulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
7.2. Convergence Study Based on Mesh Refinement . . . . . . . . . . . . . 67
7.2.1. Test Case 1: Mass Source Induced Flow in an Extruded Ring . 68
7.2.2. Test Case 2: Cylinder Inside a Homogenous Flow . . . . . . . 70

8. Applications 75
8.1. Lid-Driven Cavity Benchmark . . . . . . . . . . . . . . . . . . . . . . 75
8.2. Knudsen Paradox in a Channel Flow . . . . . . . . . . . . . . . . . . 76
8.3. Thermal Transpiration Flow in a Knudsen Pump . . . . . . . . . . . 79

9. Conclusion 81
9.1. Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9.2. Technical Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.2.1. Solver Source Code . . . . . . . . . . . . . . . . . . . . . . . . 85
9.2.2. Calculations and Hardware . . . . . . . . . . . . . . . . . . . . 85
9.2.3. Software Versions . . . . . . . . . . . . . . . . . . . . . . . . . 85

A. Convergence Data and Exact Solutions 87


A.1. Exact Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
A.1.1. Decoupled Linearized Heat System . . . . . . . . . . . . . . . 88
A.1.2. Decoupled Linearized Stress System . . . . . . . . . . . . . . . 89
A.1.3. Full Linearized R13 System . . . . . . . . . . . . . . . . . . . 94
A.2. Convergence Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

B. Solver Related Data 109


B.1. Solver Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . 109
B.2. Solver Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

C. Solver Input Files for Application Cases 165


Contents xiii

D. FEniCS Example Programs 169


D.1. Poisson Problem Using First-Order Lagrange Elements . . . . . . . . 169
D.2. Stokes Problem Using Taylor–Hood Elements . . . . . . . . . . . . . 169

Bibliography 171
List of Figures
1.1. Graphical representation of chapter dependencies. . . . . . . . . . . 5
1.2. Google search interests for popular finite element software since 2010. 6

2.1. Comparison of different gas flow models and their Kn-validity. . . . . 17

4.1. P1 shape functions on the triangular reference element (2-simplex). . 35


a. N1 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
b. N2 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
c. N3 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.2. P3 shape functions on the triangular reference element (2-simplex). . 36
a. N1 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
b. N2 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
c. N3 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
d. N4 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
e. N5 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
f. N6 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
g. N7 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
h. N8 (x, y) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
i. N9 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
j. N10 (x, y). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

5.1. Extraction of a 2D model problem from an infinite cylinder. . . . . . 46


5.2. Computational domain and parameters for the decoupled heat system. 47
5.3. Series of unstructured triangular meshes for the convergence study. . 48
a. hmax = 0.99. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
b. hmax = 0.63. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
c. hmax = 0.33. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.4. DHS: Errors using P2 P1 elements without stabilization. . . . . . . . 49
5.5. DHS: Errors using P1 P1 with stabilization. . . . . . . . . . . . . . . 50
5.6. DHS: Errors using P2 P2 with stabilization. . . . . . . . . . . . . . . 51
5.7. Schematic results of the decoupled heat system test case. . . . . . . 51

6.1. Computational domain and parameters for the decoupled stress system. 60
6.2. DSS: Errors using P1 P1 P1 with stabilization (test case 1). . . . . . . 61
6.3. DSS: Errors using P3 P2 P1 without stabilization (test case 2). . . . . 62
II List of Figures

6.4. DSS: Errors using P1 P1 P1 with stabilization (test case 2). . . . . . . 63


6.5. DSS: Errors using P2 P2 P2 with stabilization (test case 2). . . . . . . 63
6.6. Schematic results for the two decoupled stress system test cases. . . 64
a. First test case. . . . . . . . . . . . . . . . . . . . . . . . . . . 64
b. Second test case. . . . . . . . . . . . . . . . . . . . . . . . . . 64

7.1. Computational domain for the linearized R13 system (test case 1). . 68
7.2. LinR13: Errors using stabilized P1 equal-order elements (test case 1). 69
7.3. LinR13: Errors using stabilized P2 equal-order elements (test case 1). 70
7.4. Schematic results of first test case for the linearized R13 equations. . 71
a. Shear stress σxy and velocity streamlines ui . . . . . . . . . . . 71
b. Temperature θ and heat flux streamlines si . . . . . . . . . . . 71
7.5. Computational domain for the linearized R13 system (test case 2). . 71
7.6. LinR13: Errors using stabilized P1 equal-order elements (test case 2). 72
7.7. Schematic results of second test case for the linearized R13 equations. 73
a. Shear stress σxy and velocity streamlines ui . . . . . . . . . . . 73
b. Temperature θ and heat flux streamlines si . . . . . . . . . . . 73

8.1. Computational domain for the lid-driven cavity application case. . . 76


8.2. Schematic results of the lid-driven cavity application case. . . . . . . 77
a. Shear stress σxy and velocity streamlines ui . . . . . . . . . . . 77
b. Temperature θ and heat flux streamlines si . . . . . . . . . . . 77
8.3. Computational domain for the channel flow application case. . . . . 77
8.4. Schematic results of the channel flow application case for Kn = 0.1. . 78
a. Shear stress σxy and velocity streamlines ui . . . . . . . . . . . 78
b. Temperature θ and heat flux streamlines si . . . . . . . . . . . 78
8.5. Knudsen paradox in a channel flow. . . . . . . . . . . . . . . . . . . 78
8.6. Computational domain for the Knudsen pump. . . . . . . . . . . . . 79
8.7. Schematic results of the Knudsen pump application case. . . . . . . 80
a. Shear stress σxy and velocity streamlines ui . . . . . . . . . . . 80
b. Temperature θ and heat flux streamlines si . . . . . . . . . . . 80
List of Tables
8.1. Temperature boundary conditions for the Knudsen pump. . . . . . . 80

9.1. Version numbers of used software. . . . . . . . . . . . . . . . . . . . 85

A.1. DLHS: L2 -errors for unstabilized P2 P1 elements. . . . . . . . . . . . 101


A.2. DLHS: l∞ -errors for unstabilized P2 P1 elements. . . . . . . . . . . . 101
A.3. DLHS: L2 -errors for stabilized P1 P1 elements. . . . . . . . . . . . . 102
A.4. DLHS: l∞ -errors for stabilized P1 P1 elements. . . . . . . . . . . . . 102
A.5. DLHS: L2 -errors for stabilized P2 P2 elements. . . . . . . . . . . . . 102
A.6. DLHS: l∞ -errors for stabilized P2 P2 elements. . . . . . . . . . . . . 103
A.7. DLSS: L2 -errors for stabilized P1 P1 P1 elements (test case 1). . . . . 103
A.8. DLSS: l∞ -errors for stabilized P1 P1 P1 elements (test case 1). . . . . 103
A.9. DLSS: L2 -errors for unstabilized P3 P2 P1 elements (test case 2). . . 104
A.10. DLSS: l∞ -errors for unstabilized P3 P2 P1 elements (test case 2). . . 104
A.11. DLSS: L2 -errors for stabilized P1 P1 P1 elements (test case 2). . . . . 104
A.12. DLSS: l∞ -errors for stabilized P1 P1 P1 elements (test case 2). . . . . 104
A.13. DLSS: L2 -errors for stabilized P2 P2 P2 elements (test case 2). . . . . 105
A.14. DLSS: l∞ -errors for stabilized P2 P2 P2 elements (test case 2). . . . . 105
A.15. LinR13: L2 -errors for stabilized P1 P1 P1 P1 P1 elements (test case 1). 105
A.16. LinR13: l∞ -errors for stabilized P1 P1 P1 P1 P1 elements (test case 1). 106
A.17. LinR13: L2 -errors for stabilized P2 P2 P2 P2 P2 elements (test case 1). 106
A.18. LinR13: l∞ -errors for stabilized P2 P2 P2 P2 P2 elements (test case 1). 106
A.19. LinR13: L2 -errors for stabilized P1 P1 P1 P1 P1 elements (test case 2). 106
A.20. LinR13: l∞ -errors for stabilized P1 P1 P1 P1 P1 elements (test case 2). 107
List of Listings

4.1. Mesh generation for a unit square domain Ω = [0, 1]2 [14]. . . . . . . . 34
4.2. Generation of function space using P1 elements for a given mesh [14]. 35
4.3. Definition of Dirchlet boundary conditions [14]. . . . . . . . . . . . . 35
4.4. Weak form specification for Poisson’s problem [14]. . . . . . . . . . . 36
4.5. Solving the weak formulation using given Dirichlet data [14]. . . . . . 37
4.6. Mixed function space definition for Taylor–Hood elements [15]. . . . . 39
4.7. Mixed weak formulation for the Stokes problem [15]. . . . . . . . . . 40

5.1. 3D STF-operator for 2D 2-tensors. . . . . . . . . . . . . . . . . . . . 44


5.2. Implementation of CIP stabilization for the decoupled heat system. . 45

6.1. Custom operator to lift a 2D 2-tensor to a 3D STF 2-tensor. . . . . . 57


6.2. Custom gradient operator to account for three dimensions. . . . . . . 57
6.3. Custom operator to obtain the STF part of a 3-tensor. . . . . . . . . 58
6.4. Custom operator to obtain the symmetric part of a 3-tensor. . . . . . 58

A.1. Usage of compiled C++ expressions in FEniCS. . . . . . . . . . . . . 87


A.2. Exact solution: heat/01_coeffs.cpp . . . . . . . . . . . . . . . . . . 88
A.3. Exact solution: stress/01_nosource_rot.cpp . . . . . . . . . . . . 89
A.4. Exact solution: stress/01_source_rot.cpp . . . . . . . . . . . . . . 91
A.5. Exact solution: r13/1_coeffs_sources_rot_noinflow.cpp . . . . . 94
A.6. Exact solution: r13/1_coeffs_nosources_norot_inflow.cpp . . . . 97

B.1. Solver file: src/fenicsR13.py . . . . . . . . . . . . . . . . . . . . . . 150


B.2. Solver file: src/input.py . . . . . . . . . . . . . . . . . . . . . . . . . 151
B.3. Solver file: src/meshes.py . . . . . . . . . . . . . . . . . . . . . . . . 154
B.4. Solver file: src/postprocessor.py . . . . . . . . . . . . . . . . . . . 154
B.5. Solver file: src/solver.py . . . . . . . . . . . . . . . . . . . . . . . . 156
B.6. Solver file: src/tensoroperations.py . . . . . . . . . . . . . . . . . 164

C.1. Lid-driven cavity input file: input.yml . . . . . . . . . . . . . . . . . 165


C.2. Channel flow input file: input.yml . . . . . . . . . . . . . . . . . . . 165
C.3. Knudsen pump input file: input.yml . . . . . . . . . . . . . . . . . . 166
VI List of Listings

D.1. FEniCS demo program to solve the Poisson problem using linear P1
elements (demo_poisson.py in [14, 18]). . . . . . . . . . . . . . . . . 169
D.2. FEniCS demo program to solve the Stokes problem using Taylor–Hood
elements P2 P1 (demo_stokes-taylor-hood.py in [15, 18]). . . . . . . 170
1. Introduction

Understanding the world in its true nature has always been a great desire in humanity
and can be considered as the motivation and the foundation for all sciences and all
research efforts. However, the exact underlying laws and physical mechanisms can
only be observed up to a limited precision or can only be derived by theoretical
considerations following generalization aspects. With these insights, mathematical
models are formulated based on the cause and effect principle. The resulting models
are then used to describe and predict the behavior of physical systems, e.g., the flow
of gases. In general, one has to keep in mind that models always contain assumptions
and admissibility restrictions apply to them.

An especially important branch of modeling is the prediction of dynamic gas flows,


in short gas dynamics. Everyone has an intuitive understanding of how a fluid flow
behaves, but a precise mathematical description can be challenging. This particular
field of application has an ancient history, and foundations were already set centuries
ago by prominent representatives, i.a. Newton, Fourier, Navier, and Stokes [37]. In
general, the conservation laws for mass, momentum, and energy can not be solved
directly. Constitutive relations, such as the thermal and caloric equation of state,
describe the gas properties while the heat flux and the fluid stress have to be modeled.
These models, classically formulated based on observations, are then used to form a
closed set of equations which can be solved.

The classical models, given by Navier–Stokes and Fourier, are nowadays heavily used
in academia and industry to predict the behavior of gas flows. A major driving
factor was the advances in efficient and scalable numerical methods, combined with
the increasing computational resources available nowadays. It allowed for relatively
accurate simulation results by approximating the underlying equations and solving
them with a reasonable computational effort. Today, computational fluid dynamics
(CFD) is undoubtedly part of every design process for all kinds of products and
processes to increase efficiency, to understand certain behaviors and eventually to
decrease costs in an industrial simulation context. In practice, the classical and
historical models are used because they work out very well in most of the considered
situations. Besides challenges in the modeling of turbulent structures [53], the overall
research field seems very well understood.
2 1. Introduction

However, with the great success in simulation contexts, one might easily forget about
the fact, that the classical Navier–Stokes–Fourier models are not the ground truth
and quickly lose their validity when leaving standard conditions. Also, the equations
of state for the considered gas, such as the ideal gas law, for example, are of course
not universal and only hold for specific conditions. The flow around a space vehi-
cle, reentering the earth’s atmosphere at high altitudes, for example, fundamentally
behaves different, expressed in a nutshell as:

“Just imagine you are at very high altitude where the air density is very
low. You might see a lot of weird things.”
— Zhenning Cai, December 2016 [11]

The following question may arise: What is so fundamentally different in the flow
situation leading to a failure of the classical models? Well, the underlying continuum
assumption – stating that a big collection of gas particles act as a continuum due to
many collisions – is violated because of the very low gas density. In these rarefied sit-
uations, the mean free path, that gas particles can travel without a collision, becomes
significant in relation to the considered length scale. In the context of dimension-
less similitude analysis, the Knudsen number is the primary dimensionless quantity
describing this crucial relation between the mean path and the process length scale.
For increasing Knudsen numbers above zero, a gas leaves its thermodynamic equi-
librium state and enters the non-equilibrium. In a non-equilibrium, a whole range
of rarefaction effects can be observed, that seem “weird” at first glance. The reason
for this might be the fact that these effects contradict our intuitive understanding of
gas flows at standard conditions. Similar effects can also be observed in microtech-
nology applications. For example, microelectromechanical systems (MEMS) provide
an electrical, mechanical, hydraulic, or pneumatic function on a chip with shallow
dimensions. In the near future, these “micro-machines” can become very important
and could become as pervasive as electronics nowadays [75]. It is clear that more
sophisticated models are needed in order to accurately predict and simulate flows in a
rarefied or micro-scale setting by capturing all the relevant non-equilibrium effects.

In the context of the kinetic gas theory, there exist microscopic descriptions account-
ing for individual gas particle dynamics, such as the Boltzmann equations. These
models can be used for rarefied gas situations. However, in practice, their simulation
is often rather expensive. This motivated the derivation of extended macroscopic
models, that can be used, just as the classical models, as a compact set of partial dif-
ferential evolution equations for the main gas quantities. The regularized 13-moment
(R13) equations can capture the relevant rarefaction effects while still being relatively
compact. They act as an extension to the classical NSF description of gases. However,
with extended evolution equation for the heat flux and fluid stress, their numerical
simulation remains challenging due to the increased modeling complexity.
1.1. Research Background and Context 3

1.1. Research Background and Context


The numerical treatment of the regularized 13-moment equations is active research.
These equations only have an extended structure compared to already existing models,
and the goal is to apply already known numerical methods and study how these meth-
ods work out. It has to be noted that there is a still ongoing discussion about boundary
conditions and different formulations of the underlying equations. Simultaneously,
recent advances in the numerical treatment were achieved, i.a.:
• In [43], a finite difference scheme was applied to obtain steady-state approxima-
tions of the R13 equations. A comparison to more costly numerical methods,
such as the Direct Simulation Monte Carlo (DSMC) method, was performed.
• An implicit discontinuous Galerkin approach was used in [65] for a hierarchical
simulation context. Numerical solutions of the steady-state linearized R13
equations were compared to lower order models such as the NSF system.
• Finite element methods for the steady-state linearized R13 system were pre-
sented in [68, 69] as a result of subsequent advances regarding instability issues
and modern stabilizations techniques in [70, 71, 72]. Earlier FEM approaches
using the FEniCS simulation framework were already discussed in [40] back in
2010.

1.2. Thesis Objectives


Following the motivation of Chapter 1 and considering the present state of the relevant
research in Section 1.1, the main objectives of this thesis can be summarized by the
following consecutive parts:
1. Discussion of relevant models: The need for extended gas flow models is shown
and will be discussed in relation to the classical constitutive theory. The deriva-
tion of the R13 equations is revisited, in order to have a solid foundation to
perform subsequent work. Further, the needed simplifications are applied, and
the central model equation systems are formulated.
2. Discussion of state-of-the-art finite element formulations with a focus on im-
plementation details: The model equations are solved using a finite element
discretization and are systematically validated with the exact solution.
3. Implementation of an extended gas dynamics solver using the FEniCS frame-
work: The numerical methods are implemented to obtain a solid and intuitive
simulation framework to perform gas flow simulation in rarefied or microscopic
applications. This software should be easily extensible and should provide a
4 1. Introduction

solid framework allowing for further research and more complex flow situations.
Special effort is put into intuitive implementation concepts, offered by the FEn-
iCS computing platform. This includes, for example, the usage of tensorial
objects to have a one-to-one correspondence between mathematical formulation
and final implementation. This can be seen as a test for current capabilities of
the FEniCS computing framework. Furthermore, the solver shall be publicly
available and sufficiently documented and tested.
4. Discussion of application cases: In particular, applications with occurring rar-
efaction effects are presented. This shall justify the usage of extended gas flow
models and differentiate them from classical simulation approaches.

1.3. Thesis Outline and Scope

In this Chapter 1, the present work was embedded in the physical context and the
current research context. A short presentation of the FEniCS Project, as the leading
software used throughout this work, is given in the following Section 1.4.
This thesis contains two main parts. The first part, starting with Chapter 2, revisits
the classical gas flow models and shows their deficiencies in rarefied gas applications.
This leads to the derivation of extended gas flow models, presented in Chapter 3, and
sets the mathematical modeling context of this work.
The second part starts with a brief introduction into the programming paradigms of
the FEniCS project by presenting an introduction example in Chapter 4. In order
to systematically validate the programmed solver, the model equations are split into
a decoupled heat system, validated and discussed in Chapter 5. After an analogous
validation of the second part, i.e. a decoupled stress system in Chapter 6, the full
linear R13 equation system is solved and validated in Chapter 7.
In Chapter 8, some example applications, mostly inspired by typical examples from
the literature, are presented. These application cases are particularly relevant in
the rarefied gas or micro-scale flow setting. The last Chapter 9 aims to provide an
extensive overview of future work opportunities to motivate the further development
of finite element strategies for extended gas models.
A graphical overview of the chapter dependencies is given in Fig. 1.1. However, these
dependencies are not very strong, and essential concepts or ideas are revisited if it in-
creases the clarity of the presentation. It is only natural that this present work aims at
graduating students with a background in computational modeling and mathematics.
However, as part of the Computational Engineering Science (CES) program with its
interdisciplinary concept, the present work makes use of concepts from the disciplines
1.4. The FEniCS Project 5

Chapter 1: Introduction

Chapter 4: Solving
Chapter 2: Classical Theory
PDEs using FEniCS

Chapter 3: Kinetic Gas Theory Chapter 5: Decoupled Heat System

Chapter 6: Decoupled Stress System

Chapter 7: Full Linear System

Chapter 8: Applications

Chapter 9: Conclusion

Figure 1.1. Graphical representation of chapter dependencies throughout the thesis. Except for
the derivation of the relevant physical models in Chapters 2 and 3, the thesis structure follows a
linear strategy.

Mathematics, Physics, Engineering, Computer Science and Thermodynamics. An


attempt was made to provide a good overview of the subject by providing further
references and literature.

1.4. The FEniCS Project

As the thesis title suggests, the FEniCS computing platform is used for all simulations
throughout this work. It was already mentioned, that the implementation of new
mathematical models can require large parts of auxiliary code to be written, in order
to solve the actual physical model problem. Especially in the context of Galerkin
or finite element methods, the solution procedure, for example, needs routines for
connectivity assemblage, evaluation of functions and their derivatives as well as the
final linear solve. The implementation of different finite element spaces as in [3, 5] can
also be seen as a subfield in the broad context of solving a model equation numerically.
All the above-listed prerequisites can increase the complexity and the effort when
building simulation software and are error-prone. This work, therefore, builds up
upon the existing simulation framework FEniCS [3, 35] to have a reliable existing
framework in order to set the focus more on the modeling and formulation aspects.

FEniCS is a free and open-source licensed collection of interoperable components


for automated scientific computing. The main focus is solving partial differential
equations by the finite element method [3]. Released under the LGPLv3-license [21],
it provides an “easy, intuitive, efficient, and flexible” [32] interface for computational
mathematical modeling. Compared to other finite element software, the FEniCS
6 1. Introduction

project is rather trendy nowadays, as seen in Fig. 1.2. Possible reasons for this trend
can be the generality or extensibility of FEniCS. While other software often has a
limited range of physical problems that can be analyzed, FEniCS can solve arbitrary
model formulations as long as the physical problem can be formulated mathematically.
Also, there exists a decent amount of open-access documentation, i.a. [32, 35], which
can contribute to the popularity of software in general.

FEniCS Project Code_Aster deal.II FreeFem++ Elmer


Rel. Google Search Interest

1.0
0.9
0.8
0.7
0.6
0.5
0.4
0.3
0.2
0.1
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019

Python C++ C

Figure 1.2. Relative Google search interests for popular finite element software since 2010. While
the search interests in general are very volatile for all the listed software, the FEniCS Project shows
a continuous popularity trend in contrast to the other software packages. A reference plot shows the
search interest for the programming language Python in comparison to C/C++ for the same time
range. The data was extracted from [22, 23]. The original data further shows an inhomogeneity
in terms of the geographical location of the web searches. For example, while Code_Aster and
FreeFem++ are dominant in France, Elmer is often searched in Finland and deal.II is more popular
in Germany and the USA. A possible reason for such effects can be the location of the initial and
ongoing development or language-induced accessibility barriers in the documentation of a software.

In terms of usability, FEniCS offers both a modern high-level and easy to use Python
interface as well as a high-performance C++ backend. Therefore, as presented
in Fig. 1.2, the FEniCS computing platform may also has benefitted from increasing
popularity of the Python programming language in the time range since 2010. In
order to write the mathematical models in a very high-level manner, the whole FEn-
iCS project consists of several building blocks [32], i.e flexible and reusable software
components. The effort to write a large portion of code is shifted away from the user
to the developer with automated code generation. This approach aims to solve the
“disconnect between mathematics and code” (e.g. a rather simple Poisson equation
−∆u = f vs. 100–10000 lines of code) [34]. In the optimal case, a user should only
1.4. The FEniCS Project 7

write the mathematical statements of the model problem, and all the auxiliary work
is executed automatically. To get an overview of how this concept of automated code
generation can be achieved, we will shortly introduce the main components of the
FEniCS framework.
The main component of FEniCS is DOLFIN [28, 36], being an acronym for Dynamic
Object oriented Library for FINite element computation. This library implements
the high-performance C++ backend, consisting of the relevant data structures such
as meshes, functions spaces and functions. The main algorithms in the context of the
finite element method, i.a. element assembly, mesh handling or connection to linear
algebra solvers, are also part of DOLFIN. From a user perspective, DOLFIN is the
main connection layer between the high-level Python interface and the core compo-
nents, because it handles the communication between all components and extends
them with external software. The most important internal low-level components of
FEniCS are:
• UFL [2]: The Unified Form Language is a domain-specific language to formulate
problems, in the context of finite element methods, in their weak variational form.
With UFL, a model problem can be expressed in a natural and mathematical
form. It also serves as the input for the form compiler.
• FFC [31]: The FEniCS Form Compiler automatically generates DOLFIN code
from a given variational form. The goal of a form compiler is to use a well-tested
compiler, performing automated code generation tasks, in order to improve the
correctness of the resulting code.
• FIAT [30]: The FInite element Automatic Tabulator enables the automatic
computation of basis functions for nearly arbitrary finite elements.
An extensive description of all components can be found in [35]. Note, that some
design changes in the overall framework were made since the initial publication of [35].
For example, the meshing capabilities were bundled into a standalone component
named mshr [3]. Therefore, additional and more recent resources such as [3, 32] should
be consulted together with the collection of all source code repositories in [18].
It should further be mentioned that other simulation frameworks utilize a similar
concept as FEniCS. The Firedrake project [44], e.g., also uses the Unified Form
Language allowing a potentially steep learning curve for existing FEniCS users [26].
FEniCS-HPC [27] focuses on high-performance aspects such as partitioning and load
balancing. However, FEniCS-HPC is based on an earlier version of the DOLFIN
library, and DOLFIN itself has decent scalability properties as experiments showed
in [45].
Recently, the developers of FEniCS presented a Roadmap 2019–2020, announcing a
significant redevelopment of core libraries under the names DOLFIN-X and FFC-X.
8 1. Introduction

Stable releases are expected in early 2020 [46]. The work of this thesis, therefore, still
uses the most recent version 2019.1.0.r3.
2. Modeling Ideal Gas Flows Using
the Classical Theory

To understand and derive models for rarefied gas flows, we will first introduce the
classical theory to describe the behavior of general gas flows. This chapter starts by
introducing the fundamental balance laws in Section 2.1 followed by the presentation
of classical closures in Section 2.2 that are not able to capture non-equilibrium effects
as discussed in Section 2.3.

Throughout this thesis, a closed and time-independent gas domain Ω ⊂ Rd in d ∈


{2, 3} spatial dimensions is considered. The main quantities are the evolutions over
a time span t ∈ [0, T] of field quantities, such as the gas density ρ : Ω × [0, T] → R+ ,
the gas velocity u : Ω × [0, T] → Rd and the gas temperature T : Ω × [0, T] → R+ in
the domain Ω. Besides these three fundamental quantities, we will further encounter
the pressure p : Ω × [0, T] → R+ , the heat flux s : Ω × [0, T] → Rd and the deviatoric
stress tensor σ : Ω × [0, T] → Rd×d
STF . Note, that the traditional symbol q for the heat
flux is replaced by s, in order to have an intuitive set of test functions in the weak
formulations later on. In fact, q will be used as the test function for the pressure p,
following the usual finite element terminology.

The overall goal is to determine these evolutions for all points x ∈ Ω from given
initial conditions, e.g. ρ0 (x, 0), u0 (x, 0), T (x, 0), together with a set of given boundary
conditions. The latter describe the outer environment and boundary behavior of
the fields on the domain boundary ∂Ω. In general, the change of all quantities is
described by model equations – in our particular gas flow setting by partial differential
equations (PDEs) – as introduced in the next Section 2.1.

2.1. Fundamental Balance Laws

The flow of gas follows three fundamental laws of physics: the point-wise conserva-
tion of mass in Eq. (2.1a), the conservation of momentum in Eq. (2.1b) (Newton’s
second law of motion) and the conservation of total energy in Eq. (2.1c) (first law of
10 2. Modeling Ideal Gas Flows Using the Classical Theory

thermodynamics), expressed in component form as

∂ρ ∂(ρuj )
+ = 0, (2.1a)
∂t ∂xj
∂(ρui ) ∂(ρui uj + Πij )
+ = bi , (2.1b)
∂t ∂xj
∂Etot ∂(Etot uj + Πij ui + sj )
+ = r + u j bj . (2.1c)
∂t ∂xj

In Eq. (2.1) – compared to the quantities already introduced in Chapter 2 – Π is


the Cauchy stress (or pressure) tensor, b is the vector of body forces per volume
(e.g. gravity) and r is the local energy source (e.g. through radiative effects). Note,
that Einstein’s summation convention was applied, compare e.g. with [47]. It will be
used throughout the whole thesis to simplify notation. Except otherwise stated, all
fields depend on space x and time t although not explicitly mentioned.

Examining the conservations laws in Eq. (2.1), some observations can be done in
accordance with [61]: In Eq. (2.1a), nuclear reactions are neglected, so that the total
mass is entirely conserved. The momentum balance Eq. (2.1b) allows us to compute
the change in momentum, i.e. mass multiplied by velocity, depending on the acting
forces. The total energy in Eq. (2.1c), however, changes due to the supplied heat and
the power of all acting forces. The latter is further composed of the internal energy
density and the kinetic energy density as

1
Etot = ρ + ρu2 , (2.2)
2

with  denoting the specific internal energy and u = kuk2 = ui ui denoting the
velocity magnitude.

It is common to use the internal energy balance instead of the total energy bal-
ance. The derivation takes the kinetic energy balance into account, that is a direct
consequence of the momentum conservation multiplied by ui as

∂(ρui ) ∂(ρui uj ) ∂Πij


ui + ui + ui = bi ui . (2.3)
∂t ∂xj ∂xj

Evaluating the temporal derivative and using the product rule leads to
! ! !
∂ui ∂ρ ∂(ρuj ) ∂ui ∂(Πij ui ) ∂ui
ρui + u2i + u2i + ρui uj + − Πij = bi ui ,
∂t ∂t ∂xj ∂xj ∂xj ∂xj
(2.4)
2.1. Fundamental Balance Laws 11

where we further apply a split to use the mass conservation to express the temporal
density derivative as u2i ∂ρ
∂t
= 12 u2i ∂ρ
∂t
− 21 u2i ∂(ρu
∂xj
j)
. The remaining time derivatives are
recognized as the temporal change of kinetic energy with

∂ui 1 2 ∂ρ ∂ 1 2
 
ρui + ui = ρu , (2.5)
∂t 2 ∂t ∂t 2 i
and after reordering and a collection of spatial derivatives, the expression
!
∂ 1 2 1 2 ∂(ρuj ) ∂ui ∂(Πij ui ) ∂ui
 
ρui + ui + ρui uj + = bi ui + Πij , (2.6)
∂t 2 2 ∂xj ∂xj ∂xj ∂xj

can finally be written as the balance law for kinetic energy:

∂ 1 2 ∂ 1 2 ∂ui
   
ρui + ρui + Πij ui = bi ui + Πij . (2.7)
∂t 2 ∂xj 2 ∂xj

Note that the kinetic energy itself is not conserved in contrast to the total energy.
After subtracting the kinetic energy balance Eq. (2.7) from the conservation of total
energy Eq. (2.1c), the balance law for the internal energy can be formulated as

∂(ρ) ∂(ρuj + sj ) ∂ui


+ = r − Πij . (2.8)
∂t ∂xj ∂xj

Using the mass conservation again, after expanding the internal energy terms, leads
to an alternative form as
∂ ∂ ∂sj ∂ui
ρ + ρuj + + Πij = r. (2.9)
∂t ∂xj ∂xj ∂xj

Again note, that the internal energy itself is also not conserved because of the pro-
∂ui
duction term Πij ∂x j
describing energy conversion by, e.g., mechanical dissipation [61].
The symmetric Cauchy stress tensor Πij is usually [64] decomposed into the isotropic
pressure p := 13 Πkk and the deviatoric, trace-free stress tensor σij := Πij − 13 δij Πkk
as
Πij = pδij + σij . (2.10)
The decomposition of Eq. (2.10) is inserted into the conservation laws for mass and
momentum of Eqs. (2.1a) and (2.1b) and into the internal energy balance of Eq. (2.9).
Using the index shift properties of the Kronecker delta [47] tensor

1 i=j
δij = , (2.11)
0 else
12 2. Modeling Ideal Gas Flows Using the Classical Theory

we arrive, in accordance with [63] where source terms are omitted, at the system of
evolution equations in Lagrangian form

Dρ ∂vk
+ρ = 0, (2.12a)
Dt ∂xk
Dui ∂p ∂σij
ρ + + = bi , (2.12b)
Dt ∂xi ∂xj
D ∂ui ∂ui ∂si
ρ +p + σij + = r. (2.12c)
Dt ∂xi ∂xj ∂xi

D
Here, the material derivative is used as Dt := ∂t + uj ∂x∂ j and the mass conserva-
tion of Eq. (2.1a) was used to rewrite the velocity terms in the momentum bal-
ance Eq. (2.1b) as

∂(ρui ) ∂(ρui uj ) ∂ui ∂ρ ∂(ρuj ) ∂ui


+ =ρ + ui + ui + ρuj (2.13)
∂t ∂xj ∂t ∂t ∂xj ∂xj
∂ui ∂ui
=ρ + ρuj (2.14)
∂t ∂xj
Dui
=ρ . (2.15)
Dt
The balance laws in Eq. (2.12) provide, in a three-dimensional setting, five equations
– one from the mass balance and the internal energy balance respectively as well as
three from the vector-valued momentum balance. Besides the fields of interest ρ(x, t),
u(x, t) and T (x, t), the system of equations contains additional unknown quantities,
i.e. the pressure p(x, t), the internal energy density (x, t), the heat flux s(x, t) and
the deviatoric stress tensor σ(x, t). The problem is in that form underdetermined.
Such problems are called closure problems because it remains to find closures, that are
mathematical expressions, relating the additional unknowns to the fields of interest.
While the balance laws are typically valid in general, closures depend on the material
models [61, 64]. Prescribing a closure often takes physical intuition into account as
we will see in the following Section 2.2.

2.2. Classical Constitutive Theory

As already discussed in Section 2.1, material properties and assumptions have to be


taken into account in order to prescribe closures. This work is therefore restricted to
gas flows of monoatomic ideal gases. In contrast to solid and liquid materials, where
atoms are constantly exchanging energy and momentum, an exchange between gas
atoms can only happen during collisions. As discussed in [50], a gas is called ideal, if
2.2. Classical Constitutive Theory 13

its particles are most of the time in a free flight without collisions. This fact can be
expressed as
τc
 1, (2.16)
τ
with τc denoting the mean time between collisions and τ denoting the mean time in
free flight. It is obvious from Eq. (2.16), that gases at low density behave ideal because
of a high average particle distance and therefore a considerable mean free flight time
τ . Further arguments in [50] consider the mean kinetic energy to argue, that gases
also at high temperatures can behave ideally. Struchtrup showed in [50], that a
gas without special molecular structure behaves ideally under standard conditions,
i.e. temperature T0 = 298 K and pressure p0 = 1 bar.
Assuming an ideal gas, the pressure p can be related to the mass density ρ and the
absolute temperature T through the ideal gas law

R
p = ρRs T = ρ T, (2.17)
M
where R ≈ 8.314 molJ K is the universal gas constant, M is the molar mass of the
R
gas [37] and Rs = M is the specific gas constant. The ideal gas law is also called
thermal equation of state. The caloric equation of state, further, relates the specific
internal energy to the temperature [37] with

R
=z (T − TR ) + R . (2.18)
M
In Eq. (2.18), the coefficient z depends on the atomic structure of the considered gas,
with the relation 
3
 2 monoatomic


z = 52 diatomic . (2.19)



3 else
The restriction to monoatomic gases and an absolute scale (TR = 0, R = 0) allows
restating the relation of Eq. (2.18) to

 = cv T, (2.20)

where the heat capacity at constant volume cv = 32 Rs = const is used. A gas with
ideal thermal behavior of Eq. (2.17) and constant cv is called calorically perfect or
perfect gas in some contexts [41, 76]. With the two equations of state, the fields of
pressure, temperature and density can be related.
It remains to prescribe closure relations for the deviatoric stress σ and the heat flux s,
so-called constitutive equations [37] or laws [61]. The classical theory of gas dynamics
14 2. Modeling Ideal Gas Flows Using the Classical Theory

is founded upon the law of Fourier


∂T
si = −κ , (2.21)
∂xi
that relates the heat flux linearly to the temperature gradient, and the Navier–Stokes
law
∂uhi
σij = −2µ , (2.22)
∂xji
relating the deviatoric stress tensor to the deviatoric velocity gradient. In Fourier’s
law in Eq. (2.21), the thermal conductivity is denoted by κ ≥ 0 and the Navier–Stokes
law in Eq. (2.22) contains the viscosity µ ≥ 0, also called shear or dynamic viscosity.
In general, the material coefficients κ, µ can depend on temperature or pressure and
have to be determined by measurements [37]. Note that for ideal monoatomic gases,
there is no bulk or dilatational viscosity contribution to the stress tensor [7], so that
the decomposition of Eq. (2.10) is justified.
The classical closures, i.e. Eqs. (2.21) and (2.22), were postulated based on measure-
ments or observations [37, 64]. The theory of (linear) irreversible thermodynamics,
formulated later, takes an entropy production density into account and is based on the
Gibbs equation of reversible thermodynamics [37]. Using the second law of thermo-
dynamics to obtain a non-negative entropy production for real irreversible processes,
there is only one way to relate thermodynamic fluxes (qi , σij ) to thermodynamic forces
∂T ∂uhi
( ∂x ,
i ∂xji
) assuming linear dependency [37]. This is due to the tensorial structure of
the fluxes and the forces [61]. However, this “heuristic” [38] approach leads to para-
doxes in specific applications. In fact, infinite propagation speeds can occur using
the classical closures, compare with the heat conduction paradox in [38]. The desire
to have finite propagation speeds and a hyperbolic equation structure motivated
the development of further theories, such as the extended thermodynamics [38, 39].
In general, the goal is to derive new, extended closures that resolve the appearing
paradoxes. The methods using the kinetic theory of gases, presented in Chapter 3,
also fall under this category [39, 60]. As we will see later on in Chapter 8, extended
material laws are also able to predict physical phenomena, that the classical theory
can not predict.
In this chapter, the traditional models of Navier–Stokes and Fourier (NSF) were
presented. The relations can be inserted into the system of balance laws Eq. (2.12)
to eliminate the stress and heat flux. Using the equations of state for a monoatomic
ideal gas allows to eventually solve the resulting system with appropriate boundary
and initial values for the field evolutions of interest. We will refer to the resulting
equations as Navier–Stokes–Fourier (NSF) system for a perfect gas.
It became clear that closure relations are an essential part in the description of gases.
The choice of appropriate constitutive relation depends on the region of their validity
2.3. Discussion of Classical Models 15

and therefore, strongly depend on the considered application, as we will discuss in


the next Section 2.3.

2.3. Discussion of Classical Models

The classical NSF theory is routinely [50] used in engineering applications to analyze
fluid flows under standard conditions using the finite element method [16, 19, 55],
although they are not yet fully understood from a mathematical perspective [8]. From
a physical point of view, it is almost surprising, that a massive amount of individual
acting gas particles can be described by a rather low amount of partial differential
equations – because after all, a gas at standard conditions (1 bar, 25 ◦C) contains
about 2.43 × 1016 gas atoms per cubic millimeter [50].
The reason for this behavior is the massive amount of resulting collisions. These
collisions equally distribute deviations and disturbances of individual particles – they
behave as a continuum [50, 61, 64] – in which a macroscopic description, using the
classical NSF system is sufficient. The continuum assumption erases the molecular dis-
continuities by averaging all microscopic quantities over a small sampling volume [29].
For this to be possible, the gas has to be in thermodynamic equilibrium, where the
intermolecular collision frequency ν is much higher than the process frequency ω [29,
50], expressed as
ω
 1. (2.23)
ν
An equivalent description can be derived using the mean free flight time τ = ν1 and
the mean free path λ = cavg τ with the mean particle speed cavg [50, 64]. The mean
free path, therefore, is the mean distance traveled between two particle collisions.
The requirement of Eq. (2.23) is therefore fulfilled, if the mean free path is sufficiently
small compared to the relevant characteristic length scale L, resulting in

λ ω
Kn := =  1. (2.24)
L ν
The Knudsen number Kn in Eq. (2.24) is the dimensionless parameter describing
these considerations.

2.3.1. Characterization of Gas Flows Using the Knudsen Number

As it became already clear in Section 2.3, a classification of different flow scenarios


is needed, in order to evaluate the validity of the considered models. Very close to
the thermodynamic equilibrium, i.e. Kn ≈ 0 where the gas is very dense, the Euler
16 2. Modeling Ideal Gas Flows Using the Classical Theory

equations serve as a sufficiently good description of the gas behavior. The Euler
equations consist of the fundamental balance laws in the reversible case with si = 0
and σij = 0 [50, 61]. The Navier–Stokes–Fourier system with non-trivial heat flux
and stress closures, presented in Section 2.3, extends the range of sufficiently small
modeling errors to higher Kn-numbers of Kn . 0.01. The range, where classical
models can be used, is called the hydrodynamic regime. Outside this hydrodynamic
range, a gas flow is called rarefied and extended macroscopic models or a microscopic
description of the gas is needed. A rarefied gas is more diluted, having fewer particles
in a given control volume, but it is still possible, to use continuum models with
macroscopic descriptions. In fact, it is possible to incorporate higher-order slip
boundary condition terms into the NSF system to extend the range even further [29].
However, as we will discuss in the following Section 2.3.2, they cannot describe other
gas rarefaction effects occurring in the transition regime of 0.05 . Kn [43, 62]. A
graphical summary of gas models and a characterization, based on the Knudsen
number, is presented in Fig. 2.1.

The Boltzmann equation, introduced and discussed in Chapter 3, describes gas flows at
all Knudsen numbers within a microscopic framework. However, this central equation
of the kinetic theory of gases [50], is numerically expensive to solve [43]. It is possible,
to approximate the Boltzmann equations to extract a set of macroscopic equations.
That is desirable in order to keep the compact description of gases macroscopically.
In the context of this work, we will use the R13 equations, proposed by Struchtrup
and Torrilhon in [52] and reviewed in e.g. [51, 62], as one prominent example for
extended macroscopic gas flow models suitable in the transition regime. This set of
equations is capable of predicting various non-equilibrium effects as discussed in the
following Section 2.3.2.

2.3.2. Limitations of Classical Models: Rarefaction Effects

Following the Kn-number-based discussion of Section 2.3, the classical hydrodynamic


models are only valid close to the thermodynamic equilibrium, i.e. Kn  1. An
intuitive argument for this failure is given in [50], where the temperature and velocity
are expanded as a Taylor series. Using a dimensionless length scale x̃ = Lx , the
Knudsen number dependence, of e.g. the temperature, becomes clear as

dT 1 d2 T
T (x + λ) → T (x̃ + Kn) = T (x̃) + Kn + 2
Kn2 + O(Kn3 ). (2.25)
dx̃ 2 dx̃
Therefore, for larger Kn-numbers, higher-order terms become more significant and
have to be taken into account, i.e. specifying only first-order terms through the
∂T ∂uhi
classical closures (si = −κ ∂x i
and σij = −2µ ∂xji ) is not sufficient.
2.3. Discussion of Classical Models 17

Euler

NSF
Model Simplicity

NSF with special BC

R13

Higher-order extended macroscopic models

Boltzmann equation

λ
0 0.001 0.01 0.1 1 10 100 L
= Kn
Hydrodynamic Slip Flow Transition Regime Free Molecular
Regime Regime NSF equations fail, Flow (Free Flight)
NSF valid suffi- Higher-order modeling with extended Particle collisions
ciently close to BC needed macroscopic models are not dominant.
thermod. equilibrium. for NSF. or BM equation.

Figure 2.1. Comparison of different gas flow models and their validity among the Knudsen number
spectrum. The Euler equations are only valid very close to the thermodynamic equilibrium at
Kn ≈ 0. The classical Navier–Stokes–Fourier equations have a broader validity and can be shipped
with special higher-order boundary conditions, above the hydrodynamic flow regime, into the slip flow
regime. For accurate modeling of gas flows in the transition regime, where rarefaction effects play a
role, extended macroscopic models have to be considered. These models extend the classical NSF
closures and typical examples are the Burnett, Super-Burnett, Grad13, R13 or the R26 equations.
The Boltzmann equation, however, is valid across the complete Knudsen spectrum but can be
computationally inefficient at lower Kn-numbers. Note that the schematic ranges of the flow regime
naming are not exact and the definitions may vary, depending on the context. In [62], e.g., an
additional kinetic regime is mentioned at about 2 . Kn . 15, where a detailed description through
a distribution function is necessary. The plot extends ideas from [48] with information from [29, 50,
51].

Recalling the definition of the Knudsen number Kn = Lλ as the ratio between the
mean free path and the characteristic length scale, a non-equilibrium gas flow is
present for processes involving:

• Miniaturization (L ↓): The development to decrease to size of a technical


system in general. Examples for such micro-systems are hard disk drive heads,
inkjet printheads, micro heat-exchangers or flows in microchannels [43]. With
recent advances in industry and research, these processes are essential in many
applications.
18 2. Modeling Ideal Gas Flows Using the Classical Theory

• Gas rarefaction (λ ↑): A gas flow situation, in which the mean free path is
relatively high compared to the relevant process length scale. Examples are
re-entry flights at high altitudes where low pressure and low density are present.
The relatively low amount of particles lead to a significant ratio between the
mean free path and the length dimension of the aircraft. Other examples are
vacuum devices with very low gas densities.
In a general non-equilibrium gas flow at about Kn & 0.05, many interesting rarefaction
effects or micro-scale phenomena were observed in experiments or from the analysis of
the Boltzmann equation [51]. A comprehensive list of effects is given in [51] and [62],
i.e. a parallel heat flux (in flow direction) in a channel flow, contradicting Fourier’s law
as there is no temperature difference; a non-constant pressure behavior in Couette and
Poiseuille channel flows although no flow across the channel is present; a minimum
of the mass flow rate (Knudsen minimum) in a in force-driven Poiseuille flow, also
known as Knudsen paradox; a non-convex temperature profile in such microchannels
while NSF predicts a strictly convex profile for the same setup; temperature-induced
flow situations in channels; Knudsen boundary layers, i.e. temperature jump and
velocity slip at walls.
With the extensive list of non-equilibrium effects, the overall motivation to derive
extended macroscopic models for gas flows becomes clear, i.e. to predict as many of
these rarefaction effects as possible while keeping the set of equations compact using
a macroscopic model. These flow situations shall, therefore, be predicted quantitative
in terms of gas phenomena and qualitative in terms of a low modeling error by keeping
a high model accuracy. Another general aspect of rating the success of an extended
model is the accurate prediction of shock structures at larger Ma-numbers [59].
It turned out, that the Boltzmann equation, predicting all of the above-listed effects
accurately, can be approximated to extract extended model equations, as the following
Chapter 3 proceeds. These extended models act as an extension or generalization to
classical NSF closures, that are not able to predict the relevant flow phenomena at
higher Knudsen numbers.
3. Kinetic Theory of Non-Equilibrium
Gas Flows

The complete opposite approach compared to the classical macroscopic models, in-
troduced in Chapter 2, is to model every particle α separately, i.e on a microscopic
level, using Newton’s second law of classical mechanics. Every particle α has the
same mass m and a position xα = (xα , yα , zα ). A force Fα would then influence the
dynamic behavior of a particle α, induced by the current state of all other particles
{xβ }β=1,..,B , through [61]
mẍα = Fα ({xβ }β=1,..,B ). (3.1)

However, considering the large number of particles, this approach would lead to a
huge set of coupled equations that cannot be solved efficiently. Also, the individual
positions of all particles α are not relevant in practical applications [61]. In fact, it
practically does not make a difference, if the individual particle α = 1 or the individual
particle α = 2 is at the position x = (1, 2, 3) – because in the end, they have the same
properties anyway. However, it is important to know, how many particles are at a
given position x = (1, 2, 3) and how their current state, in terms of their velocities c, is
described. These two properties are encoded in the distribution function f (or phase
density), that serves as the primary quantity of interest in the kinetic gas theory.

In the following, a short introduction to the basic concepts of kinetic theory is given,
being preliminary to the introduction of the main model equations of this thesis,
namely the set of R13 equations.

3.1. Introduction to the Kinetic Theory of Gases

The distribution function is defined over the phase space V := Ω × R3 , that is the
product of the spatial domain x ∈ Ω ⊂ R3 and the velocity c ∈ R3 . The distribution
function f : R+ × V, (t, x, c) 7→ f (t, x, c) describes the probability of encountering
particles with the velocity c at the location x for the time t. In fact, the number of
20 3. Kinetic Theory of Non-Equilibrium Gas Flows

particles in a volume ∆V0 := ∆x0 ∆c0 at a time t can be obtained by an integration


over the volume as Z Z
N∆V0 (t) = f (t, x, c) dc dx. (3.2)
∆x0 ∆c0

The particle density atR a location x and time t can be obtained by an integration over
all possible velocities R3 f (t, x, c) dc, such that mass density ρ(x, t), as the particle
mass m times the particle density, can be identified through
Z
ρ(x, t) = m f (t, x, c) dc. (3.3)
R3

Knowledge of f does not only lead to expressions for the density. In fact, all other
macroscopic quantities (see Chapter 2) can be computed from the distribution func-
tion. The velocity u(x, t) and the internal energy density (x, t) as remaining quanti-
ties of the fundamental balance laws and the first two non-equilibrium quantities, heat
flux q(x, t) and the deviatoric stress tensor σ(x, t), can be identified [62] through
m Z
ui (x, t) = ci f (t, x, c) dc, (3.4)
ρ(x, t) R3
m Z 1
(x, t) = (ci − vi )2 f (t, x, c) dc, (3.5)
ρ(x, t) R3 2
Z
1
si (x, t) = m Ci Cj2 f (t, x, c) dc, (3.6)
R 3 2
Z
σij (x, t) = m Chi Cji f (t, x, c) dc, (3.7)
R3

where the random velocity (also peculiar or central velocity) Ci := ci − vi describes


the difference of the particle velocity ci to the flow velocity ui . The notation Ahiji
is further used to denote the symmetric and trace-free (deviatoric) part of a tensor
as Ahiji = A(ij) − 13 Akk δij = 12 (Aij + Aji ) − 13 Akk δij [50, 64]. To compute all fields of
interest, it therefore only remains to calculate the distribution function f , which can
be done using the Boltzmann equation.
The Boltzmann equation describes the change of f due to free flight (transport) and
interaction of particles (collisions). The time evolution of the distribution function
with an external force bi is given by [61, 62]

∂f ∂f bi ∂f
+ ci + = S(f, f ), (3.8)
∂t ∂xi m ∂ci
where S(f, f ) denotes the collision integral. In general, due to its higher dimension-
ality over the phase space Ω × R3 , the direct computation of f is computationally
expensive. This is the reason to approximate the Boltzmann equation in order to
derive a set of extended equations with model complexity above the classical NSF
system.
3.2. Derivation of Extended Macroscopic Models 21

3.2. Derivation of Extended Macroscopic Models


There exist multiple approaches to derive extended macroscopic model equation
based on the Boltzmann equation. Prominent ones are the Chapman–Enskog (CE)
expansion and the method of moments [61, 64]. Moment closures can be regularized
using a pseudo-equilibrium approach or by using the more general framework of
the order of magnitude method [62, 64]. Other approaches to derive closures are
entropy-based [48].

3.2.1. Chapman–Enskog Expansion

The Chapman–Enskog series expansion assumes that the distribution function f can
be expanded [64] with respect to the Knudsen number as

Knj f (j) (t, x, c)
X
f (t, x, c, Kn) = (3.9)
j=0

= fM + Kn f (1) + Kn2 f (2) + O(Kn3 ), (3.10)

in which the Maxwell distribution [64]


ρ
(ci − ui )2
!
m
fM (ρ, ui , T, c) = q 3 exp − k , (3.11)
k
2π m T 2m T

describes the gas state in thermodynamic equilibrium. This equilibrium distribu-


tion has a Gaussian shape and k denotes the Boltzmann constant. Inserting the
expansion of Eq. (3.10) into a dimensionless Boltzmann equation without external
forces and a simplified collision operator, i.e. Bhatnagar–Gross–Krook model (BGK),
allows performing a scale separation in terms of Knudsen numbers. After collect-
ing the lowest order terms, it follows that f (t, x, c) = fM + O(Kn) which is the
natural result in thermodynamic equilibrium Kn → 0. Collecting further Kn-terms
lead to a first-order
 distribution function after solving a transport equation of the
˜
form f = − ∂t̃ f + c̃i ∂x̃i f˜(0) [61] with the already known zeroth-order function,
(1) ˜(0)

i.e. the Maxwell distribution. The resulting first-order distribution function is called
Chapman–Enskog distribution fCE . This hierarchical concept can be extended to
arbitrary Kn-order.
Together with the set of fundamental balance laws Eq. (2.12), these distribution
functions can then be projected with Eqs. (3.6) and (3.7) to produce [64] the
• Euler equations using the zeroth-order distribution function fM , i.e. si = 0 and
σij = 0.
22 3. Kinetic Theory of Non-Equilibrium Gas Flows

• Navier–Stokes equations using the first-order distribution function fCE , in ac-


cordance to the argument of Eq. (2.25).

• Burnett equations using the second-order distribution function.

• Super-Burnett equations using the third-order distribution function [50].

Here, a critical requirement to a framework of deriving extended model equations


and closures became clear. In fact, the framework should be able to predict and
collapse at lower orders to the already known Euler and NSF equations – to guarantee
consistency. While the Euler and NSF equations are very popular and widely used,
higher-order equations sets, derived by the CE method, are less popular. A reason
for this can be occurring instabilities in transient processes, as reported in [50]. We
will, therefore, move on to an alternative approach to derive extended gas models in
the next Section 3.2.2.

3.2.2. Method of Moments

The method of moments assumes that the state of a gas can be described by an
extended set of moments as [50]
Z
uA = ΨA (ck )f (t, x, c) dc, (3.12)
R3

with a vector of polynomials in c and C. The classical Grad’s 13 moment hierar-


[13]
chy (G13) consists [50] of the polynomials ΨA = m{1, ci , 12 C 2 , Chi Cji , 12 C 2 Ci } and
[13]
produces uA = {ρ, ρui , ρ, σij , si } by the means of Eqs. (3.3) to (3.6).

The multiplication of the Boltzmann equation with ΨA and integration over the
velocity space produces a set of moment evolution equations. In these equations,
higher-order moments are introduced and need an explicit closure formula. The
derivation of these closures uses the Grad 13-moment distribution, that is derived
[13]
by an ansatz in the polynomials ΨA . The resulting function fG13 is then used
to evaluate the expressions for the higher-order moments [64] in order to express
these terms as functions from the variables uA – the classical closure strategy. The
complete procedure can also be seen as a model reduction by replacing the linear
higher-dimensional equation by a set of low-dimensional non-linear equations. The
collision terms on the right-hand side of Eq. (3.8) can also be evaluated using the
BGK model [64] and assuming Maxwell molecules with special power potential [50].

While the resulting G13 system of equations can predict some rarefaction effects, it
produces artifacts in shock wave profiles [64].
3.3. Regularized 13-Moment Equations 23

3.3. Regularized 13-Moment Equations


The R13 equations can be seen as the G13 set of evolution equations with a correction
(or regularization) applied, that takes care of the highest-order terms. Based on a
pseudo-equilibrium approach, Struchtrup and Torrilhon added additional terms to
the evolution equations of the heat flux and the stress tensor in [52], following an
idea shortly mentioned, but somehow rejected, in [24] by Grad as
“Thus for some purposes it would seem to be preferable to have a suitable
parabolic system. This can be achieved by an interpolation process which
mimics the interpolation of the Navier-Stokes equations between the five
moment Euler equations and the thirteen moment equations. [. . .] The
[resulting] formulas are not overwhelming in actual problems of interest.”
— Harold Grad, 1958 [24]
The same regularized equations can be derived using an order of magnitude approach,
that is more general [64]. For Maxwell molecules, this approach is also consistent, such
that it produces the Euler equations at zeroth-order, the NSF system at first-order,
a modified version of the G13 system in second-order and a modified version of the
R13 equations in third-order [50].

3.3.1. R13 Equations

The set of equations, as revisited in [62], consists of the three fundamental balance
laws from Eq. (2.12) as
Dρ ∂vk
+ρ = 0, (3.13)
Dt ∂xk
Dui ∂p ∂σij
ρ + + = bi , (3.14)
Dt ∂xi ∂xj
D ∂ui ∂ui ∂si
ρ +p + σij + = r, (3.15)
Dt ∂xi ∂xj ∂xi
with an evolution equation for the stress tensor
∂σij ∂σij uk 4 ∂shi ∂uhi ∂uji ∂mijk
+ + + 2p + 2σkhi + = −νσij , (3.16)
∂t ∂xk 5 ∂xji ∂xji ∂xk ∂xk
and an evolution equation for the heat flux
∂si ∂si uk ∂ui 5 1 ∂σjk 1 ∂p 5 ∂θ
 
+ + sk − pδij + σij − σij + p
∂t ∂xk ∂xk 2 ρ ∂xk ρ ∂xj 2 ∂xi
6 ∂uj 1 ∂ R̄ik 2
 
+ δ(ij sk) + mijk + = −ν si , (3.17)
5 ∂xk 2 ∂xk 3
24 3. Kinetic Theory of Non-Equilibrium Gas Flows

where the abbreviation


1
R̄ij = 7θσij + Rij + Rδij , (3.18)
3
is used. In Eqs. (3.16) and (3.17), the classical laws of NSF are included and are
extended by further terms to form a balance law, such that the R13 equations can
be seen as a generalized constitutive theory [62]. The terms on the right-hand side
include the collision frequency ν and the remaining highest-order moments mijk , Rij
and R. They are closed [62] with
σ 
hij
p ∂ p 20
mijk = −2 θ + shi σjki , (3.19)
ν ∂xki 15p
s 
hi
24 p ∂ p 192 20
Rij = − θ + shi qji + σkhi σjik , (3.20)
5 ν ∂xji 75p 7ρ
 
s
p ∂ pk 56 5
R = −12 θ + sk sk + σij σij . (3.21)
ν ∂xk 5p ρ

Neglecting the highest-order terms mijk , Rij , R would reduce the equations to the
G13 system [62]. Considering an ideal and monoatomic gas as in Chapter 2, the
pressure is given by p = ρRs T = ρRs cv = 23 ρ and using the temperature in terms of
k
energy units as θ = m T with Boltzmann constant k, we further have p = ρθ [62].

The evolution Eq. (3.17) for the heat flux can be rewritten only to have first-order
derivative terms. We insert the abbreviation of Eq. (3.18) and expand

∂si uk ∂uk ∂si


= si + uk , (3.22)
∂xk ∂xk ∂xk
1 ∂(7θσij ) 7 ∂σij 7 ∂θ
= θ + σij , (3.23)
2 ∂xk 2 ∂xk 2 ∂xk
1 ∂(ρθ) ∂θ θ ∂ρ
−σij = −σij − σij , (3.24)
ρ ∂xj ∂xj ρ ∂xj
5 1 ∂σjk 5 ∂σik 1 ∂σjk
 
− pδij + σij =− θ − σij , (3.25)
2 ρ ∂xk 2 ∂xk ρ ∂xk

and further expand the symmetric term, using the symmetry of δij , as
!
6 ∂uj 61 ∂uj ∂uj ∂uj
δ(ij sk) = δij sk + δki sj + δjk si (3.26)
5 ∂xk 53 ∂xk ∂xk ∂xk
!
2 ∂ui ∂uk ∂uk
= sk + sk + si . (3.27)
5 ∂xk ∂xi ∂xk
3.3. Regularized 13-Moment Equations 25

We can then restate the heat flux balance law as


∂si ∂si 7 ∂uk 5 ∂θ 5 ∂θ ∂σik θ ∂ρ
+ uk + si + p + σik +θ − σik
∂t ∂xk 5 ∂xk 2 ∂xi 2 ∂xk ∂xk ρ ∂xk
σij ∂σjk 7 ∂ui 2 ∂uk 1 ∂Rik 1 ∂R ∂uj 2
− + sk + sk + + + mijk = −ν si , (3.28)
ρ ∂xk 5 ∂xk 5 ∂xi 2 ∂xk 6 ∂xi ∂xk 3

which is equivalent to the forms given in [43, 69].

3.3.2. R13 Boundary Conditions

With the increased number of variables in the R13 system, an extensive set of boundary
conditions is required. The R13 equations are derived from the kinetic gas theory
using the distribution function f (t, x, c) as discussed in Section 3.3.1. Boundary
conditions are, therefore, also derived in this framework by prescribing the incoming
half of the distribution function (i.e. c · n > 0 where n is the normal pointing into the
domain) at a wall [66].
Following [66], the most common boundary condition for f is given by the Maxwell
accommodation model. It assumes that a certain fraction χ of particles hitting the
wall is accommodated back into the gas with a given distribution function, that is
assumed to be a Maxwellian fM as in Eq. (3.11). The remaining parameters in fM
are chosen according to the wall conditions θw and either ρw or pw . The remaining
porting of particles (1 − χ) is specularly reflected [64].
In short, the distribution function in an infinitesimal neighborhood of the considered
wall is given by

(∗)
χf + (1 − χ)fgas
M (c) (c) n · (c − v w ) > 0,
f˜(c) = (3.29)
fgas (c) n · (c − v w ) < 0,

(∗)
where fgas accounts for transformed velocities as the result of specular reflection.
Using the method of moments as in Section 3.2.2, the resulting projection can be eval-
uated, and suitable boundary conditions can be derived [66]. The relevant linearized
boundary conditions are given in Section 3.3.3.

3.3.3. Steady-State Linearization

Throughout this thesis, we will consider the steady-state and linearized R13 equations
using pressure p and temperature θ instead of density ρ. These pressure-primitive
variables are a common choice for engineering applications [55]. Reformulation of
26 3. Kinetic Theory of Non-Equilibrium Gas Flows

Eqs. (3.13) to (3.15) is done by expanding the material derivatives, using p = ρθ,
using  = 23 θ and by neglecting the temporal derivatives as

∂ρ ∂ρ ∂uk ∂( pθ ) p ∂uk
0= + uk +ρ = uk + (3.30)
∂t ∂xk ∂xk ∂xk θ ∂xk
!
p ∂θ 1 ∂p p ∂uk
= uk − 2 + + (3.31)
θ ∂xk θ ∂xk θ ∂xk
!
uk ∂p p ∂θ p ∂uk
= − + , (3.32)
θ ∂xk θ ∂xk θ ∂xk
∂ui ∂ui ∂p ∂σij
bi = ρ + ρuk + + (3.33)
∂t ∂xk ∂xi ∂xj
p ∂ui ∂p ∂σij
= uk + + , (3.34)
θ ∂xk ∂xi ∂xj
∂ ∂( 32 θ) ∂ui ∂ui ∂si
r = ρ + ρuk +p + σij + (3.35)
∂t ∂xk ∂xi ∂xj ∂xi
3p ∂θ ∂ui ∂ui ∂si
= uk +p + σij + . (3.36)
2θ ∂xk ∂xi ∂xj ∂xi
While the evolution Eq. (3.16) for the stress does not have any density or internal
energy variables and therefore only has a vanishing temporal derivative, the evolution
Eq. (3.28) for the heat flux is restated using

θ ∂ρ θ ∂( pθ )
−σik = −σik (3.37)
ρ ∂xk ρ ∂xk
∂θ θ ∂p
= σik − σik . (3.38)
∂xk p ∂xk
Both equations then read
∂σij uk 4 ∂shi ∂uhi ∂uji ∂mijk
+ + 2p + 2σkhi + = −νσij , (3.39)
∂xk 5 ∂xji ∂xji ∂xk ∂xk

∂si 7 ∂uk 5 ∂θ 5 ∂θ ∂σik ∂θ θ ∂p


uk + si + p + σik +θ + σik − σik
∂xk 5 ∂xk 2 ∂xi 2 ∂xk ∂xk ∂xk p ∂xk
θ ∂σjk 7 ∂ui 2 ∂uk 1 ∂Rik 1 R ∂uj 2
− σij + sk + sk + + + mijk = −ν si , (3.40)
p ∂xk 5 ∂xk 5 ∂xi 2 ∂xk 6 xi ∂xk 3
The steady-state closures are directly given by Eqs. (3.19) to (3.21) because they do
not contain time derivatives.
The usual [64, 69] way to linearize the R13 equations is to consider an equilibrium
(0) (0) (0)
background state U 0 = (ρ(0) , ui , θ(0) , σij , si ) = (ρ0 , 0, θ0 , 0, 0) and a perturbation
3.3. Regularized 13-Moment Equations 27

from this state as U = U 0 + δ Ũ . This ansatz is then inserted into the equation
and only the δ-linear terms are kept as deviation quantities. For quantitates in the
denominator, an additional Taylor expansion has to be utilized [69].

However, an equivalent but more general [1] approach is to consider each of the
Eqs. (3.32), (3.34), (3.36), (3.39) and (3.40) as a function Y = f (U ) where U =
(U1 , . . . , Un ) consists of all variables including derivatives. By means of a multi-variant
Taylor expansion around an operating point U 0 , that also includes expressions for all
variables and derivates, the initial function, which is formulated in terms of absolute
quantities U , can then be stated in terms of deviation quantities û := U − U 0 =
(û1 , . . . , ûn ) as
" # " #
∂f (U ) ∂f (U )
ŷ = û1 + · · · + ûn + O(û2i ) ∀1 ≤ i ≤ n. (3.41)


∂U1
U =U 0
∂Un
U =U 0

For example, the steady-state mass balance Eq. (3.32) can be seen as
!
uk ∂p p ∂θ p ∂uk
Y := 0 = − + =: f (U ), (3.42)
θ ∂xk θ ∂xk θ ∂xk

∂p
with U = (p, θ, uk , ∂x , ∂θ , ∂uk ) and the operating point U 0 = (p0 , θ0 , 0, 0, 0, 0). The
k ∂xk ∂xk
resulting equation is written in terms of deviation quantities as
" # " # " #
∂f (U ) ∂f (U ) ∂f (U )
0̂ = 0 ≈
p̂ +
θ̂ +
uˆk (3.43)
∂p
U0
∂θ
U0
∂uk
U0
ˆ ! ˆ !
   
∂f (U ) ∂p ∂f (U ) ∂θ
+   ∂p  

+   ∂θ  
∂ ∂xk U 0 ∂xk ∂ ∂xk U 0 ∂xk
ˆ !
 
∂f (U ) ∂u k
+   ∂u   ,

∂ ∂xkk
U0
∂xk

where the partial derivatives with respect to U can be evaluated as


" # " #
uk p 1 ∂uk uk ∂p 2uk p ∂θ
0= − 2 + p̂ + − 2 + 3 θ̂ (3.44)


θ θ ∂xk
U0
θ ∂xk θ ∂xk
U0

1
"
∂p p ∂θ
!#

uk
 ˆ !  u p 
∂p ˆ !
∂θ
k
+ −
uˆk +
+ − 2
θ ∂xk θ ∂xk
U0
θ
U0
∂xk θ U ∂xk
0
 
p ˆ !
∂u k
+ ,
θ U0
∂xk
28 3. Kinetic Theory of Non-Equilibrium Gas Flows

and due to the operating point U 0 , the only remaining term is the divergence of u
due to
0 = [0] p̂ + [0] θ̂ (3.45)
ˆ
∂p
! ˆ
∂θ
! 
p0
 ˆ
∂u
!
k
+ [0] uˆk + [0] + [0] + ,
∂xk ∂xk θ0 ∂xk
which finally then yields the linearized, steady-state mass balance. This more general
approach would also allow linearizing around an operating state with a non-vanishing
partial derivative, such as an evolved velocity profile across a channel. The remaining
balance laws (Eqs. (3.34) and (3.36)) can be linearized in the same way. Dropping
the deviation indicator ˆ·, we obtained the steady-state and linearized fundamental
balance laws
∂uk
= 0, (3.46)
∂xk
∂p ∂σij
+ = bi , (3.47)
∂xi ∂xj
∂ui ∂si
p0 + = r. (3.48)
∂xi ∂xi
The linearized, steady-state extended evolution equations of Eqs. (3.39) and (3.40)
then read
4 ∂shi ∂uhi ∂mijk
+ 2p0 + = −νσij , (3.49)
5 ∂xji ∂xji ∂xk
5 ∂θ ∂σik 1 ∂Rik 1 ∂R 2
p0 + θ0 + + = −ν si , (3.50)
2 ∂xi ∂xk 2 ∂xk 6 ∂xi 3
while the linearized closures from Eqs. (3.19) to (3.21) can also be processed in the
same manner as
θ0 ∂σhij
mijk = −2 , (3.51)
ν ∂xki
24 θ0 ∂shi
Rij = − , (3.52)
5 ν ∂xji
θ0 ∂sk
R = −12 . (3.53)
ν ∂xk
The body force bi and the heat source r are neglected for simplicity. Further sim-
plifications can be done by either setting all reference values θ0 , p0 to unity and end
up with the only remaining parameter τ = ν1 . Another more general approach is to
reformulate the equations in a dimensionless form by relating every quantity to its
reference. We apply the latter approach and define

xi θ p τ ui
x̂i := , θ̂ := , p̂ := , τ̂ := , ûi := √ , (3.54)
L θ0 p0 τ0 θ0
3.3. Regularized 13-Moment Equations 29

where ˆ· now indicates dimensionless quantities. Inserting this approach into an


arbitrary equation allows identifying the remaining reference values by means of a
coefficient comparison [56] to

σ si mijk Rij
σ̂ij = , ŝi = √ , m̂ijk = √ , R̂ij = , (3.55)
p0 p0 θ0 p0 θ0 p0 θ0

All quantitates are then replaced by their dimensionless counterpart multiplied with
the reference value, e.g. xi → Lx̂, and are inserted into the linearized equations.
Furthermore, the Knudsen number can be identified as


τ0 θ0
Kn = . (3.56)
L

The resulting system of interest, i.e. the linearized, steady-state and dimensionless
R13 equations, can then be written as

∂uk
= 0, (3.57)
∂xk
∂p ∂σij
+ = 0, (3.58)
∂xi ∂xj
∂ui ∂si
+ = 0. (3.59)
∂xi ∂xi
4 ∂shi ∂uhi ∂mijk 1
+2 + =− σij , (3.60)
5 ∂xji ∂xji ∂xk Kn
5 ∂θ ∂σik 1 ∂Rik 1 2
+ + =− si . (3.61)
2 ∂xi ∂xk 2 ∂xk Kn 3
∂σhij
mijk = −2 Kn , (3.62)
∂xki
24 ∂shi
Rij = − Kn , (3.63)
5 ∂xji

where the marker ˆ·, denoting dimensionless and linearized deviation quantities, has
again been dropped. Further note that the closure R̂ is zero due to the setup of no
heat source and no mass source, that allows to insert the mass balance into the heat
balance and therefore identifying the heat flux s as a divergence-free quantity. The
30 3. Kinetic Theory of Non-Equilibrium Gas Flows

above equations can also be written in tensor notation as

∇ · u = 0, (3.64)
∇p + ∇ · σ = 0, (3.65)
∇ · u + ∇ · s = 0, (3.66)
4 1
(∇s)STF + 2(∇u)STF + ∇ · m = − σ, (3.67)
5 Kn
5 1 1 2
∇θ + ∇ · σ + ∇ · R = − s, (3.68)
2 2 Kn 3
m = −2 Kn (∇σ)STF , (3.69)
24
R = − Kn (∇s)STF . (3.70)
5
Here, the deviatoric part of the symmetric tensor is denoted by (·)STF . To allow
formulating boundary value problems, a set of boundary conditions is required. The
most recent version was given in [42] while we use the notation of [65, 68] as

un = 0, (3.71)
1
 
σnt = χ̃ (ut − uw
t ) + st + mnnt , (3.72)
5
11
 
Rnt = χ̃ −(ut − uw t ) + s t − m nnt , (3.73)
5
1 2
 
w
sn = χ̃ 2(θ − θ ) + σnn + Rnn , (3.74)
2 5
2 7 2
 
w
mnnn = χ̃ − (θ − θ ) + σnn − Rnn , (3.75)
5 5 25
1 1
   
mnnn + mntt = χ̃ σnn + σtt , (3.76)
2 2
where a two-dimensional local boundary-aligned coordinate system in terms of normal
and tangential components (n, t) is used in order
q to generate the projections. The
χ
modified accommodation factor is given by χ̃ = πθ20 2−χ [68]. The boundary condi-
tions are equal to the Onsager boundary conditions of [42] and have been adjusted
compared to previous publications, as described in [65]. The most general version
of the boundary conditions includes Onsager coefficients that are not focused in the
context of this work.
4. Introduction to the FEniCS
Computing Platform

Before proceeding to treat the linearized R13 equations, a short introduction re-
garding the usage of the FEniCS project is given. This introduction also serves
as an introduction to the finite element method. Extensive example material can
be found in [32, 35]. However, due to the dynamic development of FEniCS, these
examples might not be executable without modifications. We, therefore, stick to
the current documentation [13] and its examples [18]. Furthermore, throughout this
thesis, we use the Python interface due to its modern high-level language style and
its interoperability with other Python software.

We will first consider Poisson’s equation in Section 4.1, where the general concept
of reformulating a problem from a strong into a variational or weak form, suitable
for finite element discretization, is presented. Two types of boundary conditions,
namely Dirichlet and Neumann type, are discussed. The second example problem
is Stokes’ problem, considered in Section 4.2. For this two-equation problem, the
concept of mixed finite element methods is introduced with particular focus on the
corresponding implementation in FEniCS. Mixed finite element methods require a
stable pair of elements, such as the well-known Taylor–Hood elements, that we utilize
in that example.

4.1. The Poisson Problem

One of the most important and most popular partial differential equations is Poisson’s
equation. The linear, second-order and elliptic PDE is a generalization of Laplace’s
equation arising in a variety of physical contexts [17], such as heat, chemical concentra-
tion or electrostatic field modeling. An example problem involving Poisson’s equation
acts as an introductory example to the FEniCS platform and is given in [14].

Considering a closed domain Ω and an abstract quantity u : Ω → R, a Poisson bound-


ary value problem in two dimensions with homogenous Dirichlet and inhomogeneous
Neumann boundary conditions can be formulated as:
32 4. Introduction to the FEniCS Computing Platform

Problem 4.1 (Poisson). For a given domain Ω ⊂ R2 with a boundary ∂Ω = ΓD ∪ ΓN


as a union of Dirichlet and Neumann boundary sets with outer normal vectors n, find
the function u : Ω → R, u ∈ C 2 (Ω) ∩ C 0 (Ω̄), such that

−∆u = f in Ω, (4.1)
u=0 on ΓD , (4.2)
∇u · n = g on ΓN , (4.3)

for a given right-hand side (source function) f : Ω → R, f ∈ C 0 (Ω) and given


boundary data g : ΓN → R.

In the strong formulation 4.1, the Laplace operator ∆ can be written as the divergence
2
of the gradient leading to ∆u = ∇·(∇u) = 2i=1 ∂∂xu2 which is used in Gauß’s divergence
P
i
theorem in the following.
As outlined in [16], the general idea of a spatial discretization using the finite element
method consists of weakening the requirements for u by allowing larger function
spaces than C m and reformulating the problem in a weak, variational sense. The first
step is, therefore, the multiplication of Eq. (4.1) with a test (or weighting) function
v and a subsequent integration over the domain Ω, that leads to
Z Z
− v∆u dΩ = vf dΩ. (4.4)
Ω Ω

Note, that the regularity requirement of u is already weakened and the second deriva-
tive does not have to be continuous anymore. In fact, it is sufficient that u is twice
differentiable, while the second derivative is square-integrable. In other words, the
function has to fulfill u ∈ H 2 (Ω) while the Sobolev space H 2 (Ω) consists of all func-
tions whose partial derivatives up to the second order are square-integrable and thus
part of the Lebesgue space L2 (Ω), following the general definition of

∂ |α| u
( )
H k (Ω) = u ∈ L2 (Ω) : ∈ L2 (Ω) ∀|α| ≤ k , (4.5)
∂xα1 1 ∂xα2 2

for the two-dimensional domain Ω using the multi-index α = (α1 , α2 ) ∈ N2 and


|α| = α1 + α2 . Applying integration by parts results in
Z Z Z
− v∆u dΩ = ∇v · ∇u dΩ − v (∇u · n) dΓ, (4.6)
Ω Ω Γ

that is using the product rule for the divergence operator ∇ · (v∇u) = v (∇ · ∇u) +
∇v · ∇u and Gauß’s divergence theorem [73]. The regularity requirements have been
further modified, such that u, v ∈ H 1 (Ω).
4.1. The Poisson Problem 33

The natural (Neumann) boundary conditions can now be inserted into the weak form
while the essential (Dirichlet) boundary conditions are embedded into the function
spaces, such that
n o
u ∈ S := u ∈ H 1 (Ω) : u = uD on ΓD , (4.7)
n o
v ∈ V := v ∈ H 1 (Ω) : v = 0 on ΓD . (4.8)
For our case with uD = 0, both spaces S and V are equal. The continuous weak form
can then be formulated as:

Weak Formulation 4.2. Find u ∈ S, such that


Z Z Z
∇v · ∇u dΩ = vf dΩ + vg dΓN , (4.9)
Ω Ω ΓN

for all v ∈ V.

The bilinear form a(u, v) and the linear functional l(v)


Z
a(u, v) = ∇v · ∇u dΩ, (4.10)
ZΩ Z
l(v) = vf dΩ + vg dΓN , (4.11)
Ω ΓN

can be identified. Using the FEniCS framework, the continuous weak form is sufficient
to solve the problem together with the boundary conditions using a prescribed finite
element. For the sake of completeness, we will continue to present the general concepts,
as they are used in the solver backend, before focusing on the FEniCS implementation
of Poisson’s problem.
In general, the Lax–Milgram lemma can be used to prove existence and uniqueness
of a solution to the weak formulation 4.2, if the bilinear form a(u, v) is continuous
and coercive and the linear mapping l(v) is continuous. In the case of inhomogeneous
Dirichlet conditions, further treatment of u is needed to have S = V in order to apply
the lemma, see for example [16]. To finally solve the problem, finite-dimensional
subspaces S h , V h of S, V are considered. For a given triangulation T h (Ω) of Ω into
the subdomains Ωe , the discrete spaces consist of piecewise polynomials of degree m
and read:
n o
S h = u ∈ H 1 (Ω) : u|Ωe ∈ Pm (Ωe ) ∀e and u = uD on ΓD , (4.12)
n o
V h = v ∈ H 1 (Ω) : v|Ωe ∈ Pm (Ωe ) ∀e and v = 0 on ΓD , (4.13)

The Galerkin weak form (i.e. V h = S h up to boundary conditions [6]) is therefore


analogous and reads:
34 4. Introduction to the FEniCS Computing Platform

Weak Formulation 4.3. Find uh ∈ S h ⊂ S, such that


Z Z Z
h h h
∇v · ∇u dΩ = v f dΩ + v h g dΓN , (4.14)
Ω Ω ΓN

for all v h ∈ V h ⊂ V.

With the help of Cea’s lemma, one can prove, that the error of such an approximation
is bounded, details can be found for example in [16]. To produce a linear system
of equations, the trial function uh is discretized and therefore expressed as a linear
combination of shape functions – for homogenous Dirichlet boundary conditions and
following the notation of [16] as

uh (x) =
X
NA (x)uA , (4.15)
A∈η

where A is a node in the finite element mesh and NA (x) its corresponding shape func-
tion. It is not possible and necessary to test the weak formulation against all possible
v h ∈ V h . In fact, using the same basis V h := span {NA (x)} for the test functions
produces a system of equations for the coefficients uA and the final solution can be
reconstructed by means of Eq. (4.15) as a linear combination of shape functions.
Solving this Poisson problem using FEniCS can be achieved conveniently. After
loading the dolfin module, the computational domain is restricted to a unit square
Ω = [0, 1]2 , and a triangular mesh is created using:
1 from dolfin import *
2 # Create mesh and define function space
3 mesh = UnitSquareMesh(32, 32)
2
Listing 4.1 Mesh generation for a unit square domain Ω = [0, 1] [14].

The next step is to prescribe the discrete function space V h . Here, and throughout
this thesis, we focus on H 1 -conforming Lagrangian finite elements Pm (in FEniCS
terminology CGm [35]), where m denotes the maximal polynomial degree. These
elements are the most essential choice and should be implemented in all finite element
software. The usual strategy is to transform the integrals occurring in the weak form
to a simple reference element, where the basis functions Na (x) are defined only once,
using the nodes on the reference triangle a. For Lagrange triangular elements, the
nodes (degrees of freedom) are distributed uniformly across the edges of the reference
2-simplex defined by the points (0, 0), (0, 1) and (1, 0).
Furthermore, the basis functions form a nodal basis, such that at a given reference
node a, only the corresponding basis function has a value of one while all other basis
functions vanish:
Na (xb ) = δab ∀a, b. (4.16)
4.1. The Poisson Problem 35

Here, a, b are basis points of the reference element. The basis functions for the
first-order P1 element are therefore linear and are shown in Fig. 4.1.

a) N1 (x, y). b) N2 (x, y). c) N3 (x, y).

Figure 4.1. P1 shape functions on the triangular reference element (2-simplex). The figures and a
WolframScript to generate arbitrary order Lagrangian basis function plots can be found in [54].

It should be mentioned that the FEniCS framework offers a variety of other elements,
some of them rather exotic, that can be explored in other examples [13] or in [35].
Some physical model problems require the usage of special elements. To improve
the quality of the discretization, we will later on use higher-order Pm elements. To
furthermore present an exemplary distribution of basis points inside the reference
triangle, all shape functions for the P3 element are presented in Fig. 4.2
The definition of a function space in FEniCS needs information about the mesh, such
as the spatial dimension. The definition of a suitable function space, using Lagrange
element of first order, then reads:
1 V = FunctionSpace(mesh, "Lagrange", 1)

Listing 4.2 Generation of function space using P1 elements for a given mesh [14].

The Dirichlet boundary conditions are defined with the help of an indicator function
and and DirichletBC-object. In the case of the model problem 4.1, the function u
is set to zero at the Dirichlet boundary ΓD = {(x, y) ∈ R : (0, y) ∪ (1, y) ⊂ ∂Ω}. The
corresponding definition in FEniCS uses the array of spatial coordinates, i.a. x[0],
and reads:
1 # Define Dirichlet boundary (x = 0 or x = 1)
2 def boundary(x):
3 return x[0] < DOLFIN_EPS or x[0] > 1.0 - DOLFIN_EPS
4 # Define boundary condition
5 u0 = Constant(0.0)
6 bc = DirichletBC(V, u0, boundary)

Listing 4.3 Definition of Dirchlet boundary conditions [14].

In order to solve the actual weak form 4.2, the trial and test functions are defined over
the previously created function space, so that the trial function can be expressed using
36 4. Introduction to the FEniCS Computing Platform

a) N1 (x, y). b) N2 (x, y). c) N3 (x, y). d) N4 (x, y).

e) N5 (x, y). f) N6 (x, y). g) N7 (x, y). h) N8 (x, y)

i) N9 (x, y). j) N10 (x, y).

.
Figure 4.2. P3 shape functions on the triangular reference element (2-simplex). The figures and a
WolframScript to generate arbitrary order Lagrangian basis function plots can be found in [54]

the given basis function with a simultaneous testing in the same basis. Furthermore,
all other relevant input data has to be defined. The source function f and the
Neumann boundary function (normal derivative) g are prescribed as

10 exp(−((x − 0.5)2 + (y − 0.5)2 ))


f (x, y) = , (4.17)
0.02
g(x) = sin(5x), (4.18)
while the Expression object allows the usage of standard C++ mathematical func-
tions. The coded weak form reflects the mathematical formulation in a natural manner
using tensorial functions, e.g. inner(), while integration domains are indicated using
either dx or ds. The resulting weak form in the FEniCS implementation therefore
reads:
1 # Define variational problem
2 u = TrialFunction(V)
3 v = TestFunction(V)
4 f = Expression(
5 "10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)",
6 degree=2
4.2. The Stokes Problem 37

7 )
8 g = Expression("sin(5*x[0])", degree=2)
9 a = inner(grad(u), grad(v))*dx
10 L = f*v*dx + g*v*ds

Listing 4.4 Weak form specification for Poisson’s problem [14].

The formulation of the weak form is sufficient in order to solve the discrete system. In
fact, all other element-level and system assembly routines are performed automatically.
The Dirichlet boundary conditions are also encoded in the function space by the
specification in the solve-method. It is, therefore, possible to efficiently solve the
same model equations with different boundary conditions. The discrete solution uh
of a(uh , v h ) = l(v h ) ∀v h ∈ V h is stored in a separate function, that can be written
to a file, e.g. in VTK file format. Due to its interoperability, solution fields can be
directly plotted using matplotlib. The final piece of the example code then reads:
1 # Compute solution
2 u = Function(V)
3 solve(a == L, u, bc)
4 # Save solution in VTK format
5 file = File("poisson.pvd")
6 file << u
7 # Plot solution
8 import matplotlib.pyplot as plt
9 plot(u)
10 plt.show()

Listing 4.5 Solving the weak formulation using given Dirichlet data [14].

The complete example source code example can be found in [18] and is listed in
Appendix D under Listing D.1 for reproducibility.

4.2. The Stokes Problem

In preparation to treat the linearized R13 equation system, starting in Chapter 5, a


second example using FEniCS for mixed problems is presented. The Stokes equations
act as the typical example for mixed methods. These equations describe a steady-
state flow of a viscous incompressible Newtonian fluid [77] and are a simplification of
the Navier–Stokes equations, neglecting the convective and unsteady terms [16].

The considered model problem, given as an introductory example of FEniCS in [15],


considers a slightly different version of the physical problem with a flipped sign for the
pressure. By neglecting physical parameters, the following problem can be stated:
38 4. Introduction to the FEniCS Computing Platform

Problem 4.4 (Stokes). For a closed domain Ω ⊂ R2 with a boundary ∂Ω = ΓD ∪ ΓN


as a union of a Dirichlet and Neumann boundary with outer normal vectors n, find the
scalar pressure function p : Ω → R and the vectorial velocity field function u : Ω → R2 ,
such that  
−∇ · ∇u + pI = f in Ω, (4.19)
∇·u=0 in Ω, (4.20)
u = u0 on ΓD , (4.21)
∇u · n + pn = g on ΓN , (4.22)
for a given right-hand side (source function) f : Ω → R and given boundary data
u0 : ΓD → R2 , g : ΓN → R2 .

In contrast to the Poisson problem of Section 4.1, the Stokes problem includes two
equations, the scalar-valued incompressibility constraint in Eq. (4.20) and the vector-
valued momentum balance in Eq. (4.19). It is therefore required to multiple with test
functions of matching tensorial degree. Eq. (4.19) is multiplied with v and integrated
over the domain Ω. Integration by parts is performed, allowing to insert the Neumann
boundary condition naturally. Note, that the divergence operator is recognized from
the inner product
I : ∇v = ∇ · v. (4.23)
Eq. (4.20) is integrated and multiplied with the pressure-corresponding test function
q with no integration by parts applied.
The weak form required for FEniCS does not include Dirichlet terms. This is because
of the particular way of encoding Dirichlet conditions into the corresponding spaces,
before solving the variational formulation. It is therefore sufficient to supply a weak
form assuming homogenous Dirichlet boundary conditions because the treatment
of essential boundary conditions is done automatically. Having this in mind, the
required weak formulation therefore reads:

Weak Formulation 4.5. Find (uh , ph ) ∈ W h = V h × Qh such that

a((uh , ph ), (v h , q h )) = l((v, q)), (4.24)

for all (v h , q h ) ∈ W h = V h × Qh , where


Z  
a((uh , ph ), (v h , q h )) = ∇uh : ∇v h − ph ∇ · v h + q h ∇ · uh dΩ, (4.25)
ZΩ Z
l((v h , q h )) = f · v h dΩ + g · v h dΓN , (4.26)
Ω ΓN

with proper mixed function spaces W h accounting for Dirichlet boundary conditions.
4.2. The Stokes Problem 39

The Stokes problem is of saddle-point structure with the pressure acting as a La-
grangian multiplier of the incompressibility constraint [16]. The solvability of the
discrete system, therefore, depends on the choice of the discrete finite element spaces.
In fact, after performing a rank analysis of the discrete matrix problem [16] to obtain
a non-singular system of equations, a necessary condition for a unique solution for
both pressure and velocity can be given in terms of function space dimensions [16]
as
dim Qh ≤ dim V h . (4.27)
A sufficient condition is the Ladyzhenskaya–Babuška–Brezzi (LBB) compatibility
condition [16], relating the discrete function spaces as
 
qh, ∇ · vh

inf sup ≥ α > 0. (4.28)
q h ∈Qh v h ∈V h kq h k0 kv h k1

Intuitively, one can think of this inf-sup condition as a restriction to the minimum
dimension of V h . In fact, for all q h ∈ Qh , there must exist a v h , such that the
projection (q h , ∇ · v h ) (in accord with the term occurring in the weak form) is non
vanishing.

The most common LBB-compliant mixed finite element is the Taylor–Hood element
P2 P1 , having piecewise continuous pressure and velocity. The velocity function
space allows for quadratic polynomials, while the pressure function space consists of
linear polynomials. Another option to circumvent the LBB condition is the use of
stabilization techniques, that are later introduced Chapter 5.

We will now highlight the differences in the implementation of a mixed formulation


in FEniCS compared to the basic concepts, already discussed in Section 4.1. The
example program from [18] uses a pre-generated computational mesh stored in the
mesh variable. The definition of the Taylor–Hood element is obtained by first con-
structing a P2 and P1 element, analogous to Section 4.1, followed by the generation
of a product of these two elements as:
1 # Define function spaces
2 P2 = VectorElement("Lagrange", mesh.ufl_cell(), 2)
3 P1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1)
4 TH = P2 * P1
5 W = FunctionSpace(mesh, TH)

Listing 4.6 Mixed function space definition for Taylor–Hood elements [15].

After defining the boundary conditions, trial and test functions are now created from
the mixed element function space and the resulting weak form is implemented as:
40 4. Introduction to the FEniCS Computing Platform

1 # Define variational problem


2 (u, p) = TrialFunctions(W)
3 (v, q) = TestFunctions(W)
4 f = Constant((0, 0))
5 a = (inner(grad(u), grad(v)) - div(v)*p + q*div(u))*dx
6 L = inner(f, v)*dx
7 # Compute solution
8 w = Function(W)
9 solve(a == L, w, bcs)

Listing 4.7 Mixed weak formulation for the Stokes problem [15].

The complete example source code can be found in [18] and is listed in Appendix D
under Listing D.2 for reproducibility.
This chapter introduced the implementation concepts of FEniCS in general with
the help of two introductory examples. It became clear that the focus is on the
specification of the weak formulation in tensorial notation. The treatment of essential
boundary condition is also done in a user-convenient way. The basic concepts can
easily be extended for mixed finite element problems, as we will encounter in the next
Chapter 5.
5. Decoupled Linearized Heat System

In order to get a better understanding of the full R13 system, we will split the system
physically into a heat system and a stress system to validate them separately, as it
was proposed in [68]. The first validation model problem, therefore, is extracted by
only considering all temperature and heat flux terms from the steady-state linearized
R13 system. The corresponding boundary conditions also only consists of θ, s and R
terms by neglecting all other cross-coupling terms. The extracted problem is called
decoupled heat system and reads:

Problem 5.1 (Decoupled Linearized Heat System, DLHS). For a given closed domain
nS
bc
Ω with boundary Γ := ∂Ω = Γi , find the temperature θ : Ω → R and the heat flux
i=1
s : Ω → Rd , such that

24
− Kn (∇s)STF − R = 0 in Ω, (5.1)
5
1 2 1 5
∇·R+ s + ∇θ = 0 in Ω, (5.2)
2 3 Kn 2
∇ · s = fh in Ω, (5.3)
1
 
2χ̃ (θ − θw ) + Rnn = sn on Γ, (5.4)
5
11
χ̃ st = Rnt on Γ, (5.5)
5
for a given heat source fh , a given modified accommodation coefficient χ̃, a given wall
temperature θw and under a given non-equilibrium situation described by the Knudsen
number Kn.

The problem can be seen as an extension to classical temperature modeling approaches


using only the heat equation. In Section 5.1, the weak formulation of this model
problem is derived to apply it on a sample geometry presented in Section 5.3.1.
In order to validate the finite element discretization, an empirical error analysis is
performed in Section 5.3. The need for stabilization techniques in the context of
mixed finite element methods is discussed by considering several combinations of
finite element spaces in Section 5.3.
42 5. Decoupled Linearized Heat System

5.1. Weak Formulation


In order to transform the strong form into a weak form, suitable for finite element
discretization, we integrate over the domain Ω while testing the vector Eq. (5.2) with
the test function r and the scalar Eq. (5.3) with the test function κ. Integration by
parts is further used twice in the vector equation for all Kn-independent r-terms as
1Z  1
 Z  Z   
∇ · R · r dx = − R : ∇r dx + R · n · r dl , (5.6)
2 Ω 2 Ω ∂Ω
5Z 5 Z Z 
∇θ · r dx = − θ (∇ · r) dx + θ (r · n) dl . (5.7)
2 Ω 2 Ω ∂Ω

To proceed further, we collect the boundary integrals and decompose the highest-
order moment R. In fact, for two spatial dimensions and with reference to a local
normal/tangential coordinate system along the boundary path, it holds that
 
R · n · r = Rnn rn + Rnt rt , (5.8)
θ (r · n) = θrn , (5.9)
allowing to collect the two boundary integrals of Eqs. (5.6) and (5.7) as
! !
5Z 1 1 5Z 1 1 11
    
Rnn + θ rn + Rnt rt dl = sn + θw rn + χ̃ st rt dl,
2 ∂Ω 5 5 2 ∂Ω 2χ̃ 5 5
(5.10)
where the rearranged boundary conditions (Eqs. (5.4) and (5.5))
1 1
Rnn + θ = sn + θ w , (5.11)
5 2χ
11
Rnt = χ̃ st , (5.12)
5
have naturally been inserted. Note that there is no need to enforce essential boundary
conditions. Lastly, the closure relation of Eq. (5.1) for the highest-order moment is
used to eliminate R. This is analogous to classical approaches, e.g. similar to the
elimination of the stress tensor in the classical Stokes problem [16]. The discrete weak
form can then be formulated as:
Weak Formulation 5.2. Find (s, θ) ∈ Vsh × Vθh , such that
!
12 Z Z
5 5
Kn (∇s)STF : ∇r dx + sn + θw rn dl
5 Ω Γ 4χ̃ 2
Z
11χ̃ 2 1 Z
5 Z
+ st rt dl + s · r dx − θ (∇ · r) dx = 0 (5.13)
Γ 10 3 Kn ΩZ 2 Ω Z
− (∇ · s) κ dx + fh κ dx = 0 (5.14)
Ω Ω
5.1. Weak Formulation 43

for all (r, κ) ∈ Vsh × Vθh .

The discrete weak form for the decoupled heat system is in agreement to previous
work in [68]. The superscript (·)h , usually denoting discrete functions, is skipped to
increase readability. Note that all natural boundary conditions enter the weak form.
No special treatment for Dirichlet boundary conditions has to be considered. We can
further identify the bilinear form a((·, ·), (·, ·)) and the linear functional ls ((·, ·)) on
the product space Vsh × Vθh as

ah ((s, θ), (r, κ)) = a1 ((s, θ), (r, κ)) + a2 ((s, θ), (r, κ)), (5.15)
ls ((r, κ)) = l1 ((r, κ)) + l2 ((r, κ)), (5.16)

with
Z 
12 2 1 5

a1 ((s, θ), (r, κ)) = Kn (∇s)STF : ∇r + s · r − θ (∇ · r) dx (5.17)
Ω 5 3 Kn 2 !
Z
5 11χ̃
+ s n rn + st rt dl,
∂Ω 4χ̃ 10
Z
a2 ((s, θ), (r, κ)) = − (∇ · s) κ dx, (5.18)

Z
5 w
l1 ((r, κ)) = − θ rn dl, (5.19)
Z∂Ω 2
l2 ((r, κ)) = − fh κ dx. (5.20)

In order to proceed with the numerical method, it remains to prescribe the spaces
2
Vsh ⊂ [H 1 (Ω)] and Vθh ⊂ H 1 (Ω) as the discrete subspaces of the Sobolev space H 1 (Ω).
We will use H 1 -conforming Lagrangian elements Pm , as introduced in Section 4.1.
The full implementation of the weak form is presented in the source code, Ap-
pendix B.2. An important implementation detail is the treatment of the symmetric
and trace-free (STF) operator in Eq. (5.17). For strictly two-dimensional problems,
the default built-in operators sym and dev of FEniCS/UFL can be used successively.
However, assuming a two-dimensional problem resulting from a z-homogenous three-
dimensional problem requires a change in the operator. In fact, the deviatoric part
of a 2-tensor is defined in the UFL [67] as

Aii
(A)dev = A − I, (5.21)
d
using the dimension d and Einstein’s summation notation in the trace Aii . If a
computation is therefore performed on a two-dimensional mesh, d is set to 2. In the
following, we assume homogeneity in the third spatial dimension leading to the two-
dimensional problem 5.2. A modified version to obtain the symmetric and trace-free
44 5. Decoupled Linearized Heat System

part of a 2-tensor is, therefore, used to account for this. For A ∈ R2×2 , the modified
STF operator is thus, for our purposes, defined as

1  Aii
(A)STF = A + AT − I. (5.22)
2 3
This can also be seen when assuming a 3D problem where none of the relevant fields
depends on the z-coordinate [65]. All operators except for (∇s)STF in Definition 5.2
would act two-dimensionally and the only required change to perform a 2D calculation
would be to enforce Eq. (5.22). The corresponding implementation of (·)STF therefore
artificially assumes d = 3 and reads:
1 def stf3d2(rank2_2d):
2 symm = 1/2 * (rank2_2d + ufl.transpose(rank2_2d))
3 return (
4 symm
5 - (1/3) * ufl.tr(symm) * ufl.Identity(2)
6 )

Listing 5.1 3D STF-operator for 2D 2-tensors.

5.2. Stabilization
As already discussed in Section 4.2, mixed finite element problems require to use
compatible element pairs. In the case of Stokes’s problem, a suitable choice to
circumvent the LBB condition is the Taylor–Hood element P2 P1 , where the dimension
of the velocity function space is higher than the dimension of the pressure function
space. When it comes to application cases, we do not want to focus on a particular
field, such that an equal order discretization is desired. Because of the lack of physical
intuition about higher-order moments, these should also not be focussed in terms
of discretization order. This is especially the case for even more extended model
equations above the R13 case.
One approach to overcome the compatible condition on the discrete function spaces
is stabilization. In general, stabilization techniques modify the left-hand side of the
weak form in order to stabilize the discrete system, i.e. adding entries to the zero
submatrix in the Stokes case for example. Residual-based stabilization techniques are
popular for flow problem [16] and add a stabilization term based on the value of the
current residuum R(u), which is the difference between the left and the right-hand
side of the considered model problem, as
nel
X
ã(u, v) = a(u, v) + (P(v), δR(u))Ωe , (5.23)
e=1
5.2. Stabilization 45

where the stabilization parameter δ has to be chosen and P(v) can be seen as a
perturbation of the test function v. Depending on the considered model, promi-
nent example are the Streamline-Upwind Petrov–Galerkin (SUPG) stabilization for
the steady advection-diffusion equation, the Galerkin/Least-Squares (GLS) stabi-
lization for the unsteady advection-diffusion equation or the Pressure-Stabilizing/
Petrov–Galerkin (PSPG) method for the Navier–Stokes equations [16].

In contrast to the above-listed stabilization methods, we will use the continuous


interior penalty (CIP) method, that adds stabilization terms based on edge (in two
dimensions) inner products rather than element inner products. The perturbation of
the joined weak form reads
XZ
ã((s, θ), (r, κ)) = ah ((s, θ), (r, κ)) − δ1 [∇θ · n] [∇κ · n] dl, (5.24)
E∈E E

where E is the index set of all interior element edges, δ1 is a stabilization parameter
(comparable to δ in Eq. (5.23)) and [f · n] = f + · n+ + f − · n− denoting the jump of
the quantity f across the edge, weighted with the oppositely directed edge normals
n+ and n− . If the temperature field θ is assumed to be in C 1 , no stabilization term
is added and the method is consistent [71]. In general, interior penalty methods have
strong mathematical foundations [49] and a wide application range [78]. An analysis
of the discrete system in [69] proposed a scaling of the stabilization term as

δ1 = δ˜1 h3 , (5.25)

such that the order of stabilization does not change due to mesh refinement. The
remaining parameter is not very sensitive and the method is very robust in order to
produce low errors for a wide range of δ˜1 -values. A discussion is presented e.g. in [10],
where the method was applied for the generalized Stokes problem.

The implementation in FEniCS uses the support for discontinuous Galerkin (DG)
operators in the UFL. An integration over all interior edges, as a subset of all edges,
can be archived using dS instead of ds. The resulting implementation then intuitively
reads:
1 # Apply stabilization
2 h_avg = (h("+") + h("-"))/2.0
3 stab_heat = - (
4 delta_1 * h_avg**3 *
5 df.jump(df.grad(theta), n) * df.jump(df.grad(kappa), n)
6 ) * df.dS
7 form_a = a1 + a2 + stab_heat

Listing 5.2 Implementation of CIP stabilization for the decoupled heat system.
46 5. Decoupled Linearized Heat System

5.3. Convergence Study Based on Mesh Refinement


In order to validate the numerical method, a convergence study is performed and the
discrete solutions are compared to exact solutions. The error behavior is discussed
concerning different element pair combinations and the corresponding exact solutions
are listed in Appendix A.1.1. Computations for a particular test case are performed
on a series of refined meshes. The overall setup is introduced in the following Sec-
tion 5.3.1.

5.3.1. Computational Domain and Input Parameters

The considered domain Ω ⊂ R2 is a ring and consists of the area between two coaxial
circles with radii R1 and R2 as
n o
Ω = x = (x, y)T : R1 ≤ kxk2 ≤ R2 , (5.26)

and follows previous works [65, 68]. This domain allows the computation of exact
solutions for the decoupled heat system 5.1. Sharp corners are also avoided and a
prescription of normal fluxes does not produce any problems, because the circle origin
is excluded from the domain. As already indicated in Section 5.1, we assume 2D
problems as simplifications for 3D problems with full symmetry and homogeneity
in the third spatial direction. The considered 2D domain can, therefore, be seen as
an area cut of an infinitely stretched cylinder with full symmetry and no change in
z-direction (vanishing gradients) as sketched in Fig. 5.1.

Figure 5.1. Extraction of a 2D model problem from an infinite cylinder with homogeneity in the
third spatial direction and thus vanishing gradients. A sketch of the two-dimensional cut is presented
in Fig. 5.2

The inner circle boundary is denoted by Γ1 and the outer circle by Γ2 . The remaining
parameters are chosen, such that comparison to existing literature [68] is possible.
5.3. Convergence Study Based on Mesh Refinement 47

The wall temperatures are prescribed as θw |Γ1 = 1.0 and θw |Γ2 = 0.5. The heat
2
√ 2 f2h depends on the radius and reads fh (R) = 2 − R withT R = kxk2 =
source
x + y denoting the radius as distance from the circle center (0, 0) . Furthermore,
the accommodation coefficient is assumed to be unity for the sake of simplicity.
Note, that this technically contradicts with the results obtained with the Maxwell
accommodation model, discussed in Section 3.3.3. In fact, the previously introduced
linearized accommodation coefficient
s
2 χ
χ̃ = , (5.27)
πθ0 2 − χ

reduces with the assumed reference temperature θ0 = 1 to

2
χ= q > 1, (5.28)
2
1+ π

which contradicts with χ ∈ [0, 1] [65]. The overall computational domain with all
relevant parameters is shown Fig. 5.2.

R2
fh
R1
Ω : Kn

Γ1
Γi : χ̃, θw
Γ2

Figure 5.2. Computational domain and all relevant parameters for the decoupled heat system. Inside
the domain Ω, the flow situation is described through the Knudsen number Kn and a heat source
fh acts. At both boundaries, the linearized accommodation coefficient χ̃ and the wall temperature
θw have to be prescribed.

In order to test the numerical method, we consider a series of general unstructured


triangular meshes without spatial refinement leading to approximately similar cell sizes
throughout the domain. The tests are therefore performed for a very general setup
without taking any properties of curved domains into account. The mesh resolution
at the inner boundary is equal to the resolution at the outer boundary. Using this
48 5. Decoupled Linearized Heat System

approach, the numerical method is, therefore, tested for principal correctness and
better results are expected using structured or adaptive meshes.
The mesh generator Gmsh [20] is used to create the series of ring meshes, because
it provided better meshing results than the FEniCS internal mshr-tool. Note that
no element split is applied to obtain the refined meshes. In fact, finer meshes are
the result of a complete remeshing procedure with a lower cell size factor. The mesh
resolution is characterized by the maximum cell size hmax . Some example meshes
are presented in Fig. 5.3 with their corresponding hmax . In contrast to [68], no
isoparametric higher-order representation of the domain boundaries is considered due
to the lack of compatibility in the current implementation of FEniCS. L2 -convergence
rates beyond second order are therefore not expected.

a) hmax = 0.99. b) hmax = 0.63. c) hmax = 0.33.

Figure 5.3. Series of unstructured triangular meshes used for the convergence study. Note, that the
coarsest mesh is not uniform to sufficiently resolve the inner boundary. This is the default behavior
of Gmsh, and with finer meshes, this effect vanishes. The bounding box of the mesh slightly varies
due to the curved boundary and the node placement.

5.3.2. Error Discussion

The test case, introduced in Section 5.3, is evaluated for a gas rarefaction setting of
Kn = 0.1. To rate the success of the numerical method, we first have to introduce
suitable error measures. The relative L2 function error eL2 is used as

kfex − fh kL2 (Ω)


eL2 = , (5.29)
max {fex |n }n∈η

where fex is the exact solution projected to a sufficiently large function space [32]
depending on the function space for the discrete solution fh and η is the set of all mesh
nodes. For vector- or tensor-valued fields, the error is calculated component-wise.
5.3. Convergence Study Based on Mesh Refinement 49

The resulting solution is reported in terms of node values. Therefore, particular


interest should also be given to these points, using a second relative error el∞ . We
define this vector-based error as

k{fex |n − fh |n }n∈η kl∞ (η)


el ∞ = , (5.30)
max {fex |n }n∈η

while l∞ is used to indicate that this error only considers point-wise errors based on
mesh node values. For cases, where the exact solution is uniformly vanishing, the
absolute error is used. If el∞ decays to zero for refined meshes, it is ensured, that for
all points of interest – i.e. the mesh nodes – the solution converges against the exact
solution.

Unstabilized P2 P1 Elements The error results using Taylor–Hood elements with-


out stabilization are presented in Fig. 5.4. As expected, the numerical method
convergence in the considered range of mesh sizes. Convergence rates in both L2 - and
l∞ -norms are obtained. The relative errors of the vector-valued heat flux s, discretized
with second-order elements, are below the errors of the scalar-valued temperature θ,
discretized with first-order elements.

Relative L2 -errors Relative l∞ -errors


100 100

10−1 10−1

10−2 10−2

10−3 10−3

10−4 10−4

10−1 100 10−1 100


hmax hmax
θ sx sy O(h) O(h2 )

Figure 5.4. Errors for the decoupled heat system using P2 P1 elements without stabilization. All
solution errors scale with second order in the mesh size h. The underlying error data is listed in
Tables A.1 and A.2.
50 5. Decoupled Linearized Heat System

Stabilized P1 P1 Equal-Order Elements Using equal-order P1 elements requires a


stabilization. The CIP method (Section 5.2) is applied with a stabilization parameter
δ̃1 = 1 following [68]. The errors of the obtained discrete solutions are presented
in Fig. 5.5. The L2 -error for both fields is of second order. However, a decreased
converge rate for the scalar-valued θ in the l∞ -norm can be observed in comparison
to Fig. 5.4.

Relative L2 -errors Relative l∞ -errors


100 100

10−1 10−1

10−2 10−2

10−3 10−3

10−4 10−4

10−5 10−5
10−2 10−1 100 10−2 10−1 100
hmax hmax
θ sx sy O(h) O(h2 )

Figure 5.5. Errors for the decoupled heat system using P1 P1 and CIP-stabilization (δ̃1 = 1). A
decreased converge rate for the scalar-valued θ can be observed in contrast to Taylor–Hood elements
without stabilization. The underlying error data is listed in Tables A.3 and A.4.

Stabilized P2 P2 Equal-Order Elements The lack of l∞ -converges in the temper-


ature can be overcome by using equal-order P2 elements, as shown in Fig. 5.6. As
expected, this element combination yields the most accurate solution and provides
the best convergence behavior by the cost of being the most expensive approach. A
schematic visualization of the discrete solution using this setup is presented in Fig. 5.7.
5.3. Convergence Study Based on Mesh Refinement 51

Relative L2 -errors Relative l∞ -errors


100 100

10−1 10−1

10−2 10−2

10−3 10−3

10−4 10−4

10−1 100 10−1 100


hmax hmax
θ sx sy O(h) O(h2 )

Figure 5.6. Errors for the decoupled heat system using P2 P2 and CIP-stabilization (δ̃1 = 1).
Second-order converges rates can be observed for all fields. The order of the relative error is similar
for all solution components. The underlying error data is listed in Tables A.5 and A.6.

θ ��� ��

-�

-�
-� -� � � �

Figure 5.7. Schematic results of the heat system test case. A radial-symmetric heat flux is induced
by the temperature difference between the inner and the outer walls.
6. Decoupled Linearized Stress
System

After the decoupled heat system 5.1 has been successfully discretized and solved,
the validation of the decoupled stress system is done analogously. The problem is
extracted from the full steady-state and linearized R13 system by only considering
the terms including the pressure, the velocity, the stress tensor and the highest-order
moment m. The corresponding boundary terms also only consist of p, u, σ and m
terms by neglecting all cross-coupling to the heat variables.

For real-life applications, it is often necessary to prescribe inflow or outflow conditions.


The initial normal velocity boundary condition un = 0 is therefore replaced by an
inflow model, following [65] as

w χ̃ ((p − pw ) + σnn ) = (un − uw


n) , (6.1)

where a wall pressure pw , a normal wall velocity uw


n and a normal velocity prescription
coefficient w have to be chosen. Intuitively, a value of w = 0 together with uwn = 0
w
reduces the inflow model back to the standard boundary condition. A value of  → ∞,
however, allows enforcing a pressure boundary condition, such that pw = p + σnn .

Following [68], a mass source fm , acting inside the domain Ω, is also added to the
mass balance equation as ∇ · u = fm . This mass source can be used to create test
cases with a mass source induced flow. Note, that the addition of a mass source
can violate the assumptions made in Section 3.3.1, because in that case, the energy
equation can not be simplified and the highest-order moment R is not zero. From a
physical point of view, the mass flow rate can result from chemical reactions under
different conditions, such that the mass source can be spatial dependent. However,
for real applications cases, it is very rare to model flows with a mass source but rather
common to use inflow/outflow-based induction mechanisms. The resulting problem
is called decoupled stress system and reads:

Problem 6.1 (Decoupled Linearized Stress System, DLHS). For a given closed
nS
bc
domain Ω ∈ Rd with boundary Γ := ∂Ω = Γi , find the pressure p : Ω → R, the
i=1
54 6. Decoupled Linearized Stress System

velocity u : Ω → Rd and the symmetric and trace-free stress tensor σ : Ω → Rd×d STF ,
such that
1
2(∇σ)STF + m=0 in Ω, (6.2)
Kn
1
∇·m+ σ + 2(∇u)STF = 0 in Ω, (6.3)
Kn
∇ · σ + ∇p = 0 in Ω, (6.4)
∇ · u = fm in Ω, (6.5)
w w w
 χ̃ ((p − p ) + σnn ) = (un − un ) on Γ, (6.6)
w
χ̃ ((ut − ut ) + mnnt ) = σnt on Γ, (6.7)
7
χ̃σnn = mnnn on Γ, (6.8)
5
1 1
   
χ̃ σtt + σnn = mntt + mnnn on Γ, (6.9)
2 2
for a given mass source fm , a given modified accommodation coefficient χ̃, a given
wall pressure pw , a given normal wall velocity uw n , a given tangential wall velocity
w w
ut , a normal velocity prescription coefficient  and under a given gas rarefaction
situation described by the Knudsen number Kn.

The model problem can be seen as an extension to classical flow modeling with
an additional evolution equation for the stress tensor σ. In Section 6.1, the weak
formulation of this problem is derived and the corresponding stabilization is applied in
Section 6.2. The convergence study, based on mesh refinement, in Section 6.3 discusses
the numerical errors in Section 6.3.2 for test cases defined in Section 6.3.1.

6.1. Weak Formulation

The weak form is derived by multiplying with the test functions (ψ, v, q) and integra-
tion over the domain Ω. The tensorial Eq. (6.3) is multiplied with the tensorial test
function ψ, the vectorial Eq. (6.4) with v and the scalar Eq. (6.5) with q.

Applying integration by parts in the tensorial equation yields


Z Z Z
∇ · m : ψ dx = − m ∵ ∇ψ dx + (m · n) : ψ dl. (6.10)
Ω Ω ∂Ω

The symmetric and trace-free velocity gradient is expanded into

1
(∇u)STF = (∇u)sym − tr(∇u)I, (6.11)
3
6.1. Weak Formulation 55

such that
Z Z
1Z
(∇u)STF : ψ dx = (∇u)sym : ψ dx − (∇ · u)I : ψ dx (6.12)
Ω Ω 3 Ω
Z
1Z
= (∇u)sym : ψ dx − (∇ · u)tr(ψ) dx. (6.13)
Ω 3 Ω
We reconsider that the stress tensor is symmetric and trace-free and the same prop-
erties are chosen for the test 2-tensor ψ, such that
   
σxx σxy 0 ψxx ψxy 0
σ= σ
 xy σyy 0 
 , ψ = 
ψ
 xy ψyy 0 .

(6.14)
0 0 − (σxx + σyy ) 0 0 − (ψxx + ψyy )
This setup vanishes the trace in Eq. (6.13) and by using the orthogonality principle
of the additive tensor decomposition into a symmetric and a skew-symmetric part,
integration by parts becomes possible as
Z Z
(∇u)STF : ψ dx = ∇u : ψ dx (6.15)
Ω ΩZ Z
=− u · (∇ · ψ) dx + u · (ψ · n) dl. (6.16)
Ω ∂Ω

The boundary terms of Eqs. (6.10) and (6.16) can further be decomposed with the
same technique as in Section 5.1 using a local boundary coordinate system as

(m · n) : ψ = mnnn ψnn + 2mnnt ψnt + mntt ψtt + (−mntt − mnnn )(−ψtt − ψnn ) (6.17)
3 1 1
  
= mnnn ψnn + 2mnnt ψnt + 2 mntt + mnnn ψtt + ψnn , (6.18)
2 2 2
u · (ψ · n) = un ψnn + ut ψnt , (6.19)
where mnnt = mntn was utilized. All boundary terms of the tensorial equation can
now be collected as
Z 
3
Z Z 
(m · n) : ψ dl + 2 u · (ψ · n) dl = mnnn + 2un ψnn dl
∂Ω ∂Ω ∂Ω 2
1 1
Z Z   
+ (2mnnt + 2ut ) ψnt dl + 2 mnnn + mntt ψnn + ψtt dl, (6.20)
∂Ω ∂Ω 2 2
where the reordered boundary conditions (Eqs. (6.6) to (6.9))

2un = 2w χ̃ ((p − pw ) + σnn ) + uw


n, (6.21)
2
2mnnt + 2ut = σnt + 2uw t , (6.22)
χ̃
3 21
mnnn = χ̃σnn , (6.23)
2 10 
1 1
  
2 mntt + mnnn = 2χ̃ σtt + σnn , (6.24)
2 2
56 6. Decoupled Linearized Stress System

are then inserted naturally. No integration by parts is applied for the vector-valued
equation but for the scalar-valued equation to enforce the normal velocity boundary
conditions as Z Z Z
− (∇ · u)q dx = u · ∇q dx − un q dl. (6.25)
Ω Ω ∂Ω
The complete discrete weak form then reads:
Weak Formulation 6.2. Find (σ, u, p) ∈ Vσh × Vuh × Vph , such that
Z Z
21
2 Kn (∇σ)STF ∵ ∇ψ dx + χ̃σnn ψnn dl
Ω Γ 10 !
Z
w w 2 Z
+ (2 χ̃ ((p − p ) + σnn ) + dl + σnt + 2uw
uw
n ) ψnn
t ψnt dl
Γ Γ χ̃
1 1 1 Z
Z   
+ 2χ̃ σtt + σnn ψtt + ψnn dl + σ : ψ dx
Γ 2 2 Kn ZΩ
−2 u · (∇ · ψ) dx = 0 (6.26)
Z Ω Z
(∇ · σ) · v dx + ∇p · v dx = 0 (6.27)
Z Z Ω ΩZ

u · ∇q dx − (w χ̃ ((p − pw ) + σnn ) + uw


n ) q dl + fm q dx = 0 (6.28)
Ω Γ Ω

for all (ψ, v, q) ∈ Vσh × Vuh × Vph .

We can again collect the combined functional as ((·, ·, ·), (·, ·, ·)), which is a bilinear
form on the product space Vσh × Vuh × Vph , and the linear functional ls ((·, ·, ·)) by a
simple reordering analogous to Section 5.1.
As we already notice, the weak formulation becomes relatively large, although written
in tensorial notation. It would be possible to implement the system component-wise
and solve for p, ux , uy , σxx , σxy , σyy after expanding all operators in the weak form 6.2.
However, this would increase the complexity even more and can be error-prone.
For example, the inner product of the 3-tensors (∇σ)STF and ∇ψ using the shape
assumptions of σ and ψ would read component-wise as
2 ∂σxx ∂ψxx 16 ∂σxx ∂ψxx 4 ∂σxx ∂ψxy
(∇σ)STF ∵ ∇ψ = + −
3 ∂y ∂y 15 ∂x ∂x 15 ∂x ∂y
2 ∂σxx ∂ψxy 1 ∂σxx ∂ψyy 1 ∂σxx ∂ψyy 2 ∂σxy ∂ψxx
+ + + +
3 ∂y ∂x 3 ∂y ∂y 3 ∂x ∂x 3 ∂x ∂y
4 ∂σxy ∂ψxx 16 ∂σxy ∂ψxy 16 ∂σxy ∂ψxy 4 ∂σxy ∂ψyy
− + + −
15 ∂y ∂x 15 ∂y ∂y 15 ∂x ∂x 15 ∂x ∂y
2 ∂σxy ∂ψyy 1 ∂σyy ∂ψxx 1 ∂σyy ∂ψxx 2 ∂σyy ∂ψxy
+ + + +
3 ∂y ∂x 3 ∂y ∂y 3 ∂x ∂x 3 ∂x ∂y
4 ∂σyy ∂ψxy 16 ∂σyy ∂ψyy 2 ∂σyy ∂ψyy
− + + . (6.29)
15 ∂y ∂x 15 ∂y ∂y 3 ∂x ∂x
6.1. Weak Formulation 57

We will, therefore, make extensive use of the tensor capabilities, provided by FEniCS
and UFL, to avoid to compute such expressions and have the corresponding source
code in a compact form, that directly matches the mathematical formulation. In
fact, up to second-order tensors, all UFL operators are intuitive, except for the
already discussed encoding of 3D information presented in Section 5.1. Dealing with
3-tensors, however, is not straight-forward because some required operators are not
yet implemented in FEniCS.

First of all, using a two-dimensional mesh leads to the creation of only two spatial
variables acting in the differential operators. In order to respect the shape assumptions
on σ and ψ of Eq. (6.14), a lifting operator L is defined, mapping a 2D 2-tensor
artificially to a trace-free 3D 2-tensor. The definition
 
! a b 0
a b
L : R2×2 → R3×3 , →
7 c d 0 , (6.30)
 
TF
c d

0 0 −(a + d)

can be directly translated into FEniCS as:


1 def gen3dTF2(rank2_2d):
2 return df.as_tensor([
3 [rank2_2d[0, 0], rank2_2d[0, 1], 0],
4 [rank2_2d[1, 0], rank2_2d[1, 1], 0],
5 [0, 0, -rank2_2d[0, 0]-rank2_2d[1, 1]]
6 ])

Listing 6.1 Custom operator to lift a 2D 2-tensor to a 3D STF 2-tensor.

The gradient operator is extended in a similar fashion to account for the third dimen-
sion as:
1 def grad3dOf2(rank2_3d):
2 grad2d = df.grad(rank2_3d)
3 dim3 = df.as_tensor([
4 [0, 0, 0],
5 [0, 0, 0],
6 [0, 0, 0],
7 ])
8 grad3d = df.as_tensor([
9 grad2d[:, :, 0],
10 grad2d[:, :, 1],
11 dim3[:, :]
12 ])
13 return grad3d

Listing 6.2 Custom gradient operator to account for three dimensions.


58 6. Decoupled Linearized Stress System

Having these operators available, it is possible to evaluate (∇σ)STF . In fact, the


definition [64] of the symmetric and trace-free part of a 3-tensor Aijk , that is given
by
1 
Ahijki = A(ijk) − A(ill) δjk + A(ljl) δik + A(llk) δij , (6.31)
5
can be used in FEniCS, including all Einstein summation conventions. The corre-
sponding function is then implemented as:
1 def stf3d3(rank3_3d):
2 i, j, k, l = ufl.indices(4)
3 delta = df.Identity(3)
4 sym_ijk = sym3d3(rank3_3d)[i, j, k]
5 traces_ijk = 1/5 * (
6 + sym3d3(rank3_3d)[i, l, l] * delta[j, k]
7 + sym3d3(rank3_3d)[l, j, l] * delta[i, k]
8 + sym3d3(rank3_3d)[l, l, k] * delta[i, j]
9 )
10 tracefree_ijk = sym_ijk - traces_ijk
11 return ufl.as_tensor(tracefree_ijk, (i, j, k))

Listing 6.3 Custom operator to obtain the STF part of a 3-tensor.

Here, the symmetric part of a 3-tensor is the average over all possible transpositions
and is given by
1
A(ijk) = (Aijk + Aikj + Ajik + Ajki + Akji + Akij ) . (6.32)
6
Again, the symmetric part can also be directly translated into UFL code using:
1 def sym3d3(rank3_3d):
2 i, j, k = ufl.indices(3)
3 symm_ijk = 1/6 * (
4 # all permutations
5 + rank3_3d[i, j, k] + rank3_3d[i, k, j] + rank3_3d[j, i, k]
6 + rank3_3d[j, k, i] + rank3_3d[k, i, j] + rank3_3d[k, j, i]
7 )
8 return ufl.as_tensor(symm_ijk, (i, j, k))

Listing 6.4 Custom operator to obtain the symmetric part of a 3-tensor.

These auxiliary functions are implemented in a separate tensoroperations-module,


listed in Appendix B.2, and allow a one-to-one correlation between the mathematical
formulation and the corresponding implementation of the weak formulation. In
general, the summation convention capabilities of FEniCS would also allow tackling
even higher-order moment equation, such as the R26 equations, if auxiliary n-tensor
operators are defined. The full implementation of the weak formulation for the
decoupled heat system is listed in Appendix B.2.
6.2. Stabilization 59

6.2. Stabilization
To allow the usage of equal-order elements, the stress system also has to be stabilized.
We stick to the same CIP framework as proposed in [68] and modify the left-hand
side of the weak formulation as
ãs ((σ, u, p), (ψ, v, q)) = as ((σ, u, p), (ψ, v, q)) + S((u, p), (v, q)), (6.33)
where the stabilization term is given by
XZ XZ
S((u, p), (v, q)) = δ2 [∇u · n] · [∇v · n] dl − δ3 [∇p · n] [∇q · n] dl.
E∈E E E∈E E

(6.34)
Note that the stress system, in contrast to the heat system, consists of three equations,
such that the two lowest-order quantities are stabilized. The stabilization parameters
are set to
δ2 = δ˜2 h3 , (6.35)
δ3 = δ˜3 h, (6.36)
in accordance to [68].

6.3. Convergence Study Based on Mesh Refinement


The validation of the proposed method for the decoupled stress system is analogous
to the tests for the heat system in Section 5.3. We consider the same series of un-
structured meshes and discuss the relative L2 - and l∞ -errors based on exact solutions,
that are listed in Appendix A.1.2. Due to different boundary conditions, the overall
test case changes and is introduced in Section 6.3.1.

6.3.1. Computational Domain and Input Parameters

The computational ring domain remains the same compared to the test of Section 5.3.1.
Both inner and outer ring walls are first modeled as solid walls, such that w = 0 and
uwn = 0 for both walls. This setup allows to compare against existing exact solutions
from [68]. We further use a moderate rarefaction state with Kn = 0.1 while the
stabilization terms are set to δ̃2 = 1 for the velocity and δ̃3 = 0.01 for the pressure
terms.
The flow inside the domain is either induced by the prescribed mass source fm or
by a prescribed tangential velocity uw
t at the inner cylinder wall. The computational
domain with all the prescribed parameters is presented in Fig. 6.1.
60 6. Decoupled Linearized Stress System

R2

R1
uw
t Ω : Kn

fm
Γ1
Γi : χ̃, w , pw uw w
n , ut

Γ2

Figure 6.1. The computational domain and all relevant parameters for the decoupled stress system.
Inside the domain Ω, the flow situations is described through the Knudsen number Kn and a mass
source fm is acting inside the domain. At both boundaries, the linearized accommodation coefficient
χ̃ has to be prescribed. Furthermore, both walls have a normal velocity component vtw = 0 to model
solid walls, and a rotation of the inner cylinder is prescribed by the tangential velocity component
vtw . The inflow model can be activated by prescribing w 6= 0 together with a wall pressure pw .

6.3.2. Error Discussion

Two test cases are considered to validate the decoupled stress system. In order to be
consistent with the simplification of the initial R13 system of Section 3.3.1, the first
test case does not include a mass source. The flow inside the domain is induced only
w,1
by a rotating inner cylinder with uw t = ut = −10 resulting in a counter-clockwise
rotation. The second test case considers the same inner rotation combined with a mass
2
source fm = 25 (1 − 185R
Kn2
) cos(φ), where φ ∈ [−π, π] is the polar angle with respect to
the positive x-axis. The exact solution for the second test case is derived in [68] while
the exact solution for the first test case has been derived separately as a simplification
of the second test case. Both exact solutions are listed in Appendix A.1.2. The
pressure has no reference in the problem specification, such that it is only determined
up to a constant. In order to compare with exact solutions, the pressure is therefore
scaled to have zero mean before an error comparison is performed.

Test Case 1: Stabilized P1 P1 P1 Equal-Order Elements The first test case de-
scribes a flow, that is induced by a rotation of the inner cylinder, without any mass
source. The error results for stabilized P1 equal-order elements are presented in
Fig. 6.2. Optimal convergence rates for the L2 -errors are obtained for all fields. In the
more restrictive l∞ -error, both velocity fields convergence, but with an order between
6.3. Convergence Study Based on Mesh Refinement 61

one and two. A schematic visualization of the discrete solution for this test case is
presented in Fig. 6.6a.

Relative L2 -errors Relative l∞ -errors

100 100

10−1 10−1

10−2 10−2

10−3 10−3

10−4 10−4

10−5 10−5
10−2 10−1 100 10−2 10−1 100
hmax hmax
p ux uy σxx σxy σyy O(h) O(h2 )

Figure 6.2. Errors for the decoupled stress system using P1 P1 P1 and CIP-stabilization (δ̃2 =
1, δ̃3 = 0.01) for the first test case. Second-order converges rates in the L2 -norm can be observed
for all fields. The pressure and stress fields have second-order convergence in the l∞ -norm while the
velocity is limited to about first-order. The underlying error data is listed in Tables A.7 and A.8.

Test Case 2: Unstabilized P3 P2 P1 Elements For the second test case, the most
natural choice of a stable triple of finite elements P3 P2 P1 is considered. The resulting
errors are presented in Fig. 6.3. The error of the pressure and stress components
have a lower error compared to the velocity components. Both velocity components
have a reduced convergence rate in the L2 -norm and in the l∞ -norm. An inspection
of the resulting fields for pressure, velocity and stress tensor showed no oscillations.
A schematic visualization of the discrete solution for this test case is presented in
Fig. 6.6b.

Test Case 2: Stabilized P1 P1 P1 Equal-Order Elements In contrast to P3 P2 P1


elements, the L2 -error rates can be improved by enabling CIP stabilization for the
lowest-order fields. The resulting errors are presented in Fig. 6.4. Now, both velocity
components have an improved, almost optimal convergence rate in the L2 -norm.
However, the convergence rate of the velocity in the l∞ -norm is one order less than
the rate for the other fields.
62 6. Decoupled Linearized Stress System

Relative L2 -errors Relative l∞ -errors

100 100

10−1 10−1

10−2 10−2

10−3 10−3

10−4 10−4
10−1 100 10−1 100
hmax hmax
p ux uy σxx σxy σyy O(h) O(h2 )

Figure 6.3. Errors for the decoupled stress system using P3 P2 P1 for the second test case. Second-
order converges rates in the L2 - and the l∞ -norm can be observed for the pressure and stress. The
errors in both velocity are higher and the convergence order is reduced compared to the other fields.
The underlying error data is listed in Tables A.9 and A.10.

Test Case 2: Stabilized P2 P2 P2 Equal-Order Elements The l∞ -error of the


velocity field can be further improved by using P2 P2 P2 elements. The resulting
errors in Fig. 6.5 show a second-order convergence rate for all fields in both errors.
However, third-order convergence rates were achieved in [68] using an isoparametric
boundary discretization approach. This approach is not available in FEniCS, such
that convergence rates beyond second-order are not expected.
6.3. Convergence Study Based on Mesh Refinement 63

Relative L2 -errors Relative l∞ -errors

100 100
10−1 10−1
10−2 10−2
10−3 10−3
10−4 10−4
10−5 10−5
10−2 10−1 100 10−2 10−1 100
hmax hmax
p ux uy σxx σxy σyy O(h) O(h2 )

Figure 6.4. Errors for the decoupled stress system using P1 P1 P1 and CIP-stabilization (δ̃2 =
1, δ̃3 = 0.01) for the second test case. Second-order converges rates in the L2 -norm can be observed
for almost all fields. The pressure and stress fields have second-order convergence in the l∞ -norm
while the velocity is limited to approximately first-order. The underlying error data is listed in
Tables A.11 and A.12.

Relative L2 -errors Relative l∞ -errors

100 100

10−1 10−1

10−2 10−2

10−3 10−3

10−4 10−4

10−1 100 10−1 100


hmax hmax
p ux uy σxx σxy σyy O(h) O(h2 )

Figure 6.5. Errors for the decoupled stress system using P2 P2 P2 and CIP-stabilization (δ̃2 =
1, δ̃3 = 0.01) for the second test case. Second-order converges rates in the L2 - and the l∞ -norm can
be observed for all fields. The underlying error data is listed in Tables A.13 and A.14.
64 6. Decoupled Linearized Stress System

σ�� ��� �� σ�� ��� ��


� �

� �

� �

-� -�

-� -�
-� -� � � � -� -� � � �
a) First test case. b) Second test case.

Figure 6.6. In Fig. 6.6a, a radial velocity profile can be observed due to the prescribed tangential
rotation of the inner cylinder wall. With mass source in Fig. 6.6b, a flow around the cylinder is
developed. Due to the present rotation of inner cylinder, the flow field is not symmetric.
7. Full Linearized R13 System

After the separate validation of the decoupled heat and stress system in Chapters 5
and 6, the coupled system, as derived in Section 3.3.1, is considered by adding all
coupling terms to the equations and the boundary conditions. In fact, the heat flux
balance has the additional coupling term ∇·σ while the deviatoric stress tensor balance
has the additional coupling term 45 (∇s)STF . The strong formulation consists of five
balance laws with two closures for the highest-order moments R and m, combined
with a set of six boundary conditions including the inflow model, that was introduced
in Chapter 6. The resulting problem can be formulated as:

Problem 7.1 (Linearized R13 System, LinR13). For a given closed domain Ω ∈ Rd
nS
bc
with boundary Γ := ∂Ω = Γi , find the temperature θ : Ω → R, the heat flux
i=1
s : Ω → Rd , the pressure p : Ω → R, the velocity u : Ω → Rd and the symmetric and
trace-free stress tensor σ : Ω → Rd×d
STF , such that

5 1 1 2
∇θ + ∇ · σ + ∇ · R = − s in Ω, (7.1)
2 2 Kn 3
∇ · s = fh in Ω, (7.2)
4 1
(∇s)STF + 2(∇u)STF + ∇ · m = − σ in Ω, (7.3)
5 Kn
∇p + ∇ · σ = 0 in Ω, (7.4)
∇ · u = fm in Ω, (7.5)
24
R = − Kn (∇s)STF in Ω, (7.6)
5
m = −2 Kn (∇σ)STF in Ω, (7.7)
66 7. Full Linearized R13 System

with the boundary conditions

(un − uw w w
n ) =  χ̃ ((p − p ) + σnn ) on Γ, (7.8)
1
 
σnt = χ̃ (ut − uw t ) + st + mnnt on Γ, (7.9)
5
11
 
Rnt = χ̃ −(ut − uw t ) + s t − m nnt on Γ, (7.10)
5
1 2
 
sn = χ̃ 2(θ − θw ) + σnn + Rnn on Γ, (7.11)
2 5
2 7 2
 
w
mnnn = χ̃ − (θ − θ ) + σnn − Rnn on Γ, (7.12)
5 5 25
1 1
   
mnnn + mntt = χ̃ σnn + σtt on Γ, (7.13)
2 2
for a given heat source fh , a given mass source fm , a given modified accommoda-
tion coefficient χ̃, a given wall temperature θw , a given wall pressure pw , a given
normal wall velocity uw w
n , a given tangential wall velocity ut , a given normal velocity
prescription coefficient w and under a given gas rarefaction situation described by
the Knudsen number Kn.

7.1. Weak Formulation


The weak form for the full linearized R13 equations is obtained by combining the
weak forms of the two decoupled systems with the addition of the coupling terms.
In fact, the expressions ∇ · σ and 45 (∇s)STF enter the equations without any special
treatment. The boundary conditions are reordered similar to the process for the
decoupled systems. This reordering yields better convergence results as reported
in [68]. In fact, we recognize similar terms in Eqs. (7.9) and (7.10) such that Eq. (7.9)
is added to Eq. (7.10) resulting in a modified version of Eq. (7.10) as
1 1 6
Rnt = σnt + χ̃ st . (7.14)
2 2 5
The same procedure is performed using Eq. (7.12) to rewrite Eq. (7.11) as
3 3 9
mnnn = − sn + χ̃ σnn . (7.15)
2 10 4
In Eqs. (7.14) and (7.15), the coefficients are changed compared to the expressions of
the decoupled systems and one additional coupling term is added for each condition.
In Eqs. (7.9) and (7.11), the coefficients remain the same and one coupling term is
added for each condition. Eqs. (7.8) and (7.13) remain the same compared to the
conditions of the decoupled stress system. Respecting these changes, the final weak
form can then be formulated as:
7.2. Convergence Study Based on Mesh Refinement 67

Weak Formulation 7.2. Find (s, θ, σ, u, p) ∈ Vsh × Vθh × Vσh × Vuh × Vph , such that

12 Z
2 1 Z 5Z
Kn (∇s)STF : ∇r dx + s · r dx − θ (∇ · r) dx
5 Ω 3 Kn Ω 2 Ω!
Z   Z
5 5 5
∇ · σ · r dx + sn − σnn + θw rn dl
Ω Γ 4χ̃ 8 2
Z 
6χ̃ 1

+ st − σnt rt dl = 0 (7.16)
Z Γ 5 2Z
− (∇ · s) κ dx + fh κ dx = 0 (7.17)
Ω Ω
Z
1 Z Z
2 Kn (∇σ)STF ∵ ∇ψ dx + σ : ψ dx − 2 u · (∇ · ψ) dx
Ω Kn Z Ω  Ω
4Z 9 3

+ (∇s)STF : ψ dx + χ̃σnn − sn ψnn dl
5 Ω Γ 4 10
1 1
Z  
+ 2χ̃ σtt + σnn ψtt + ψnn dl
Γ 2 !
2
Z
2 2
+ σnt + 2uwt − st ψnt dl
Γ χ̃ 5
Z
+ (2w χ̃ ((p − pw ) + σnn ) + uw
n ) ψnn dl = 0 (7.18)
Γ Z Z
(∇ · σ) · v dx + ∇p · v dx = 0 (7.19)
Z Z Ω ΩZ

u · ∇q dx − (w χ̃ ((p − pw ) + σnn ) + uw


n ) q dl + fm q dx = 0 (7.20)
Ω Γ Ω

for all (r, κ, ψ, v, q) ∈ Vsh × Vθh × Vσh × Vuh × Vph .

The full implementation of the weak formulation for the linearized R13 system is
listed in Appendix B.2.

7.2. Convergence Study Based on Mesh Refinement


The validation of the full linearized R13 system is analogous to the procedure for
the two sub-systems. The same series of refined ring meshes is used as introduced in
Section 5.3.1. The validation is further split into two test cases.
The first test case builds up upon the test case for the decoupled stress system. The
flow is induced by a rotation of the inner cylinder combined with a mass source. The
second test uses the inflow model together with radial-dependent velocity components
to create a flow around the inner cylinder, while the outer circular boundary then
only determines the size of the computational domain without any wall.
68 7. Full Linearized R13 System

7.2.1. Test Case 1: Mass Source Induced Flow in an Extruded


Ring

The mass source induced test case is the combination of the test cases for the decou-
pled heat and the decoupled stress system, presented in Sections 5.3 and 6.3. The
computational domain again consists of the area between two coaxial circles with radii
R1 = 12 and R2 = 2. A rotation of the inner cylinder is prescribed with uw,1 t = −10
5R2
while the mass source is increased to fm = (1 − 18 Kn2 ) cos(φ). The inflow model is
deactivated with w = 0 and the walls are modeled as impermeable with uw n = 0.
The addition of the heat system terms increases the number of parameters, such that
boundary conditions for the temperature with θw |Γ1 = 1 and θw |Γ2 = 12 are required.
No heat source fh = 0 is applied. The flow is characterized by a relatively high
Knudsen number of Kn = 1. Using this set of boundary conditions, the test case can
be compared to [68, 69]. The computational domain with all parameters is shown in
Fig. 7.1 while the exact solution for this test case is listed in Appendix A.1.3. The sta-
bilization parameters are inherited from both subsystems as δ̃1 = 1, δ̃2 = 1, δ̃3 = 0.01.
The following test cases show that the convergence results for the full system are
similar to the results of two separate tests of the subsystems.

R2

R1
uw
t Ω : Kn

fm
Γ1
Γi : χ̃, θw , uw w w w
t , un , p , 

Γ2

Figure 7.1. Computational domain and all relevant parameters for the linearized R13 system. Inside
the domain Ω, the flow situations is described through the Knudsen number Kn and a mass source
fm acts. At both boundaries, the linearized accommodation coefficients and the wall temperature θw
has to be prescribed. Furthermore, both walls have a normal velocity component vtw = 0 to model
solid walls and a rotation of the inner cylinder is prescribed by the tangential velocity component
vtw . The inflow model can be activated by prescribing w 6= 0 together with a wall pressure pw .

The convergence behavior for P1 equal-order elements for all fields is presented in
Fig. 7.2. In the L2 -norm and the l∞ -norm, the heat flux s, the pressure p and the
stress tensor σ have second order convergence rates. The temperature θ and the
7.2. Convergence Study Based on Mesh Refinement 69

velocity components uy , ux have a slightly decreased convergence rate in the L2 -norm,


i.e. below two. In the l∞ -norm, however, these fields only show first-order convergence.
This behavior is already known from both decoupled systems using P1 equal-order
elements.

Relative L2 -errors Relative l∞ -errors


101 101
100 100
10−1 10−1
10−2 10−2
10−3 10−3
10−4 10−4
10−5 10−5
10−2 10−1 100 10−2 10−1 100
hmax hmax
θ sx sy p ux uy σxx σxy σyy O(h) O(h2 )

Figure 7.2. Errors for the linearized R13 system using stabilized P1 equal-order elements (test case
1). The fields s, p, σ have second-order convergence rates in the L2 - and the l∞ -norm. The fields
θ, u have a reduced L2 -rate and have a first-order convergence behavior in the l∞ -norm. The largest
relative error can be observed for the velocity components. The underlying error data is listed in
Tables A.15 and A.16

In order to improve the convergence rates, equal-order P2 elements are tested for
the same test case. The corresponding errors are shown in Fig. 7.3. Especially, the
critical velocity and temperature fields, in that case, also show a second-order error
convergence behavior in the L2 -norm. The l∞ -rates are still slightly decreased and
are almost of second order.

Fig. 7.4 shows schematic field results for this test case using the last setup with
equal-order P2 elements. In contrast to the results for the decoupled stress system
in Fig. 6.6b, an opposite-directed flow can be observed. The reason for this is the
2
Kn-dependence of the mass source fm = (1− 185RKn2
) cos(φ) and the increased Knudsen
number of Kn = 1 in contrast to Kn = 0.1 in Fig. 6.6b.
70 7. Full Linearized R13 System

Relative L2 -errors Relative l∞ -errors

100 100

10−1 10−1
10−2
10−2
10−3
10−3
10−4
10−4
10−1 100 10−1 100
hmax hmax
θ sx sy p ux uy σxx σxy σyy O(h) O(h2 )

Figure 7.3. Errors for the linearized R13 system using stabilized P2 equal-order elements (test case
1). All fields have second-order convergence rates in the L2 -norm. The fields θ and u have a reduced
l∞ -rate while all other fields also convergence with second order in the l∞ -norm. In contrast to
Fig. 7.2, the number of refined meshes is decreased due to memory limitations. The underlying error
data is listed in Tables A.17 and A.18

7.2.2. Test Case 2: Cylinder Inside a Homogenous Flow

The second test case considers a flow scenario with inflow and outflow boundary
conditions prescribed, similar to the test case in [65]. The velocity prescription
coefficient, therefore, has to be w 6= 0. In contrast to previous test cases, the outer
wall is not modeled as impermeable but only acts as a cut-off from a larger homogenous
velocity field. The inner cylinder wall is still modeled as a non-rotating solid wall
with zero normal velocity uw w
n |Γ1 = 0 and zero tangential velocity ut |Γ1 = 0 prescribed.
A temperature difference is applied between the inner and the outer cylinder walls
with θw |Γ1 = 1 and θw |Γ2 = 2.

The flow is driven by a pressure difference at the outer cylinder wall with pw |Γ2 =
−p0 nx , in which the background pressure is set to p0 = 0.27 and nx is equal to
cos(φ) for the considered geometry. The velocity components at the outer boundary
are set to uw w
n |Γ2 = u0 nx and ut |Γ2 = −u0 ny with background velocity u0 = 1 and
ny = sin(φ). The remaining parameters read w |Γ1 = 10−3 to focus on velocity
prescription, w |Γ2 = 103 to focus on pressure prescription and Kn = 1 as flow
characterization. The computational domain with all parameters is shown in Fig. 7.5
while the exact solution for this test case is listed in Appendix A.1.3.
7.2. Convergence Study Based on Mesh Refinement 71

σ�� ��� �� θ ��� ��


� �

� �

� �

-� -�

-� -�
-� -� � � � -� -� � � �
a) Shear stress σxy and velocity streamlines ui . b) Temperature θ and heat flux streamlines si .

Figure 7.4. Schematic results of first test case for the linearized R13 equations. The flow past the
cylinder in Fig. 7.4a is not symmetric due to the prescribed rotation of the inner wall. In Fig. 7.4b,
a heat flux from warm to cold regions can be observed due to supplied boundary conditions.

R2

R1
Ω : Kn

Γ1
Γi : χ̃, θw , uw w w w
t , un , p , 

Γ2

Figure 7.5. Computational domain and all relevant parameters for the linearized R13 system (test
case 2). Inside the domain Ω, the flow situations is described through the Knudsen number Kn.
The inner cylinder wall is assumed to be solid (small w and zero tangential and normal velocity
components). A pressure gradient in x-direction is acting at the outer boundary.

The stabilization parameters remain unchanged compared to the previous test case
and the errors using P1 equal-order elements are presented in Fig. 7.6. A similar
convergence behavior, as for the previous test case, can be observed. In the l∞ -
norm, the heat flux, the pressure and the stress tensor components show second-order
72 7. Full Linearized R13 System

convergence rates while the remaining temperature and velocity components only
show first-order convergence. This is similar to all previous test cases with stabilized
P1 elements. However, in the L2 -norm, both velocity components, now, also show
second-order and only the temperature shows a convergence rate below second-order.
This is a difference compared to the previous test case using P1 elements.

Relative L2 -errors Relative l∞ -errors

100 100
10−1 10−1
10−2 10−2
10−3 10−3
10−4 10−4
10−5 10−5
10−2 10−1 100 10−2 10−1 100
hmax hmax
θ sx sy p ux uy σxx σxy σyy O(h) O(h2 )

Figure 7.6. Errors for the linearized R13 system using stabilized P1 equal-order elements (test case
2). All fields, except for the temperature, have second-order convergence rates in the L2 -norm. The
temperature error, furthermore, shows a reduced l∞ -rate together with both velocity components
while all other fields also convergence with second order in the l∞ -norm. The underlying error data
is listed in Tables A.19 and A.20

Fig. 7.7 shows schematic field results for this test case. The homogenous outer flow
field enters the computational domain in Fig. 7.7a in parallel. The stress component
σxy has a greater magnitude at areas, where the flow field is parallel to the inner
cylinder walls. As expected with the given set of heat boundary conditions, Fig. 7.7b
shows a heat flux from the warm outer cylinder wall to the cold inner wall. However,
the temperature field is advected in flow direction, such that a colder gas region can
only be observed behind the cylinder while the region before the cylinder is warmer.
7.2. Convergence Study Based on Mesh Refinement 73

σ�� ��� �� θ ��� ��


� �

� �

� �

-� -�

-� -�
-� -� � � � -� -� � � �
a) Shear stress σxy and velocity streamlines ui . b) Temperature θ and heat flux streamlines si .

Figure 7.7. Schematic results of second test case for the linearized R13 equations. The flow past
the cylinder in Fig. 7.7a induces higher |σxy | values above and below the cylinder, where the flow
direction is tangential to the inner cylinder walls. The temperature distribution in Fig. 7.7b reveals
a colder region behind the cylinder due to the present flow field.
8. Applications
In order to present the capabilities of the developed solver, available from [58], this
chapter shortly presents some application case. It is possible to perform calculations
on arbitrary shaped two-dimensional geometries. We will, therefore, consider three
application cases. The first case in Section 8.1 is the lid-driven cavity, which is a very
common benchmark for two-dimensional flow solvers. The case also acts as the first
test case for non-curved boundaries.
In order to highlight the enhanced capabilities to predict flows for moderate Knudsen
numbers, the other two test cases will discuss typical rarefaction effects. In fact, the
channel flow example in Section 8.2 shows a similar behavior, regarding the Knudsen
paradox, as it was discussed in [62] for the one-dimensional case. In Section 8.3, a
temperature gradient at the domain walls induces a thermal transpiration flow without
the presence of gravity. The two latter application cases would not be possible using
a classical NSF solver and confirm the previously made statement, that the R13
equations, in fact, are able to predict rarefaction effects.

8.1. Lid-Driven Cavity Benchmark


The first test case considers a lid-driven cavity, which is a standard test case amongst
flow solvers and is not restricted to finite elements based solvers [25]. In [43], Rana,
Torrilhon, and Struchtrup used this test case to compare a finite difference based
discretization of the R13 system to direct numerical methods. The solver in the
context of this work is limited to the linearized R13 system, such that it cannot be
directly compared to the results in [43]. However, qualitative comparisons in terms of
characteristic flow features could be made, and the case can especially be important
in future work, when a detailed benchmark of the numerical method is desired.
In Fig. 8.1, the two-dimensional computational domain, consisting of a square with
side length L = 1, is presented. The boundary Γ is composed of the upper boundary
line Γ2 , and the remaining boundary lines joined in Γ1 . A horizontal x-aligned velocity
profile is prescribed at the upper boundary, such that uw t |Γ2 = u0 = 1. The remaining
boundary portion is assumed to consist of solid walls, such that the normal and
tangential velocity components are set to zero. Furthermore, both boundaries have a
76 8. Applications

u0
Γ2 : θw

Ω : Kn

Γ1 : θw

L
Figure 8.1. Computational domain for the lid-driven cavity application case as the unit square
with a prescribed tangential velocity at the upper boundary. All other boundaries are modeled as
solid walls.

uniform temperature of θw = 1. The Knudsen number is set to Kn = 0.08 while the


modified accommodation coefficient is set to unity for all boundaries.

We solve the problem with equal-order P1 elements using the CIP stabilization method
with the stabilization parameters δ̃1 = δ̃2 = 1, δ̃3 = 0.01. The computational mesh is
a uniformly sized unstructured triangular mesh, consisting of 2654 elements and 1392
nodes. The corresponding solver input file is listed in Listing C.1 and the solution
routine takes 1 s to run.

The discrete solutions are presented in Fig. 8.2. The deviatoric shear stress σxy and
the velocity streamlines are shown in Fig. 8.2a. These fields are similar to the results
obtained in [43]. The heat flux and the temperature distribution in Fig. 8.2b is not
symmetric.

8.2. Knudsen Paradox in a Channel Flow

Another classic example is a microchannel flow similar to the one-dimensional case


discussed in [62]. In our two-dimensional setting, the flow is in between two infinitely
large plates with a distance of H = 1 and is driven by a pressure difference between the
inflow pressure pw |Γ4 =1 and the outflow pressure pw |Γ2 = 0. Prescribing this pressure
gradient is equivalent to an acting body force as in [62]. A uniform temperature
θw = 1 is applied to all boundaries. The upper and lower solid walls have w = 10−3
with zero velocity components. The in- and outflow walls are modeled with the
8.2. Knudsen Paradox in a Channel Flow 77

σ�� ��� �� θ ��� ��


��� ���

��� ���

��� ���

��� ���

��� ���

��� ���
��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���
a) Shear stress σxy and velocity streamlines ui . b) Temperature θ and heat flux streamlines si .

Figure 8.2. Schematic results of the lid-driven cavity application case. The velocity field in Fig. 8.2a
shows a clockwise rotation as expected. The heat flux and the temperature in Fig. 8.2b are not
entirely symmetric, which is similar to the results of [43].

parameter w = 103 and uw w


n = ut = 0. The overall situation is presented in Fig. 8.3
and the solver input file is listed in Listing C.2.

Γ3
Γ4 : pw,4 Ω : Kn H Γ2 : pw,2
Γ1

L
Figure 8.3. Computational domain for the channel flow application case. The flow is driven by a
pressure difference between the inflow and outflow boundary. The upper and lower boundaries are
modeled as solid walls.

The resulting computational mesh consists of 10712 uniform but unstructured triangles
with 5517 nodes. The problem is discretized using P1 equal-order finite elements and
CIP stabilization with the usual parameters is applied. After the discrete system is
solved in 3 s, the result in Fig. 8.4 are obtained for a Knudsen number of Kn = 0.1.
As expected, the flow field in Fig. 8.4a is almost parallel to the outer walls, and
the deviatoric stress component σxy has its maxima at both outer channel walls. In
Fig. 8.4b, the heat flux is nonzero, although no temperature gradient is applied.
78 8. Applications

σ�� ��� �� θ ��� ��


��� ���
��� ���
��� ���
��� ���
��� ���
��� ���
� � � � � � � � � �
a) Shear stress σxy and velocity streamlines ui . b) Temperature θ and heat flux streamlines si .

Figure 8.4. Schematic results of the channel flow application case for Kn = 0.1. The velocity field
in Fig. 8.4a shows a flow, that is almost parallel to the outer walls. Varying temperatures and a
heat flux in inflow direction can be observed in Fig. 8.2b.

An exciting phenomenon can further be observed during a parameter study for the
Knudsen number. A series of calculations are performed for a range of Kn-numbers
in [0.03, 2.0] using the same overall problem setup. A dimensionless mass flow rate
through the outflow boundary can be defined as
Z
J˜ := u · n dl, (8.1)
Γ2

similar to the one-dimensional considerations in [62]. With increasing Knudsen num-


ber, a decreasing mass flow rate is observed. However, the dimensionless mass flow
rate has a minimum at about Kn ≈ 0.3 with a subsequent increase again. This
phenomenon is known as Knudsen paradox, following [62], and is confirmed by mea-
surements [74]. The relation between the dimensionless mass flow and the Knudsen
number is presented in Fig. 8.5.

0.7
Linearized R13 equations
Diml. mass flow rate J˜

0.6

0.5

0.4

0.3

10−1 100
Kn

Figure 8.5. Knudsen paradox in a channel flow. With increasing Knudsen number, the dimensionless
mass flow through the channel first decreases to a minimum before increasing again. Increasing the
λ
Knudsen number, in this context Kn = H , either means diluting the gas or decreasing the channel
width H.
8.3. Thermal Transpiration Flow in a Knudsen Pump 79

8.3. Thermal Transpiration Flow in a Knudsen Pump

Another rarefaction effect can be observed in a Knudsen pump, which is inspired by [4,
33, 72]. Without any body force acting, a flow is solely induced by a temperature
gradient at the outer walls.

We consider a racetrack-shaped geometry, that is created by slicing a ring with inner


and outer radii R1 and R2 into two halves and placing two rectangular connections
in between these elements to form a closed track. The two connection elements have
the side lengths 2L and R2 − R1 . In order to prescribe a linear temperature profile at
the boundaries, four control points for each wall are considered, as presented Fig. 8.6.
The lower temperature is set to θ0 = 0.5 and the higher temperature is set to θ1 = 1.5.

θ0 Γ6 θ1

R2
θ0 Γ2 θ1
L
Γ7 Γ3 Γ1 Ω Γ5
R1
θ1 Γ4 θ0

θ1 Γ8 θ0

Figure 8.6. Computational domain for the Knudsen pump application case. The flow is driven
by a temperature gradient at the outer walls. All walls are modeled as impermeable. The starting
points of both half rings act as control points for the prescribed temperatures θ0 and θ1 . In between
these points, a linear temperature gradient is applied.

The boundary path between θ0 and θ1 have a linear temperature profile applied. In
order to derive the corresponding expressions for θw , we need to define a modified
polar angle function. Let φ(a,b) (x, y) : R2 → [−π, π] denote a function, returning the
polar angle concerning the shifted origin (a, b), such that for example φ(1,0) (1, 2) = π2 .
This function is available in most programming languages as atan2(y,x). Table 8.1
shows the corresponding boundary expressions for the temperature at the inner wall.
For the outer wall, the same expressions as for the inner wall are used, such that
for example θ|Γ5 = θ|Γ1 . The corresponding input file also makes use of predefined
macros to obtain the polar angle and is listed in Listing C.3.
80 8. Applications

Boundary path Γi Temperature expression θ|Γi


12
Γ1 φ (x, y) + 1
2 π (1,0)
1
Γ2 2
x+1
12
Γ3 − 2 π φ(−1,0) (−x, y) + 1
Γ4 − 12 x + 1

Table 8.1. Temperature boundary conditions for the Knudsen pump.

The remaining parameters uw w w


n , ut and  are all set to zero in order to model solid
walls. The discrete solution is obtained for an unstructured triangle mesh consisting
of 12190 elements and 6286 nodes. P1 equal-order elements, with CIP stabilization
(δ̃1 = δ̃2 = 1, δ̃3 = 0.01) enabled, are used. It takes 4 s to obtain the discrete solution
of Fig. 8.7. A counter-clockwise gas flow can be observed in Fig. 8.7a and the stress
component σxy has higher values at the relatively curved parts of the geometry near
the inner wall. Fig. 8.7a shows the linearly applied temperature profile at the walls.
However, this temperature profile changes throughout the pump width due to diffusion.
Intuitively, a heat flux from warm to cold regions can also be observed.

σ�� ��� �� θ ��� ��


� �

� �

� �

-� -�

-� -�
-� -� -� � � � � -� -� -� � � � �
a) Shear stress σxy and velocity streamlines ui . b) Temperature θ and heat flux streamlines si .

Figure 8.7. Schematic results of the Knudsen pump application case. A gas flow in counter-
clockwise direction can be observed in Fig. 8.2a, similar to the results obtained in [4, 72]. In
Fig. 8.2b, the linear temperature gradient at both boundary walls can be observed, resulting in a
heat flux from warm to cold regions.
9. Conclusion

Aim of this project was to enable the simulation of gas flows in non-equilibrium
situations using the FEniCS computing platform. Recalling the discussion of Chap-
ter 1, the FEniCS framework is a promising-looking, modern and popular software for
solving partial differential equations using the finite element method. As it turns out
throughout this thesis, its enhanced capabilities of treating tensor-valued expressions
in weak formulations are very beneficial for the specific use case of modeling rarefied
gas flows using the tensor-valued system of R13 equations.

In order to generally motivate the usage of extended models, suitable for moderate
Knudsen number flows, the classical gas models were introduced in Chapter 2. They
consist of the three fundamental balance laws together with the closures of Navier–
Stokes and Fourier. A subsequent discussion showed the limitations of the classical
models, i.e. the lack of predicting various rarefaction effects, that play an essential role
in non-equilibrium gas flows. In fact, gas flows in the transition regime with moderate
Knudsen numbers, require more refined and extended model equations to accurately
predict the behavior of rarefied gases or gas flows in miniaturized applications.

The R13 equations were the main model equations throughout this project. The
set of equations was derived from the kinetic gas theory in Chapter 3 and can be
seen as an approximation of the Boltzmann equation. Being two orders of magni-
tude more accurate in the Kn-number from an asymptotic perspective [64], the R13
equations extend the classical NSF system with additional evolution equations for
the heat flux and the deviatoric stress tensor leading to an increase model complexity.
This additional model accuracy is required to predict the rarefaction effects of a gas
in the non-equilibrium. As a first step, the full nonlinear R13 system was reduced
to a steady-state, linearized and two-dimensional system of equations, that is still
capable of predicting non-equilibrium effects as seen in Chapter 8. In fact, both
decoupled systems were implemented as mixed systems in Chapters 5 and 6. During
the implementation, it turned out, that additional tensorial operations had to be
implemented in order to treat tensors with a rank above two. Code snippets showed
the corresponding operators, implemented using the summation convention, that can
naturally be used in form language of FEniCS. These additional operator functions
enabled an implementation, that matches the mathematical formulation of the corre-
sponding weak form – one of the main concepts in the software engineering approach
82 9. Conclusion

of FEniCS according to [34]. The numerical discretization, using the finite element
method, was performed successively for a decoupled heat and stress system before
the full linearized R13 system was addressed. After a general introduction to the
finite element method and the FEniCS framework in Chapter 4, the intuitive imple-
mentation of a mixed finite element formulation was discussed using the exemplary
Stokes problem. This allowed understanding requirements and pitfalls in the process
of translating the mathematical mixed weak formulation into its corresponding source
code statements.

Both extracted subsystems were stabilized using the CIP method to enable the use
of equal-order Lagrangian elements, as the most basic and intuitive choice for finite
elements. A convergence study with exact solution showed convergence for both
subsystems, such that the full linearized R13 system resulted by the combination of
both weak forms with additional coupling terms, as discussed in Chapter 7. The full
linearized system was also validated against exact solutions for test cases, including
mass flow driven and inflow induced gas situations. The resulting convergence rates
were similar to the convergence rates of both subsystems, such that the full equations
also converge. While the usual convergence measure is often solely the L2 -norm,
all convergence studies in this work additionally considered a more restrictive node-
wise l∞ -error, in which a decreased convergence rate was observed in contrast to the
traditional L2 -error.

An essential part of this project was the implementation of a rarefied gas solver
fenicsR13 [58]. The solver can be accessed according to Section 9.2.1 and builds on
the most recent version of the FEniCS platform. The particular software engineering
concept of FEniCS, i.e. separating the mathematical statements from auxiliary FEM
implementation, enabled an intuitive implementation in the unified form language.
This high-level Python interface of FEniCS integrates well with other software tools
and file formats. The programmed solver is, therefore, programmed in a general way
allowing for arbitrary complex geometries and a wide range of boundary conditions
using a clean input file. Popular Python software engineering tools, such as pytest
and Sphinx, are integrated and enable continuous integration (CI) testing and an
automated generation of source code documentation. The capabilities of the imple-
mented solver were tested in Chapter 8 by considering application cases, in which
rarefaction effects are expected. Due to the observed convergence of the numerical
method to exact solutions, the prediction of the Knudsen paradox in Section 8.2 was
no surprise because it was already found in a similar one-dimensional setting in [62]
analytically. A transpiration or temperature-induced flow without a body force was
observed in Section 8.3. The implemented solver should serve as a starting point for
future work and could be a valuable alternative to classical NSF simulations for gas
flows in a rarefied setting. Possible extensions are discussed and presented in the
following Section 9.1.
9.1. Future Work 83

The underlying computing platform FEniCS, indeed, turned out to be an easy, and
intuitive to use, framework that allows to implement mathematical models quickly.
Numerical methods for the R13 equations required the treatment of tensorial struc-
tures and the ability to quickly interchange between mathematical formulations,
e.g. to switch stabilization methods, system formulations or boundary conditions.
Except for the treatment of rank-3 tensors, FEniCS can handle all the above in its
current implementation, and the resulting source code remains rather compact and
readable.

9.1. Future Work

The underlying formulations of the fenicsR13 solver for the steady-state, linearized
and two-dimensional R13 equations were presented. The current implementation of
the solver, therefore, allows to extend the research in various directions and can act as
a starting point for further improvements and new ideas. During the theoretical recap
of the model equations and during the formulation of the numerical method, certain
simplifications were applied. Future work could aim to remove these simplifications
in order to extend the solver to more general use cases. Further improvement and
new directions of research can be collected and can be sorted into the following four
categories:

• Model Complexity: (a) The underlying model shall successively be refined in


terms of model complexity. This includes a more general formulation with
a dimensionless body force, a volumetric heat source and a potential mass
source. This would result in a setup, where the highest-order moment R in
the heat flux balance cannot be neglected; (b) The extension to a transient
(unsteady) formulation would include the addition of suitable time integration
schemes or space-time formulations; (c) A nonlinear formulation would require
the addition of an additional solver for the nonlinear system of equations. Here,
the algorithm could be inspired by classical algorithms for nonlinear problems,
such as classical methods for the NSF system. FEniCS also offers certain
capabilities to solve nonlinear variational problems; (d) A three-dimensional
model formulation would require more boundary conditions for the additional
second tangential direction. These boundary conditions already exist, such that
the projection into a boundary aligned coordinate system would remain as the
only challenge because all other tensorial operators would remain the same.
While the tensorial approach of FEniCS was already used in this thesis, the
extension to 3D should be straight-forward if the boundary conditions can be
inserted into the corresponding weak forms. It should further be possible to
derive exact solutions also for three dimensions, based on the Stokes approach
84 9. Conclusion

for simple geometries such as spheres. These exact solutions should be used to
validate the method further.

• Numerical Analysis: A detailed analysis of the used numerical method can


formalize the computational results. This could include a reformulation of the
variational forms in terms of sub functionals as it is common in the framework
of mixed finite element methods. The analysis can make use of already existing
concepts for simpler problems and extend these for the enlarged R13 equation
system. This would allow analyzing the resulting discrete system, such that a
stable 5-tuple of Lagrangian finite elements might be proven. An error analysis
could lead to a-priori error estimates, such that the behavior of the numerical
method can be measured in terms of optimal convergence rates. Stabilization
can also be subject to further research. The proposed CIP method could be
extended by a potentially optimal stabilization parameter or different stabiliza-
tions, such as GLS, could be implemented. Based on an analysis of the discrete
system, the solver routine can be changed to use a suitable iterative solver and
preconditioners. This would allow increasing the mesh complexity.

• Performance and Error Comparison: The used finite element framework can
be tested against other numerical approaches in terms of accuracy and overall
performance. A comparison could identify bottlenecks and should be executed
with respect to already existing approaches, such as finite difference schemes,
discontinuous Galerkin methods, or Direct Simulation Monte Carlo (DSMC)
methods. An aim could further be to test the method against experimental
data for a series of problem parameters. This could help to identify parameter
regions, where the given models are not sufficiently accurate. The capabilities
of FEniCS for parallel simulations can also be subject for further studies.

• Implementation and Accessibility: The implementation should keep its intuitive


one-to-one relation between the mathematical formulation and the correspond-
ing implementation provided by the FEniCS framework. An adaptive approach
could extend the solver with additional models equations such as the NSF sys-
tem or higher-order moment systems (e.g. R26). Ideally, the solver could make
use of estimated errors in order to choose the required model equations to meet
an user-specified accuracy, similar to [65].

9.2. Technical Details

In order to guarantee reproducibility of the obtained results, the used hardware, the
relevant software and other technical details are listed in the following.
9.2. Technical Details 85

9.2.1. Solver Source Code

The solver is hosted on RWTH’s Gitlab instance in [58]. The solver’s documentation
is also hosted on RWTH’s Gitlab instance using the pages feature of Gitlab in [57].
The repository contains all convergence tests and all application cases. A snapshot
of the current solver documentation and the relevant solver source code are given in
Appendices B.1 and B.2.

9.2.2. Calculations and Hardware

Unless otherwise stated, all calculations were performed in a Docker container on an


iMac 2017 with a 3.4 GHz Intel Core i5–7500 CPU using serial execution. The
system has a total memory of 48 GB available. The resulting discrete systems were
solved with the default direct solver UMFPACK [12], shipped with FEniCS.

9.2.3. Software Versions

In Table 9.1, the specific version numbers of all software tools, used in the context of
this work, are listed.

Program Version
Gmsh 4.4.0
FEniCS (Docker image) 2019.1.0.r3
Python 3.6.8
Docker 2.1.0.2
Paraview 5.6.0
Wolfram Mathematica 12

Table 9.1. Version numbers of used software.


A. Convergence Data and Exact
Solutions

A.1. Exact Solutions


To ensure reproducibility of the obtained convergence results, the used exact solutions
are listed here in C++-format. This format can be directly used within the FEniCS
framework or in any other C++-based solver. Using the C++ interface was required
in order to have access to the modified Bessel functions, provided by the Boost
libraries [9]. Using Python binding syntax, the resulting .cpp files can then directly
be used as:
1 with open("file_to_exact_solution.cpp", "r") as file:
2 exact_solution_cpp_code = file.read()
3 esol = df.compile_cpp_code(exact_solution_cpp_code)
4 theta_esol = df.CompiledExpression(esol.Temperature(), degree=2)

Listing A.1 Usage of compiled C++ expressions in FEniCS.

The listed exact solutions are based on an ansatz inspired by the derivation of exact
solutions for the Stokes equations in cylindrical coordinates (using R, φ, z), that was
performed in [68, 69]. The fields of interest in this coordinate system then read:

θ(R, φ) = c0 (R) + cos(φ)c(R) (A.1)


sR (R, φ) = α0 (R) + cos(φ)α(R) (A.2)
sφ (R, φ) = β0 (R) − sin(φ)β(R) (A.3)
p(R, φ) = d0 (R) + cos(φ)d(R) (A.4)
uR (R, φ) = a0 (R) + cos(φ)a(R) (A.5)
uφ (R, φ) = b0 (R) − sin(φ)b(R) (A.6)
σRR (R, φ) = γ0 (R) + cos(φ)γ(R) (A.7)
σRφ (R, φ) = κ0 (R) + sin(φ)κ(R) (A.8)
σφφ (R, φ) = −(ω0 (R) + cos(φ)ω(R)) (A.9)
Note that the stress tensor is assumed to be symmetric and trace-free such that
σzz = −(σRR + σφφ ). Further details can be found in [68, 69]. The computer algebra
88 A. Convergence Data and Exact Solutions

system Wolfram Mathematica was used to calculate the integration constants for the
given ansatz.
To use the exact fields in a two-dimensional cartesian coordinate system (using
x, y), the following transformations [47] for a vector u and a symmetric 2-tensor σ
(i.e. σRφ = σφR ) are applied:

sx = sR cos(φ) − sφ sin(φ) (A.10)


sy = sR sin(φ) + sφ cos(φ) (A.11)
σxx = σRR cos2 (φ) − (σRφ + σRφ ) cos(φ) sin(φ) + σφφ sin2 (φ) (A.12)
σxy = σRφ cos2 (φ) + (σRR − σφφ ) cos(φ) sin(φ) − σRφ sin2 (φ) (A.13)
σyy = σφφ cos2 (φ) + (σRφ + σRφ ) cos(φ) sin(φ) + σRR sin2 (φ) (A.14)

A.1.1. Decoupled Linearized Heat System


1 #include <pybind11/pybind11.h>
2 #include <pybind11/eigen.h>
3
4 #include <cmath>
5 #include <boost/math/special_functions/bessel.hpp>
6
7 using namespace std;
8
9 namespace py = pybind11;
10
11 #include <dolfin/function/Expression.h>
12
13 double tau = 0.1;
14 double C_1 = -0.40855716127979214;
15 double C_2 = 2.4471587630476663;
16
17 class Temperature : public dolfin::Expression {
18 public:
19 Temperature() : dolfin::Expression() {}
20 void eval(Eigen::Ref<Eigen::VectorXd> values,
21 Eigen::Ref<const Eigen::VectorXd> x) const override {
22
23 double R = sqrt(pow(x[0],2)+pow(x[1],2));
24 double phi = atan2(x[1],x[0]);
25
26 double c_0 = C_2 + (- (20.0*(C_1)*std::log(R)) + ((5.0/4.0)*std::pow(R, 4)) - (2.0*std::pow(R, 2)*(24.0*std::pow(tau, 2) +
5.0)))/(tau*75.0);
27 double c = 0.0;
28
29 double theta = c_0 + cos(phi) * c;
30
31 values[0] = theta;
32 }
33 };
34
35 class Heatflux : public dolfin::Expression {
36 public:
37 Heatflux() : dolfin::Expression(2) {}
38 void eval(Eigen::Ref<Eigen::VectorXd> values,
39 Eigen::Ref<const Eigen::VectorXd> x) const override {
40
41 double R = sqrt(pow(x[0],2)+pow(x[1],2));
42 double phi = atan2(x[1],x[0]);
43
44 double alpha_0 = (C_1)/R + (pow(R,2) - (pow(R,4)/4))/R;
45 double alpha = 0;
46
47 double beta_0 = 0;
48 double beta = 0;
49
50 double s_R = alpha_0 + cos(phi) * alpha;
51 double s_phi = beta_0 - sin(phi) * beta;
52
A.1. Exact Solutions 89

53 // https://ocw.mit.edu/courses/aeronautics-and-astronautics/
54 // ...16-07-dynamics-fall-2009/lecture-notes/MIT16_07F09_Lec05.pdf
55 double s_x = s_R * cos(phi) - s_phi * sin(phi);
56 double s_y = s_R * sin(phi) + s_phi * cos(phi);
57
58 values[0] = s_x ;
59 values[1] = s_y ;
60 }
61 };
62
63
64 PYBIND11_MODULE(SIGNATURE, m) {
65
66 py::class_<Temperature, std::shared_ptr<Temperature>, dolfin::Expression>
67 (m, "Temperature")
68 .def(py::init<>());
69
70 py::class_<Heatflux, std::shared_ptr<Heatflux>, dolfin::Expression>
71 (m, "Heatflux")
72 .def(py::init<>());
73
74 }

Listing A.2 Exact solution: heat/01_coeffs.cpp .

A.1.2. Decoupled Linearized Stress System


1 #include <pybind11/pybind11.h>
2 #include <pybind11/eigen.h>
3
4 #include <cmath>
5 #include <boost/math/special_functions/bessel.hpp>
6
7 using namespace std;
8 //using namespace boost::math;
9
10 namespace py = pybind11;
11
12 #include <dolfin/function/Expression.h>
13
14 double C_0 = 0;
15 double C_1 = 0.6015037593984962;
16 double C_2 = 0;
17 double C_3 = 0;
18 double C_4 = 0;
19 double C_5 = 0;
20 double C_6 = -0.6917293233082705;
21 double C_7 = 0;
22 double C_8 = 0;
23 double C_9 = 0;
24 double C_10 = 0;
25 double C_11 = 0;
26 double C_12 = 0;
27 double C_13 = 0;
28 double C_14 = 0;
29 double C_15 = 0;
30 double kn = 0.1;
31 // double A_1 = 0.4;
32 double A_1 = 0.0;
33
34 double lambda_1 = sqrt(5.0/9.0);
35 double lambda_2 = sqrt(5.0/6.0);
36 double lambda_3 = sqrt(3.0/2.0);
37
38 double BI( int n, double x ) { return boost::math::cyl_bessel_i(n,x); }
39 double BK( int n, double x ) { return boost::math::cyl_bessel_k(n,x); }
40
41 double I_0( double x) { return BI(0,x); }
42 double I_1( double x) { return BI(1,x); }
43 double I_2( double x) { return BI(2,x); }
44 double I_3( double x) { return BI(3,x); }
45
46 double K_0( double x) { return BK(0,x); }
47 double K_1( double x) { return BK(1,x); }
48 double K_2( double x) { return BK(2,x); }
49 double K_3( double x) { return BK(3,x); }
50
51 class Pressure : public dolfin::Expression {
90 A. Convergence Data and Exact Solutions

52 public:
53 Pressure() : dolfin::Expression() {}
54 void eval(Eigen::Ref<Eigen::VectorXd> values,
55 Eigen::Ref<const Eigen::VectorXd> x) const override {
56
57 double R = sqrt(pow(x[0],2)+pow(x[1],2));
58 double phi = atan2(x[1],x[0]);
59
60 double d_0 = C_9 + C_2*K_0( R*lambda_2/kn) + C_8*I_0(R*lambda_2/kn);
61 double d = - (10*A_1 * pow(R,2))/(27*kn) + (4*C_4*R)/(kn) - (2*C_5*kn)/R + C_14*K_1( R*lambda_2/kn) + C_15* I_1(
R*lambda_2/kn);
62
63 values[0] = d_0 + cos(phi) * d;
64 }
65 };
66
67 class Velocity : public dolfin::Expression {
68 public:
69 Velocity() : dolfin::Expression(2) {}
70 void eval(Eigen::Ref<Eigen::VectorXd> values,
71 Eigen::Ref<const Eigen::VectorXd> x) const override {
72
73 double R = sqrt(pow(x[0],2)+pow(x[1],2));
74 double phi = atan2(x[1],x[0]);
75
76 double a_0 = C_7*kn/R;
77 double a = -A_1*R*((2*pow(R,2))/(27*pow(kn,2)) - 2.0/3.0) + C_0 - (C_3*pow(kn,2))/(2*pow(R,2)) + (C_4 *
pow(R,2))/(2*pow(kn,2)) + C_5 * (std::log(R/kn)-1.0/2.0);
78
79 double b_0 = C_1/(2*R*kn) + C_6*R;
80 double b = -A_1*R*((pow(R,2))/(54*pow(kn,2)) - 1.0/3.0) + C_0 + (C_3*pow(kn,2))/(2*pow(R,2)) + (3*C_4 *
pow(R,2))/(2*pow(kn,2)) + C_5 * (std::log(R/kn)+1.0/2.0);
81
82 double u_R = a_0 + cos(phi) * a;
83 double u_phi = b_0 - sin(phi) * b;
84
85 double u_x = u_R * cos(phi) - u_phi * sin(phi);
86 double u_y = u_R * sin(phi) + u_phi * cos(phi);
87
88 values[0] = u_x ;
89 values[1] = u_y ;
90 }
91 };
92
93 class Stress : public dolfin::Expression {
94 public:
95 Stress() : dolfin::Expression(2,2) {}
96 void eval(Eigen::Ref<Eigen::VectorXd> values,
97 Eigen::Ref<const Eigen::VectorXd> x) const override {
98
99 double R = sqrt(pow(x[0],2)+pow(x[1],2));
100 double phi = atan2(x[1],x[0]);
101
102 double gamma_0 =
103 + (2*C_7*pow(kn,2))/(pow(R,2))
104 + (C_10*kn*K_1((R*lambda_3)/kn))/(lambda_3*R)
105 + (C_11*kn*I_1((R*lambda_3)/kn))/(lambda_3*R)
106 + C_2*(
107 + (kn*K_1((R*lambda_2/kn)))/(2*lambda_2*R)
108 - K_2(R*lambda_2/kn)
109 )
110 + C_8*(
111 - (kn*I_1(R*lambda_2/kn))/(2*lambda_2*R)
112 - I_2(R*lambda_2/kn)
113 );
114 double gamma =
115 + (7.*A_1*pow(R,2))/(27.*kn)
116 - (2.*pow(kn,3)*(C_3-(64.*C_5)/15.))/pow(R,3)
117 - (2.*C_4*R)/kn
118 - (2.*C_5*kn)/R
119 + (C_12*kn*I_2(R*lambda_3/kn))/(lambda_3*R)
120 + (C_13*kn*K_2(R*lambda_3/kn))/(lambda_3*R)
121 + C_14*(
122 - K_1(R*lambda_2/kn)
123 - (3.*kn*K_2(R*lambda_2/kn))/(2.*lambda_2*R)
124 )
125 + C_15*(
126 + (3.*kn*I_2(R*lambda_2/kn))/(2.*lambda_2*R)
127 - I_1(R*lambda_2/kn)
128 );
129
130 double kappa_0 =
131 C_1/pow(R,2);
132 double kappa =
133 - (A_1*pow(R,2))/(9*kn)
A.1. Exact Solutions 91

134 - (2.*pow(kn,3)*(C_3-(64.*C_5)/15.))/pow(R,3)
135 + (2.*C_4*R)/kn
136 + (C_12*kn*I_2(R*lambda_3/kn))/(lambda_3*R)
137 + (C_13*kn*K_2(R*lambda_3/kn))/(lambda_3*R)
138 - (3.*C_14*kn*K_2(R*lambda_2/kn))/(2.*lambda_2*R)
139 + (3.*C_15*kn*I_2(R*lambda_2/kn))/(2.*lambda_2*R);
140
141 double omega_0 =
142 (2*C_7*pow(kn,2))/pow(R,2)
143 + C_10*(K_0(R*lambda_3/kn)+(kn*K_1(R*lambda_3/kn)/(lambda_3*R)))
144 + C_11*((kn*I_1(R*lambda_3/kn))/(lambda_3*R)-I_0(R*lambda_3/kn)) + C_2 * (
145 - 1./2.*K_0(R*lambda_2/kn)
146 - (3*kn*K_1(R*lambda_2/kn))/(2*lambda_2*R)
147 )
148 + C_8 * (
149 + (3*kn*I_1(R*lambda_2/kn))/(2*lambda_2*R)
150 - 1./2.*I_0(R*lambda_2/kn)
151 );
152 double omega =
153 (2*A_1*pow(R,2))/(27*kn)
154 - (2*pow(kn,3)*(C_3-(64*C_5)/15.))/(pow(R,3))
155 - (2*C_4*R)/kn
156 - (2*C_5*kn)/R
157 + C_12 * (
158 + (kn*I_2(R*lambda_3/kn))/(lambda_3*R)
159 - I_1(R*lambda_3/kn)
160 )
161 + C_13 * (
162 + K_1(R*lambda_3/kn)
163 + (kn*K_2(R*lambda_3/kn))/(lambda_3*R)
164 )
165 + C_14 * (
166 + (kn*K_2(R*lambda_2/kn))/(2*lambda_2*R)
167 - 1/2.*K_3(R*lambda_2/kn)
168 )
169 + C_15 * (
170 - (kn*I_2(R*lambda_2/kn))/(2*lambda_2*R)
171 - 1/2.*I_3(R*lambda_2/kn)
172 );
173
174 double sigma_RR = gamma_0 + cos(phi) * gamma;
175 double sigma_Rphi = kappa_0 + sin(phi) * kappa;
176 double sigma_phiphi = -(omega_0 + cos(phi) * omega);
177
178 // Heinz Schade p377: Assume symmetry of tensor <=> a_Rphi = a_phiR
179 double sigma_xx =
180 + sigma_RR * pow(cos(phi),2)
181 - (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
182 + sigma_phiphi * pow(sin(phi),2);
183 double sigma_xy =
184 + sigma_Rphi * pow(cos(phi),2)
185 + (sigma_RR - sigma_phiphi) * cos(phi) * sin(phi)
186 - sigma_Rphi * pow(sin(phi),2);
187 double sigma_yy =
188 + sigma_phiphi * pow(cos(phi),2)
189 + (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
190 + sigma_RR * pow(sin(phi),2);
191
192 values[0] = sigma_xx ;
193 values[1] = sigma_xy ;
194 values[2] = sigma_yy ;
195 }
196 };
197
198 PYBIND11_MODULE(SIGNATURE, m) {
199
200 py::class_<Pressure, std::shared_ptr<Pressure>, dolfin::Expression>
201 (m, "Pressure")
202 .def(py::init<>());
203
204 py::class_<Velocity, std::shared_ptr<Velocity>, dolfin::Expression>
205 (m, "Velocity")
206 .def(py::init<>());
207
208 py::class_<Stress, std::shared_ptr<Stress>, dolfin::Expression>
209 (m, "Stress")
210 .def(py::init<>());
211 }

Listing A.3 Exact solution: stress/01_nosource_rot.cpp .

1 #include <pybind11/pybind11.h>
2 #include <pybind11/pybind11.h>
3 #include <pybind11/eigen.h>
92 A. Convergence Data and Exact Solutions

4
5 #include <cmath>
6 #include <boost/math/special_functions/bessel.hpp>
7
8 using namespace std;
9 //using namespace boost::math;
10
11 namespace py = pybind11;
12
13 #include <dolfin/function/Expression.h>
14
15 double C_0 = -50.80230139855979;
16 double C_1 = 0.6015037593984962;
17 double C_2 = 0;
18 double C_3 = -444.7738727200452;
19 double C_4 = -0.12443443849461801;
20 double C_5 = 39.38867688999618;
21 double C_6 = -0.6917293233082705;
22 double C_7 = 0;
23 double C_8 = 0;
24 double C_9 = 0;
25 double C_10 = 0;
26 double C_11 = 0;
27 double C_12 = 2.255312046238658E-11;
28 double C_13 = 407.2248457002586;
29 double C_14 = -104.89346597195336;
30 double C_15 = 4.870715709115059E-7;
31 double kn = 0.1;
32 double A_1 = 0.4;
33 // // double A_1 = 0.0;
34
35 double lambda_1 = sqrt(5.0/9.0);
36 double lambda_2 = sqrt(5.0/6.0);
37 double lambda_3 = sqrt(3.0/2.0);
38
39 double BI( int n, double x ) { return boost::math::cyl_bessel_i(n,x); }
40 double BK( int n, double x ) { return boost::math::cyl_bessel_k(n,x); }
41
42 double I_0( double x) { return BI(0,x); }
43 double I_1( double x) { return BI(1,x); }
44 double I_2( double x) { return BI(2,x); }
45 double I_3( double x) { return BI(3,x); }
46
47 double K_0( double x) { return BK(0,x); }
48 double K_1( double x) { return BK(1,x); }
49 double K_2( double x) { return BK(2,x); }
50 double K_3( double x) { return BK(3,x); }
51
52 class Pressure : public dolfin::Expression {
53 public:
54 Pressure() : dolfin::Expression() {}
55 void eval(Eigen::Ref<Eigen::VectorXd> values,
56 Eigen::Ref<const Eigen::VectorXd> x) const override {
57
58 double R = sqrt(pow(x[0],2)+pow(x[1],2));
59 double phi = atan2(x[1],x[0]);
60
61 double d_0 = C_9 + C_2*K_0( R*lambda_2/kn) + C_8*I_0(R*lambda_2/kn);
62 double d = - (10*A_1 * pow(R,2))/(27*kn) + (4*C_4*R)/(kn) - (2*C_5*kn)/R + C_14*K_1( R*lambda_2/kn) + C_15* I_1(
R*lambda_2/kn);
63
64 values[0] = d_0 + cos(phi) * d;
65 }
66 };
67
68 class Velocity : public dolfin::Expression {
69 public:
70 Velocity() : dolfin::Expression(2) {}
71 void eval(Eigen::Ref<Eigen::VectorXd> values,
72 Eigen::Ref<const Eigen::VectorXd> x) const override {
73
74 double R = sqrt(pow(x[0],2)+pow(x[1],2));
75 double phi = atan2(x[1],x[0]);
76
77 double a_0 = C_7*kn/R;
78 double a = -A_1*R*((2*pow(R,2))/(27*pow(kn,2)) - 2.0/3.0) + C_0 - (C_3*pow(kn,2))/(2*pow(R,2)) + (C_4 *
pow(R,2))/(2*pow(kn,2)) + C_5 * (std::log(R/kn)-1.0/2.0);
79
80 double b_0 = C_1/(2*R*kn) + C_6*R;
81 double b = -A_1*R*((pow(R,2))/(54*pow(kn,2)) - 1.0/3.0) + C_0 + (C_3*pow(kn,2))/(2*pow(R,2)) + (3*C_4 *
pow(R,2))/(2*pow(kn,2)) + C_5 * (std::log(R/kn)+1.0/2.0);
82
83 double u_R = a_0 + cos(phi) * a;
84 double u_phi = b_0 - sin(phi) * b;
85
A.1. Exact Solutions 93

86 double u_x = u_R * cos(phi) - u_phi * sin(phi);


87 double u_y = u_R * sin(phi) + u_phi * cos(phi);
88
89 values[0] = u_x ;
90 values[1] = u_y ;
91 }
92 };
93
94 class Stress : public dolfin::Expression {
95 public:
96 Stress() : dolfin::Expression(2,2) {}
97 void eval(Eigen::Ref<Eigen::VectorXd> values,
98 Eigen::Ref<const Eigen::VectorXd> x) const override {
99
100 double R = sqrt(pow(x[0],2)+pow(x[1],2));
101 double phi = atan2(x[1],x[0]);
102
103 // std::cout << atan2(0.0,1.0);
104
105 double gamma_0 =
106 + (2*C_7*pow(kn,2))/(pow(R,2))
107 + (C_10*kn*K_1((R*lambda_3)/kn))/(lambda_3*R)
108 + (C_11*kn*I_1((R*lambda_3)/kn))/(lambda_3*R)
109 + C_2*(
110 + (kn*K_1((R*lambda_2/kn)))/(2*lambda_2*R)
111 - K_2(R*lambda_2/kn)
112 )
113 + C_8*(
114 - (kn*I_1(R*lambda_2/kn))/(2*lambda_2*R)
115 - I_2(R*lambda_2/kn)
116 );
117 double gamma =
118 + (7.*A_1*pow(R,2))/(27.*kn)
119 - (2.*pow(kn,3)*(C_3-(64.*C_5)/15.))/pow(R,3)
120 - (2.*C_4*R)/kn
121 - (2.*C_5*kn)/R
122 + (C_12*kn*I_2(R*lambda_3/kn))/(lambda_3*R)
123 + (C_13*kn*K_2(R*lambda_3/kn))/(lambda_3*R)
124 + C_14*(
125 - K_1(R*lambda_2/kn)
126 - (3.*kn*K_2(R*lambda_2/kn))/(2.*lambda_2*R)
127 )
128 + C_15*(
129 + (3.*kn*I_2(R*lambda_2/kn))/(2.*lambda_2*R)
130 - I_1(R*lambda_2/kn)
131 );
132
133 double kappa_0 =
134 C_1/pow(R,2);
135 double kappa =
136 - (A_1*pow(R,2))/(9*kn)
137 - (2.*pow(kn,3)*(C_3-(64.*C_5)/15.))/pow(R,3)
138 + (2.*C_4*R)/kn
139 + (C_12*kn*I_2(R*lambda_3/kn))/(lambda_3*R)
140 + (C_13*kn*K_2(R*lambda_3/kn))/(lambda_3*R)
141 - (3.*C_14*kn*K_2(R*lambda_2/kn))/(2.*lambda_2*R)
142 + (3.*C_15*kn*I_2(R*lambda_2/kn))/(2.*lambda_2*R);
143
144 double omega_0 =
145 (2*C_7*pow(kn,2))/pow(R,2)
146 + C_10*(K_0(R*lambda_3/kn)+(kn*K_1(R*lambda_3/kn)/(lambda_3*R)))
147 + C_11*((kn*I_1(R*lambda_3/kn))/(lambda_3*R)-I_0(R*lambda_3/kn)) + C_2 * (
148 - 1./2.*K_0(R*lambda_2/kn)
149 - (3*kn*K_1(R*lambda_2/kn))/(2*lambda_2*R)
150 )
151 + C_8 * (
152 + (3*kn*I_1(R*lambda_2/kn))/(2*lambda_2*R)
153 - 1./2.*I_0(R*lambda_2/kn)
154 );
155 double omega =
156 (2*A_1*pow(R,2))/(27*kn)
157 - (2*pow(kn,3)*(C_3-(64*C_5)/15.))/(pow(R,3))
158 - (2*C_4*R)/kn
159 - (2*C_5*kn)/R
160 + C_12 * (
161 + (kn*I_2(R*lambda_3/kn))/(lambda_3*R)
162 - I_1(R*lambda_3/kn)
163 )
164 + C_13 * (
165 + K_1(R*lambda_3/kn)
166 + (kn*K_2(R*lambda_3/kn))/(lambda_3*R)
167 )
168 + C_14 * (
169 + (kn*K_2(R*lambda_2/kn))/(2*lambda_2*R)
170 - 1/2.*K_3(R*lambda_2/kn)
94 A. Convergence Data and Exact Solutions

171 )
172 + C_15 * (
173 - (kn*I_2(R*lambda_2/kn))/(2*lambda_2*R)
174 - 1/2.*I_3(R*lambda_2/kn)
175 );
176
177 double sigma_RR = gamma_0 + cos(phi) * gamma;
178 double sigma_Rphi = kappa_0 + sin(phi) * kappa;
179 double sigma_phiphi = -(omega_0 + cos(phi) * omega);
180
181 // Heinz Schade p377: Assume symmetry of tensor <=> a_Rphi = a_phiR
182 double sigma_xx =
183 + sigma_RR * pow(cos(phi),2)
184 - (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
185 + sigma_phiphi * pow(sin(phi),2);
186 double sigma_xy =
187 + sigma_Rphi * pow(cos(phi),2)
188 + (sigma_RR - sigma_phiphi) * cos(phi) * sin(phi)
189 - sigma_Rphi * pow(sin(phi),2);
190 double sigma_yy =
191 + sigma_phiphi * pow(cos(phi),2)
192 + (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
193 + sigma_RR * pow(sin(phi),2);
194
195 values[0] = sigma_xx ;
196 values[1] = sigma_xy ;
197 values[2] = sigma_yy ;
198 }
199 };
200
201 PYBIND11_MODULE(SIGNATURE, m) {
202
203 py::class_<Pressure, std::shared_ptr<Pressure>, dolfin::Expression>
204 (m, "Pressure")
205 .def(py::init<>());
206
207 py::class_<Velocity, std::shared_ptr<Velocity>, dolfin::Expression>
208 (m, "Velocity")
209 .def(py::init<>());
210
211 py::class_<Stress, std::shared_ptr<Stress>, dolfin::Expression>
212 (m, "Stress")
213 .def(py::init<>());
214 }

Listing A.4 Exact solution: stress/01_source_rot.cpp .

A.1.3. Full Linearized R13 System


1 #include <pybind11/pybind11.h>
2 #include <pybind11/eigen.h>
3
4 #include <cmath>
5 #include <boost/math/special_functions/bessel.hpp>
6
7 using namespace std;
8
9 namespace py = pybind11;
10
11 #include <dolfin/function/Expression.h>
12
13 double kn = 1.0;
14 double A_1 = 1.0;
15
16 // Constants
17 double C_0 = -1.004419665507595;
18 double C_1 = 0.4448013383516245;
19 double C_2 = 0.2112530227632378;
20 double C_3 = -0.4315096542509863;
21 double C_4 = 0.08819320821751526;
22 double C_5 = -0.06836832840389916;
23 double C_6 = 0.09335102518735922;
24 double C_7 = 0;
25 double C_8 = 0.5183777145707696;
26 double C_9 = -0.0026285115884281396; // FIXME: Check scaling
27 double C_10 = -0.06963847872220634;
28 double C_11 = 0.01839832993369312;
29 double C_12 = -0.03561221007805056;
A.1. Exact Solutions 95

30 double C_13 = 0.2707355692561467;


31 double C_14 = 0.04291262895440975;
32 double C_15 = -0.02500281007035313;
33 double C_16 = 0.03610593775576753;
34 double C_17 = -0.006316859711714699;
35 double C_18 = -0.01923465390597724;
36 double C_19 = -0.001999331311951152;
37 double C_20 = -0.09987535948000502;
38 double C_21 = -0.002620826571728536;
39 double C_22 = -0.01585859733640677;
40 double C_23 = 0.8086861581439194;
41
42 double lambda_1 = sqrt(5.0/9.0);
43 double lambda_2 = sqrt(5.0/6.0);
44 double lambda_3 = sqrt(3.0/2.0);
45
46 double BI( int n, double x ) { return boost::math::cyl_bessel_i(n,x); }
47 double BK( int n, double x ) { return boost::math::cyl_bessel_k(n,x); }
48
49 double I_0( double x) { return BI(0,x); }
50 double I_1( double x) { return BI(1,x); }
51 double I_2( double x) { return BI(2,x); }
52 double I_3( double x) { return BI(3,x); }
53
54 double K_0( double x) { return BK(0,x); }
55 double K_1( double x) { return BK(1,x); }
56 double K_2( double x) { return BK(2,x); }
57 double K_3( double x) { return BK(3,x); }
58
59 class Temperature : public dolfin::Expression {
60 public:
61 Temperature() : dolfin::Expression() {}
62 void eval(Eigen::Ref<Eigen::VectorXd> values,
63 Eigen::Ref<const Eigen::VectorXd> x) const override {
64
65 double R = sqrt(pow(x[0],2)+pow(x[1],2));
66 double phi = atan2(x[1],x[0]);
67
68 double c_0 = C_8 + (2*C_17*I_0((R*lambda_2)/kn))/5. + (2*C_16*K_0((R*lambda_2)/kn))/5. - (4*C_6*std::log(R/kn))/15.;
69 double c = (-4*C_2*R)/(15.*kn) + (8*C_4*R)/(5.*kn) - (4*A_1*std::pow(R,2))/(27.*kn) - (2*C_1*kn)/(15.*R) - (4*C_5*kn)/(5.*R)
+ (2*C_23*I_1((R*lambda_2)/kn))/5. + (2*C_22*K_1((R*lambda_2)/kn))/5.;
70
71 double theta = c_0 + cos(phi) * c;
72
73 values[0] = theta;
74 }
75 };
76
77 class Heatflux : public dolfin::Expression {
78 public:
79 Heatflux() : dolfin::Expression(2) {}
80 void eval(Eigen::Ref<Eigen::VectorXd> values,
81 Eigen::Ref<const Eigen::VectorXd> x) const override {
82
83 double R = sqrt(pow(x[0],2)+pow(x[1],2));
84 double phi = atan2(x[1],x[0]);
85
86 double alpha_0 = (C_6*kn)/R;
87 double alpha = C_2 - (C_1*std::pow(kn,2))/(2.*std::pow(R,2)) - (5*((2*C_14*kn*I_1((R*lambda_1)/kn))/(R*lambda_1) +
(2*C_15*kn*K_1((R*lambda_1)/kn))/(R*lambda_1)))/2.;
88
89 double beta_0 = C_11*I_1((R*lambda_1)/kn) + C_12*K_1((R*lambda_1)/kn);
90 double beta = C_2 + (C_1*std::pow(kn,2))/(2.*std::pow(R,2)) - (5*(C_14*(I_0((R*lambda_1)/kn) + I_2((R*lambda_1)/kn)) -
C_15*(K_0((R*lambda_1)/kn) + K_2((R*lambda_1)/kn))))/2.;
91
92 double s_R = alpha_0 + cos(phi) * alpha;
93 double s_phi = beta_0 - sin(phi) * beta;
94
95 double s_x = s_R * cos(phi) - s_phi * sin(phi);
96 double s_y = s_R * sin(phi) + s_phi * cos(phi);
97
98 values[0] = s_x ;
99 values[1] = s_y ;
100 }
101 };
102
103 class Pressure : public dolfin::Expression {
104 public:
105 Pressure() : dolfin::Expression() {}
106 void eval(Eigen::Ref<Eigen::VectorXd> values,
107 Eigen::Ref<const Eigen::VectorXd> x) const override {
108
109 double R = sqrt(pow(x[0],2)+pow(x[1],2));
110 double phi = atan2(x[1],x[0]);
111
96 A. Convergence Data and Exact Solutions

112 double d_0 = C_9 + C_17*I_0((R*lambda_2)/kn) + C_16*K_0((R*lambda_2)/kn);


113 double d = (4*C_4*R)/kn - (10*A_1*std::pow(R,2))/(27.*kn) - (2*C_5*kn)/R + C_23*I_1((R*lambda_2)/kn) +
C_22*K_1((R*lambda_2)/kn);
114
115 values[0] = d_0 + cos(phi) * d;
116 }
117 };
118
119 class Velocity : public dolfin::Expression {
120 public:
121 Velocity() : dolfin::Expression(2) {}
122 void eval(Eigen::Ref<Eigen::VectorXd> values,
123 Eigen::Ref<const Eigen::VectorXd> x) const override {
124
125 double R = sqrt(pow(x[0],2)+pow(x[1],2));
126 double phi = atan2(x[1],x[0]);
127
128 double a_0 = (C_7*kn)/R;
129 double a = C_0 - A_1*R*(-2/3. + (2*std::pow(R,2))/(27.*std::pow(kn,2))) + (C_4*std::pow(R,2))/(2.*std::pow(kn,2)) -
(C_3*std::pow(kn,2))/(2.*std::pow(R,2)) + (2*C_14*kn*I_1((R*lambda_1)/kn))/(R*lambda_1) +
(2*C_15*kn*K_1((R*lambda_1)/kn))/(R*lambda_1) + C_5*(-1/2. + std::log(R/kn));
130
131 double b_0 = C_10*R + C_13/(2.*R*kn) + (C_11*R)/(3.*std::sqrt(5)*kn) + (-6*C_11*I_1((R*lambda_1)/kn) -
6*C_12*K_1((R*lambda_1)/kn))/15.;
132 double b = C_0 - A_1*R*(-1/3. + std::pow(R,2)/(54.*std::pow(kn,2))) + (3*C_4*std::pow(R,2))/(2.*std::pow(kn,2)) +
(C_3*std::pow(kn,2))/(2.*std::pow(R,2)) + C_14*(I_0((R*lambda_1)/kn) + I_2((R*lambda_1)/kn)) -
C_15*(K_0((R*lambda_1)/kn) + K_2((R*lambda_1)/kn)) + C_5*(1/2. + std::log(R/kn));
133
134 double u_R = a_0 + cos(phi) * a;
135 double u_phi = b_0 - sin(phi) * b;
136
137 double u_x = u_R * cos(phi) - u_phi * sin(phi);
138 double u_y = u_R * sin(phi) + u_phi * cos(phi);
139
140 values[0] = u_x ;
141 values[1] = u_y ;
142 }
143 };
144
145 class Stress : public dolfin::Expression {
146 public:
147 Stress() : dolfin::Expression(2,2) {}
148 void eval(Eigen::Ref<Eigen::VectorXd> values,
149 Eigen::Ref<const Eigen::VectorXd> x) const override {
150
151 double R = sqrt(pow(x[0],2)+pow(x[1],2));
152 double phi = atan2(x[1],x[0]);
153
154 double gamma_0 = (4*C_6*std::pow(kn,2))/(5.*std::pow(R,2)) + (2*C_7*std::pow(kn,2))/std::pow(R,2) +
(C_19*kn*I_1((R*lambda_3)/kn))/(R*lambda_3) + C_17*(-(kn*I_1((R*lambda_2)/kn))/(2.*R*lambda_2) - I_2((R*lambda_2)/kn))
+ (C_18*kn*K_1((R*lambda_3)/kn))/(R*lambda_3) + C_16*((kn*K_1((R*lambda_2)/kn))/(2.*R*lambda_2) - K_2((R*lambda_2)/kn));
155 double gamma = (-2*C_4*R)/kn + (7*A_1*std::pow(R,2))/(27.*kn) - (2*C_5*kn)/R - (4*C_1*std::pow(kn,3))/(5.*std::pow(R,3)) -
(2*(C_3 - (64*C_5)/15.)*std::pow(kn,3))/std::pow(R,3) + C_23*(-I_1((R*lambda_2)/kn) +
(3*kn*I_2((R*lambda_2)/kn))/(2.*R*lambda_2)) + (C_20*kn*I_2((R*lambda_3)/kn))/(R*lambda_3) +
C_22*(-K_1((R*lambda_2)/kn) - (3*kn*K_2((R*lambda_2)/kn))/(2.*R*lambda_2)) +
(C_21*kn*K_2((R*lambda_3)/kn))/(R*lambda_3);
156
157 double omega_0 = (4*C_6*std::pow(kn,2))/(5.*std::pow(R,2)) + (2*C_7*std::pow(kn,2))/std::pow(R,2) +
C_17*(-I_0((R*lambda_2)/kn)/2. + (3*kn*I_1((R*lambda_2)/kn))/(2.*R*lambda_2)) + C_19*(-I_0((R*lambda_3)/kn) +
(kn*I_1((R*lambda_3)/kn))/(R*lambda_3)) + C_16*(-K_0((R*lambda_2)/kn)/2. - (3*kn*K_1((R*lambda_2)/kn))/(2.*R*lambda_2))
+ C_18*(K_0((R*lambda_3)/kn) + (kn*K_1((R*lambda_3)/kn))/(R*lambda_3));
158 double omega = (-2*C_4*R)/kn + (2*A_1*std::pow(R,2))/(27.*kn) - (2*C_5*kn)/R - (4*C_1*std::pow(kn,3))/(5.*std::pow(R,3)) -
(2*(C_3 - (64*C_5)/15.)*std::pow(kn,3))/std::pow(R,3) + C_20*(-I_1((R*lambda_3)/kn) +
(kn*I_2((R*lambda_3)/kn))/(R*lambda_3)) + C_23*(-(kn*I_2((R*lambda_2)/kn))/(2.*R*lambda_2) - I_3((R*lambda_2)/kn)/2.) +
C_21*(K_1((R*lambda_3)/kn) + (kn*K_2((R*lambda_3)/kn))/(R*lambda_3)) + C_22*((kn*K_2((R*lambda_2)/kn))/(2.*R*lambda_2)
- K_3((R*lambda_2)/kn)/2.);
159
160 double kappa_0 = C_13/std::pow(R,2);
161 double kappa = (2*C_4*R)/kn - (A_1*std::pow(R,2))/(9.*kn) - (4*C_1*std::pow(kn,3))/(5.*std::pow(R,3)) - (2*(C_3 -
(64*C_5)/15.)*std::pow(kn,3))/std::pow(R,3) + (3*C_23*kn*I_2((R*lambda_2)/kn))/(2.*R*lambda_2) +
(C_20*kn*I_2((R*lambda_3)/kn))/(R*lambda_3) - (3*C_22*kn*K_2((R*lambda_2)/kn))/(2.*R*lambda_2) +
(C_21*kn*K_2((R*lambda_3)/kn))/(R*lambda_3);
162
163 double sigma_RR = gamma_0 + cos(phi) * gamma;
164 double sigma_Rphi = kappa_0 + sin(phi) * kappa;
165 double sigma_phiphi = -(omega_0 + cos(phi) * omega);
166
167 // Heinz Schade p377: Assume symmetry of tensor <=> a_Rphi = a_phiR
168 double sigma_xx =
169 + sigma_RR * pow(cos(phi),2)
170 - (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
171 + sigma_phiphi * pow(sin(phi),2);
172 double sigma_xy =
173 + sigma_Rphi * pow(cos(phi),2)
174 + (sigma_RR - sigma_phiphi) * cos(phi) * sin(phi)
A.1. Exact Solutions 97

175 - sigma_Rphi * pow(sin(phi),2);


176 double sigma_yy =
177 + sigma_phiphi * pow(cos(phi),2)
178 + (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
179 + sigma_RR * pow(sin(phi),2);
180
181 values[0] = sigma_xx ;
182 values[1] = sigma_xy ;
183 values[2] = sigma_yy ;
184 values[3] = sigma_xy ; // no difference because symmetry
185 }
186 };
187
188 PYBIND11_MODULE(SIGNATURE, m) {
189
190 py::class_<Temperature, std::shared_ptr<Temperature>, dolfin::Expression>
191 (m, "Temperature")
192 .def(py::init<>());
193
194 py::class_<Heatflux, std::shared_ptr<Heatflux>, dolfin::Expression>
195 (m, "Heatflux")
196 .def(py::init<>());
197
198 py::class_<Pressure, std::shared_ptr<Pressure>, dolfin::Expression>
199 (m, "Pressure")
200 .def(py::init<>());
201
202 py::class_<Velocity, std::shared_ptr<Velocity>, dolfin::Expression>
203 (m, "Velocity")
204 .def(py::init<>());
205
206 py::class_<Stress, std::shared_ptr<Stress>, dolfin::Expression>
207 (m, "Stress")
208 .def(py::init<>());
209
210 }

Listing A.5 Exact solution: r13/1_coeffs_sources_rot_noinflow.cpp .

1 #include <pybind11/pybind11.h>
2 #include <pybind11/eigen.h>
3
4 #include <cmath>
5 #include <boost/math/special_functions/bessel.hpp>
6
7 using namespace std;
8
9 namespace py = pybind11;
10
11 #include <dolfin/function/Expression.h>
12
13 double kn = 1.0;
14
15 // // Constants eps=10^-5
16 // double C_0 = 1.088418574133493;
17 // double C_1 = -0.4708784837854026;
18 // double C_2 = -0.1060780078877844;
19 // double C_3 = 0.6397363738946663;
20 // double C_4 = -0.04268373030990083;
21 // double C_5 = 0.1027291801998076;
22 // double C_6 = -0.1867020667903523;
23 // double C_7 = 2.864923360570221e-7;
24 // double C_8 = 1.963244537638472;
25 // double C_9 = 0.007704644954804194;
26 // double CK_1 = -0.01841555365364274;
27 // double CK_2 = 0.02647436311982035;
28 // double CK_3 = -0.0722116290730638;
29 // double CK_4 = 0.01263372155827893;
30 // double CK_5 = 0.03846910453592139;
31 // double CK_6 = 0.003998659067908465;
32 // double CK_7 = 0.05469703377538136;
33 // double CK_8 = 0.04448263813229264;
34 // double CK_9 = -0.001607567940383826;
35 // double CK_10 = 0.1781491089766695;
36
37 // // Constants eps=10^-4
38 // double C_0 = 1.088458465830652;
39 // double C_1 = -0.4708795265589713;
40 // double C_2 = -0.1060844454754048;
41 // double C_3 = 0.6397268622237638;
42 // double C_4 = -0.0426888328939176;
43 // double C_5 = 0.1027285697835575;
44 // double C_6 = -0.1867022145254646;
45 // double C_7 = 2.864825757859002e-6;
98 A. Convergence Data and Exact Solutions

46 // double C_8 = 1.963244238669897;


47 // double C_9 = 0.007703775736883896;
48 // double CK_1 = -0.01841695056397639;
49 // double CK_2 = 0.02647443224729624;
50 // double CK_3 = -0.07220941121077909;
51 // double CK_4 = 0.01263374077119675;
52 // double CK_5 = 0.03846727512087732;
53 // double CK_6 = 0.003998627065175332;
54 // double CK_7 = 0.0547022288973836;
55 // double CK_8 = 0.04447888927384885;
56 // double CK_9 = -0.001604798505932295;
57 // double CK_10 = 0.178170030606713;
58
59 // // Constants eps=10^-3
60 double C_0 = 1.088856712167725;
61 double C_1 = -0.4708896102041703;
62 double C_2 = -0.1061486462553457;
63 double C_3 = 0.6396313746480878;
64 double C_4 = -0.04273974910778836;
65 double C_5 = 0.102722409367338;
66 double C_6 = -0.1867036913227729;
67 double C_7 = 0.00002863849458707334;
68 double C_8 = 1.963241250104882;
69 double C_9 = 0.007695099574563038;
70 double CK_1 = -0.01843088435918791;
71 double CK_2 = 0.02647510412125701;
72 double CK_3 = -0.07218724090202373;
73 double CK_4 = 0.01263393282835227;
74 double CK_5 = 0.03844898782835787;
75 double CK_6 = 0.003998307157812554;
76 double CK_7 = 0.05475405925519971;
77 double CK_8 = 0.04444138977514516;
78 double CK_9 = -0.001577106876225382;
79 double CK_10 = 0.178378791898016;
80
81 // // Constants eps=10^-2
82 // double C_0 = 1.09277278324226;
83 // double C_1 = -0.4709565408029582;
84 // double C_2 = -0.1067733521869535;
85 // double C_3 = 0.6386400415463009;
86 // double C_4 = -0.04323806992636355;
87 // double C_5 = 0.1026552684836559;
88 // double C_6 = -0.1867184037607001;
89 // double C_7 = 0.0002854059606868768;
90 // double C_8 = 1.963211476840119;
91 // double C_9 = 0.007609935703161957;
92 // double CK_1 = -0.0185667325855968;
93 // double CK_2 = 0.02647991106816222;
94 // double CK_3 = -0.07196637153183492;
95 // double CK_4 = 0.0126358461775725;
96 // double CK_5 = 0.03826680259919404;
97 // double CK_6 = 0.003995120114343059;
98 // double CK_7 = 0.05526041387534725;
99 // double CK_8 = 0.04406535024733006;
100 // double CK_9 = -0.001300480668267124;
101 // double CK_10 = 0.180421409499547;
102
103 // // Constants eps=10^-1
104 // double C_0 = 1.125921604594353;
105 // double C_1 = -0.4686941217654443;
106 // double C_2 = -0.1114820681020402;
107 // double C_3 = 0.6256505450040041;
108 // double C_4 = -0.04724969749859539;
109 // double C_5 = 0.1015107112011764;
110 // double C_6 = -0.1868598401676765;
111 // double C_7 = 0.002753811809744579;
112 // double C_8 = 1.962925254827631;
113 // double C_9 = 0.006913904797024802;
114 // double CK_1 = -0.01961423092232162;
115 // double CK_2 = 0.0263626002451694;
116 // double CK_3 = -0.06984306808715408;
117 // double CK_4 = 0.01265423995026528;
118 // double CK_5 = 0.03651538489775871;
119 // double CK_6 = 0.003964481821802226;
120 // double CK_7 = 0.05925618682225351;
121 // double CK_8 = 0.04024172550289172;
122 // double CK_9 = 0.001420920976733996;
123 // double CK_10 = 0.1968170158183649;
124
125 double lambda_1 = sqrt(5.0/9.0);
126 double lambda_2 = sqrt(5.0/6.0);
127 double lambda_3 = sqrt(3.0/2.0);
128
129 double BI( int n, double x ) { return boost::math::cyl_bessel_i(n,x); }
130 double BK( int n, double x ) { return boost::math::cyl_bessel_k(n,x); }
A.1. Exact Solutions 99

131
132 double I_0( double x) { return BI(0,x); }
133 double I_1( double x) { return BI(1,x); }
134 double I_2( double x) { return BI(2,x); }
135 double I_3( double x) { return BI(3,x); }
136
137 double K_0( double x) { return BK(0,x); }
138 double K_1( double x) { return BK(1,x); }
139 double K_2( double x) { return BK(2,x); }
140 double K_3( double x) { return BK(3,x); }
141
142 class Temperature : public dolfin::Expression {
143 public:
144 Temperature() : dolfin::Expression() {}
145 void eval(Eigen::Ref<Eigen::VectorXd> values,
146 Eigen::Ref<const Eigen::VectorXd> x) const override {
147
148 double R = sqrt(pow(x[0],2)+pow(x[1],2));
149 double phi = atan2(x[1],x[0]);
150
151 double c_0 = C_8 + (2*CK_4*I_0((R*lambda_2)/kn))/5. + (2*CK_3*K_0((R*lambda_2)/kn))/5. - (4*C_6*std::log(R/kn))/15.;
152 double c = (-4*C_2*R)/(15.*kn) + (8*C_4*R)/(5.*kn) - (2*C_1*kn)/(15.*R) - (4*C_5*kn)/(5.*R) +
(2*CK_10*I_1((R*lambda_2)/kn))/5. + (2*CK_9*K_1((R*lambda_2)/kn))/5.;
153
154 double theta = c_0 + cos(phi) * c;
155
156 values[0] = theta;
157 }
158 };
159
160 class Heatflux : public dolfin::Expression {
161 public:
162 Heatflux() : dolfin::Expression(2) {}
163 void eval(Eigen::Ref<Eigen::VectorXd> values,
164 Eigen::Ref<const Eigen::VectorXd> x) const override {
165
166 double R = sqrt(pow(x[0],2)+pow(x[1],2));
167 double phi = atan2(x[1],x[0]);
168
169 double alpha_0 = (C_6*kn)/R;
170 double alpha = C_2 - (C_1*std::pow(kn,2))/(2.*std::pow(R,2)) - (5*((2*CK_1*kn*I_1((R*lambda_1)/kn))/(R*lambda_1) +
(2*CK_2*kn*K_1((R*lambda_1)/kn))/(R*lambda_1)))/2.;
171
172 double beta_0 = 0;
173 double beta = C_2 + (C_1*std::pow(kn,2))/(2.*std::pow(R,2)) - (5*(CK_1*(I_0((R*lambda_1)/kn) + I_2((R*lambda_1)/kn)) -
CK_2*(K_0((R*lambda_1)/kn) + K_2((R*lambda_1)/kn))))/2.;
174
175 double s_R = alpha_0 + cos(phi) * alpha;
176 double s_phi = beta_0 - sin(phi) * beta;
177
178 double s_x = s_R * cos(phi) - s_phi * sin(phi);
179 double s_y = s_R * sin(phi) + s_phi * cos(phi);
180
181 values[0] = s_x ;
182 values[1] = s_y ;
183 }
184 };
185
186 class Pressure : public dolfin::Expression {
187 public:
188 Pressure() : dolfin::Expression() {}
189 void eval(Eigen::Ref<Eigen::VectorXd> values,
190 Eigen::Ref<const Eigen::VectorXd> x) const override {
191
192 double R = sqrt(pow(x[0],2)+pow(x[1],2));
193 double phi = atan2(x[1],x[0]);
194
195 double d_0 = C_9 + CK_4*I_0((R*lambda_2)/kn) + CK_3*K_0((R*lambda_2)/kn);
196 double d = (4*C_4*R)/kn - (2*C_5*kn)/R + CK_10*I_1((R*lambda_2)/kn) + CK_9*K_1((R*lambda_2)/kn);
197
198 double p = d_0 + cos(phi) * d;
199
200 values[0] = p;
201 }
202 };
203
204 class Velocity : public dolfin::Expression {
205 public:
206 Velocity() : dolfin::Expression(2) {}
207 void eval(Eigen::Ref<Eigen::VectorXd> values,
208 Eigen::Ref<const Eigen::VectorXd> x) const override {
209
210 double R = sqrt(pow(x[0],2)+pow(x[1],2));
211 double phi = atan2(x[1],x[0]);
212
100 A. Convergence Data and Exact Solutions

213 double a_0 = (C_7*kn)/R;


214 double a = C_0 + (C_4*std::pow(R,2))/(2.*std::pow(kn,2)) - (C_3*std::pow(kn,2))/(2.*std::pow(R,2)) +
(2*CK_1*kn*I_1((R*lambda_1)/kn))/(R*lambda_1) + (2*CK_2*kn*K_1((R*lambda_1)/kn))/(R*lambda_1) + C_5*(-0.5 +
std::log(R/kn));
215
216 double b_0 = 0;
217 double b = C_0 + (3*C_4*std::pow(R,2))/(2.*std::pow(kn,2)) + (C_3*std::pow(kn,2))/(2.*std::pow(R,2)) +
CK_1*(I_0((R*lambda_1)/kn) + I_2((R*lambda_1)/kn)) - CK_2*(K_0((R*lambda_1)/kn) + K_2((R*lambda_1)/kn)) + C_5*(0.5 +
std::log(R/kn));
218
219 double u_R = a_0 + cos(phi) * a;
220 double u_phi = b_0 - sin(phi) * b;
221
222 double u_x = u_R * cos(phi) - u_phi * sin(phi);
223 double u_y = u_R * sin(phi) + u_phi * cos(phi);
224
225 values[0] = u_x ;
226 values[1] = u_y ;
227 }
228 };
229
230 class Stress : public dolfin::Expression {
231 public:
232 Stress() : dolfin::Expression(2,2) {}
233 void eval(Eigen::Ref<Eigen::VectorXd> values,
234 Eigen::Ref<const Eigen::VectorXd> x) const override {
235
236 double R = sqrt(pow(x[0],2)+pow(x[1],2));
237 double phi = atan2(x[1],x[0]);
238
239 double gamma_0 = (4*C_6*std::pow(kn,2))/(5.*std::pow(R,2)) + (2*C_7*std::pow(kn,2))/std::pow(R,2) +
(CK_6*kn*I_1((R*lambda_3)/kn))/(R*lambda_3) + CK_4*(-(kn*I_1((R*lambda_2)/kn))/(2.*R*lambda_2) - I_2((R*lambda_2)/kn))
+ (CK_5*kn*K_1((R*lambda_3)/kn))/(R*lambda_3) + CK_3*((kn*K_1((R*lambda_2)/kn))/(2.*R*lambda_2) - K_2((R*lambda_2)/kn));
240 double gamma = (-2*C_4*R)/kn - (2*C_5*kn)/R - (4*C_1*std::pow(kn,3))/(5.*std::pow(R,3)) - (2*(C_3 -
(64*C_5)/15.)*std::pow(kn,3))/std::pow(R,3) + CK_10*(-I_1((R*lambda_2)/kn) +
(3*kn*I_2((R*lambda_2)/kn))/(2.*R*lambda_2)) + (CK_7*kn*I_2((R*lambda_3)/kn))/(R*lambda_3) +
CK_9*(-K_1((R*lambda_2)/kn) - (3*kn*K_2((R*lambda_2)/kn))/(2.*R*lambda_2)) +
(CK_8*kn*K_2((R*lambda_3)/kn))/(R*lambda_3);
241
242 double omega_0 = (4*C_6*std::pow(kn,2))/(5.*std::pow(R,2)) + (2*C_7*std::pow(kn,2))/std::pow(R,2) +
CK_4*(-I_0((R*lambda_2)/kn)/2. + (3*kn*I_1((R*lambda_2)/kn))/(2.*R*lambda_2)) + CK_6*(-I_0((R*lambda_3)/kn) +
(kn*I_1((R*lambda_3)/kn))/(R*lambda_3)) + CK_3*(-K_0((R*lambda_2)/kn)/2. - (3*kn*K_1((R*lambda_2)/kn))/(2.*R*lambda_2))
+ CK_5*(K_0((R*lambda_3)/kn) + (kn*K_1((R*lambda_3)/kn))/(R*lambda_3));
243 double omega = (-2*C_4*R)/kn - (2*C_5*kn)/R - (4*C_1*std::pow(kn,3))/(5.*std::pow(R,3)) - (2*(C_3 -
(64*C_5)/15.)*std::pow(kn,3))/std::pow(R,3) + CK_7*(-I_1((R*lambda_3)/kn) + (kn*I_2((R*lambda_3)/kn))/(R*lambda_3)) +
CK_10*(-(kn*I_2((R*lambda_2)/kn))/(2.*R*lambda_2) - I_3((R*lambda_2)/kn)/2.) + CK_8*(K_1((R*lambda_3)/kn) +
(kn*K_2((R*lambda_3)/kn))/(R*lambda_3)) + CK_9*((kn*K_2((R*lambda_2)/kn))/(2.*R*lambda_2) - K_3((R*lambda_2)/kn)/2.);
244
245 double kappa_0 = 0;
246 double kappa = (2*C_4*R)/kn - (4*C_1*std::pow(kn,3))/(5.*std::pow(R,3)) - (2*(C_3 -
(64*C_5)/15.)*std::pow(kn,3))/std::pow(R,3) + (3*CK_10*kn*I_2((R*lambda_2)/kn))/(2.*R*lambda_2) +
(CK_7*kn*I_2((R*lambda_3)/kn))/(R*lambda_3) - (3*CK_9*kn*K_2((R*lambda_2)/kn))/(2.*R*lambda_2) +
(CK_8*kn*K_2((R*lambda_3)/kn))/(R*lambda_3);
247
248 double sigma_RR = gamma_0 + cos(phi) * gamma;
249 double sigma_Rphi = kappa_0 + sin(phi) * kappa;
250 double sigma_phiphi = -(omega_0 + cos(phi) * omega);
251
252 // Heinz Schade p377: Assume symmetry of tensor <=> a_Rphi = a_phiR
253 double sigma_xx =
254 + sigma_RR * pow(cos(phi),2)
255 - (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
256 + sigma_phiphi * pow(sin(phi),2);
257 double sigma_xy =
258 + sigma_Rphi * pow(cos(phi),2)
259 + (sigma_RR - sigma_phiphi) * cos(phi) * sin(phi)
260 - sigma_Rphi * pow(sin(phi),2);
261 double sigma_yy =
262 + sigma_phiphi * pow(cos(phi),2)
263 + (sigma_Rphi + sigma_Rphi) * cos(phi) * sin(phi)
264 + sigma_RR * pow(sin(phi),2);
265
266 values[0] = sigma_xx ;
267 values[1] = sigma_xy ;
268 values[2] = sigma_yy ;
269 }
270 };
271
272 PYBIND11_MODULE(SIGNATURE, m) {
273
274 py::class_<Temperature, std::shared_ptr<Temperature>, dolfin::Expression>
275 (m, "Temperature")
276 .def(py::init<>());
277
278 py::class_<Heatflux, std::shared_ptr<Heatflux>, dolfin::Expression>
A.2. Convergence Errors 101

279 (m, "Heatflux")


280 .def(py::init<>());
281
282 py::class_<Pressure, std::shared_ptr<Pressure>, dolfin::Expression>
283 (m, "Pressure")
284 .def(py::init<>());
285
286 py::class_<Velocity, std::shared_ptr<Velocity>, dolfin::Expression>
287 (m, "Velocity")
288 .def(py::init<>());
289
290 py::class_<Stress, std::shared_ptr<Stress>, dolfin::Expression>
291 (m, "Stress")
292 .def(py::init<>());
293
294 }

Listing A.6 Exact solution: r13/1_coeffs_nosources_norot_inflow.cpp .

A.2. Convergence Errors


In the following section, the convergence errors used to create all convergence plots
are listed.

hmax θ sx sy
9.89−1 2.00−1 2.21−1 2.23−1
6.34−1 1.29−1 1.05−1 1.06−1
3.29−1 3.69−2 2.15−2 2.12−2
1.68−1 1.05−2 5.71−3 5.70−3
8.73−2 2.60−3 1.39−3 1.39−3
4.71−2 6.72−4 3.62−4 3.62−4
2.33−2 1.73−4 9.22−5 9.22−5

Table A.1. DLHS: L2 -errors for unstabilized P2 P1 elements.

hmax θ sx sy
9.89−1 1.21−1 2.90−1 2.26−1
6.34−1 7.85−2 1.43−1 1.50−1
3.29−1 2.84−2 2.89−2 3.70−2
1.68−1 8.40−3 7.99−3 6.25−3
8.73−2 2.67−3 1.59−3 1.64−3
4.71−2 6.45−4 3.98−4 3.90−4
2.33−2 1.83−4 9.58−5 9.51−5

Table A.2. DLHS: l∞ -errors for unstabilized P2 P1 elements.


102 A. Convergence Data and Exact Solutions

hmax θ sx sy
9.89−1 1.85−1 3.82−1 5.19−1
6.34−1 2.00−1 3.53−1 3.48−1
3.29−1 7.36−2 1.45−1 1.44−1
1.68−1 1.81−2 2.81−2 2.75−2
8.73−2 4.24−3 5.66−3 5.67−3
4.71−2 1.16−3 1.38−3 1.38−3
2.33−2 3.30−4 3.48−4 3.49−4
1.20−2 9.61−5 8.67−5 8.67−5

Table A.3. DLHS: L2 -errors for stabilized P1 P1 elements.

hmax θ sx sy
9.89−1 1.15−1 3.06−1 2.89−1
6.34−1 1.89−1 3.01−1 3.08−1
3.29−1 9.47−2 1.36−1 1.52−1
1.68−1 3.82−2 4.22−2 4.32−2
8.73−2 1.15−2 8.34−3 7.99−3
4.71−2 4.79−3 1.85−3 1.80−3
2.33−2 2.02−3 5.67−4 5.10−4
1.20−2 8.74−4 1.28−4 1.18−4

Table A.4. DLHS: l∞ -errors for stabilized P1 P1 elements.

hmax θ sx sy
9.89−1 1.70−1 1.87−1 1.55−1
6.34−1 7.48−2 7.89−2 8.04−2
3.29−1 2.26−2 2.03−2 1.95−2
1.68−1 6.36−3 5.62−3 5.62−3
8.73−2 1.59−3 1.39−3 1.38−3
4.71−2 4.10−4 3.61−4 3.61−4
2.33−2 1.04−4 9.22−5 9.22−5

Table A.5. DLHS: L2 -errors for stabilized P2 P2 elements.


A.2. Convergence Errors 103

hmax θ sx sy
9.89−1 1.20−1 1.92−1 1.31−1
6.34−1 6.66−2 8.46−2 8.65−2
3.29−1 2.05−2 1.69−2 2.20−2
1.68−1 5.45−3 5.53−3 5.46−3
8.73−2 1.28−3 1.38−3 1.39−3
4.71−2 3.31−4 3.51−4 3.55−4
2.33−2 8.91−5 9.03−5 8.97−5

Table A.6. DLHS: l∞ -errors for stabilized P2 P2 elements.

hmax p ux uy σxx σxy σyy


9.89−1 3.21−2 9.00−1 7.79−1 4.75−1 4.05−1 4.74−1
6.34−1 5.35−2 6.29−1 6.26−1 3.39−1 3.39−1 3.39−1
3.29−1 5.83−2 1.69−1 1.60−1 1.15−1 1.16−1 1.14−1
1.68−1 1.28−2 3.37−2 3.47−2 2.52−2 2.46−2 2.54−2
8.73−2 2.02−3 7.17−3 7.02−3 4.42−3 4.35−3 4.41−3
4.71−2 3.83−4 1.97−3 2.00−3 1.02−3 1.01−3 1.02−3
2.33−2 8.48−5 5.65−4 5.71−4 2.49−4 2.50−4 2.50−4
1.20−2 1.84−5 1.67−4 1.67−4 6.11−5 6.09−5 6.10−5

Table A.7. DLSS: L2 -errors for stabilized P1 P1 P1 elements (test case 1).

hmax p ux uy σxx σxy σyy


9.89−1 3.17−2 9.69−1 9.68−1 4.18−1 4.10−1 4.17−1
6.34−1 5.90−2 8.59−1 8.57−1 2.90−1 2.89−1 2.89−1
3.29−1 8.86−2 3.84−1 3.84−1 1.96−1 1.91−1 1.90−1
1.68−1 3.18−2 1.26−1 1.26−1 5.74−2 5.79−2 5.21−2
8.73−2 1.24−2 3.52−2 3.39−2 1.17−2 1.16−2 1.22−2
4.71−2 3.16−3 1.31−2 1.29−2 3.48−3 3.11−3 3.44−3
2.33−2 6.82−4 5.66−3 5.83−3 9.24−4 9.49−4 9.49−4
1.20−2 2.28−4 2.43−3 2.53−3 2.26−4 2.11−4 2.36−4

Table A.8. DLSS: l∞ -errors for stabilized P1 P1 P1 elements (test case 1).
104 A. Convergence Data and Exact Solutions

hmax p ux uy σxx σxy σyy


9.89−1 1.84−1 2.04−1 2.70−1 2.73−1 2.63−1 2.45−1
6.34−1 1.01−1 9.35−2 1.52−1 1.36−1 9.41−2 1.08−1
3.29−1 3.32−2 3.21−2 5.48−2 4.67−2 3.30−2 3.52−2
1.68−1 1.06−2 1.18−2 2.24−2 1.33−2 8.89−3 1.04−2
8.73−2 2.71−3 4.32−3 8.59−3 3.11−3 2.08−3 2.51−3
4.71−2 7.23−4 1.77−3 3.73−3 7.48−4 5.22−4 6.38−4

Table A.9. DLSS: L2 -errors for unstabilized P3 P2 P1 elements (test case 2).

hmax p ux uy σxx σxy σyy


9.89−1 1.18−1 1.90−1 3.02−1 4.06−1 2.53−1 4.31−1
6.34−1 7.14−2 1.48−1 2.04−1 1.91−1 1.95−1 2.08−1
3.29−1 2.71−2 9.50−2 1.46−1 7.99−2 8.20−2 8.20−2
1.68−1 1.05−2 5.27−2 9.12−2 3.00−2 3.03−2 2.85−2
8.73−2 3.68−3 2.98−2 4.81−2 7.94−3 6.31−3 8.60−3
4.71−2 1.21−3 1.52−2 2.64−2 2.42−3 2.20−3 2.56−3

Table A.10. DLSS: l∞ -errors for unstabilized P3 P2 P1 elements (test case 2).

hmax p ux uy σxx σxy σyy


9.89−1 2.180 6.45−1 8.65−1 6.32−1 6.59−1 5.97−1
6.34−1 1.590 4.16−1 5.81−1 4.55−1 3.47−1 4.19−1
3.29−1 1.65−1 6.76−2 9.31−2 8.89−2 7.24−2 8.28−2
1.68−1 8.88−3 1.98−2 2.47−2 2.45−2 1.98−2 2.34−2
8.73−2 4.50−3 5.54−3 7.35−3 6.02−3 4.64−3 5.69−3
4.71−2 1.43−3 1.75−3 2.36−3 1.52−3 1.15−3 1.43−3
2.33−2 3.91−4 5.73−4 7.84−4 4.02−4 2.93−4 3.69−4
1.20−2 9.79−5 1.87−4 2.63−4 9.88−5 7.23−5 8.92−5

Table A.11. DLSS: L2 -errors for stabilized P1 P1 P1 elements (test case 2).

hmax p ux uy σxx σxy σyy


9.89−1 2.630 5.61−1 6.49−1 5.45−1 8.10−1 5.66−1
6.34−1 1.860 3.95−1 4.65−1 4.07−1 3.41−1 3.65−1
3.29−1 1.86−1 1.88−1 2.01−1 1.70−1 1.77−1 1.50−1
1.68−1 1.79−2 9.15−2 1.14−1 6.97−2 7.16−2 6.98−2
8.73−2 7.10−3 3.60−2 4.45−2 2.14−2 1.98−2 1.98−2
4.71−2 2.26−3 1.54−2 1.99−2 6.97−3 5.65−3 6.66−3
2.33−2 7.17−4 6.88−3 9.32−3 1.81−3 1.49−3 1.62−3
1.20−2 1.92−4 3.17−3 4.25−3 4.66−4 4.48−4 5.11−4

Table A.12. DLSS: l∞ -errors for stabilized P1 P1 P1 elements (test case 2).
A.2. Convergence Errors 105

hmax p ux uy σxx σxy σyy


9.89−1 2.07−1 1.55−1 1.99−1 2.20−1 3.33−1 2.08−1
6.34−1 1.12−1 6.63−2 1.08−1 1.17−1 8.81−2 1.13−1
3.29−1 4.12−2 2.34−2 3.80−2 3.41−2 2.83−2 3.37−2
1.68−1 1.18−2 6.59−3 1.08−2 9.52−3 7.33−3 9.39−3
8.73−2 2.94−3 1.62−3 2.66−3 2.25−3 1.73−3 2.21−3
4.71−2 7.62−4 4.16−4 6.87−4 5.82−4 4.45−4 5.71−4
2.33−2 1.94−4 1.05−4 1.74−4 1.48−4 1.13−4 1.45−4

Table A.13. DLSS: L2 -errors for stabilized P2 P2 P2 elements (test case 2).

hmax p ux uy σxx σxy σyy


9.89−1 1.22−1 1.10−1 1.83−1 6.80−1 3.35−1 6.72−1
6.34−1 9.08−2 8.82−2 9.78−2 3.68−1 3.55−1 3.63−1
3.29−1 3.46−2 4.37−2 6.23−2 1.44−1 1.15−1 1.43−1
1.68−1 9.44−3 1.59−2 1.87−2 3.63−2 2.71−2 3.64−2
8.73−2 2.11−3 3.84−3 5.02−3 7.92−3 6.75−3 7.73−3
4.71−2 5.52−4 1.18−3 1.45−3 1.86−3 1.59−3 1.82−3
2.33−2 1.37−4 2.88−4 4.11−4 4.55−4 3.91−4 4.53−4

Table A.14. DLSS: l∞ -errors for stabilized P2 P2 P2 elements (test case 2).

hmax θ sx sy p ux uy σxx σxy σyy


9.89−1 1.60−1 4.32−1 3.76−1 3.440 6.35−1 7.94−1 3.97−1 3.30−1 3.80−1
6.34−1 9.82−2 3.13−1 3.01−1 1.930 3.74−1 5.17−1 2.36−1 2.27−1 2.23−1
3.29−1 2.94−2 9.41−2 8.24−2 2.98−1 1.90−1 3.12−1 9.33−2 9.09−2 8.98−2
1.68−1 1.14−2 2.29−2 2.11−2 3.56−2 8.41−2 1.45−1 3.05−2 2.91−2 2.90−2
8.73−2 3.56−3 4.74−3 4.84−3 2.82−3 2.83−2 5.10−2 7.47−3 7.04−3 7.18−3
4.71−2 1.33−3 1.24−3 1.24−3 1.44−3 1.09−2 2.04−2 1.94−3 1.92−3 1.86−3
2.33−2 4.86−4 3.26−4 3.23−4 5.56−4 4.04−3 7.52−3 5.02−4 4.96−4 4.82−4
1.20−2 1.68−4 8.08−5 8.02−5 1.14−4 1.42−3 2.60−3 1.25−4 1.22−4 1.19−4

Table A.15. LinR13: L2 -errors for stabilized P1 P1 P1 P1 P1 elements (test case 1).
106 A. Convergence Data and Exact Solutions

hmax θ sx sy p ux uy σxx σxy σyy


9.89−1 1.25−1 2.81−1 2.68−1 3.810 6.96−1 8.35−1 3.98−1 3.66−1 3.99−1
6.34−1 7.58−2 1.79−1 2.06−1 2.210 6.52−1 7.94−1 2.35−1 2.40−1 2.33−1
3.29−1 5.66−2 7.60−2 5.78−2 3.84−1 5.51−1 7.48−1 9.01−2 9.58−2 8.45−2
1.68−1 3.30−2 2.58−2 2.38−2 7.76−2 3.43−1 4.77−1 4.23−2 3.69−2 3.70−2
8.73−2 1.49−2 5.65−3 6.44−3 1.03−2 1.45−1 2.30−1 1.20−2 1.05−2 1.12−2
4.71−2 7.52−3 1.83−3 1.78−3 3.81−3 7.08−2 1.23−1 3.86−3 3.87−3 3.50−3
2.33−2 4.21−3 5.09−4 5.13−4 1.09−3 4.12−2 7.07−2 1.13−3 1.11−3 1.15−3
1.20−2 1.88−3 1.32−4 1.23−4 3.36−4 2.10−2 3.53−2 3.20−4 3.20−4 3.18−4

Table A.16. LinR13: l∞ -errors for stabilized P1 P1 P1 P1 P1 elements (test case 1).

hmax θ sx sy p ux uy σxx σxy σyy


9.89−1 4.97−2 2.04−1 1.73−1 6.69−1 3.15−1 4.69−1 3.10−1 2.67−1 2.99−1
6.34−1 1.48−2 9.61−2 9.87−2 1.57−1 1.14−1 1.67−1 1.38−1 1.35−1 1.33−1
3.29−1 3.36−3 3.16−2 3.25−2 1.96−2 2.80−2 3.96−2 4.66−2 4.57−2 4.53−2
1.68−1 1.05−3 9.34−3 9.43−3 5.50−3 6.50−3 9.06−3 1.36−2 1.34−2 1.30−2
8.73−2 2.43−4 2.34−3 2.35−3 1.51−3 1.06−3 2.06−3 3.40−3 3.28−3 3.25−3
4.71−2 6.45−5 6.07−4 6.11−4 4.03−4 3.04−4 5.12−4 8.78−4 8.55−4 8.44−4

Table A.17. LinR13: L2 -errors for stabilized P2 P2 P2 P2 P2 elements (test case 1).

hmax θ sx sy p ux uy σxx σxy σyy


9.89−1 3.97−2 2.03−1 1.80−1 3.30−1 5.08−1 5.88−1 9.65−1 9.40−1 9.39−1
6.34−1 1.99−2 9.82−2 1.05−1 1.14−1 3.17−1 3.47−1 4.58−1 4.55−1 4.41−1
3.29−1 8.84−3 3.85−2 3.51−2 3.33−2 1.16−1 1.48−1 1.34−1 1.30−1 1.27−1
1.68−1 3.56−3 1.17−2 1.13−2 1.30−2 5.41−2 4.79−2 3.05−2 2.82−2 2.84−2
8.73−2 9.77−4 2.95−3 2.74−3 2.13−3 8.05−3 1.83−2 6.88−3 6.83−3 6.66−3
4.71−2 4.11−4 7.74−4 7.03−4 4.28−4 4.54−3 4.90−3 1.70−3 1.66−3 1.61−3

Table A.18. LinR13: l∞ -errors for stabilized P2 P2 P2 P2 P2 elements (test case 1).

hmax θ sx sy p ux uy σxx σxy σyy


9.89−1 5.87−2 3.30−1 3.10−1 8.01−1 1.290 5.96−1 6.57−1 9.42−1 1.020
6.34−1 4.37−2 2.92−1 1.91−1 5.69−1 7.21−1 4.83−1 8.46−1 1.060 1.340
3.29−1 1.92−2 1.29−1 7.64−2 1.83−1 2.11−1 2.41−1 4.36−1 6.30−1 6.49−1
1.68−1 6.94−3 3.04−2 2.05−2 4.20−2 7.67−2 7.39−2 1.18−1 1.66−1 1.71−1
8.73−2 2.17−3 5.22−3 4.60−3 6.31−3 2.59−2 1.22−2 2.02−2 2.81−2 2.80−2
4.71−2 8.08−4 1.17−3 1.15−3 1.20−3 7.29−3 2.59−3 3.99−3 5.44−3 5.33−3
2.33−2 2.94−4 2.87−4 2.93−4 2.64−4 2.00−3 8.72−4 9.03−4 1.22−3 1.19−3
1.20−2 1.02−4 7.05−5 7.26−5 6.50−5 5.52−4 3.30−4 2.23−4 3.01−4 2.92−4

Table A.19. LinR13: L2 -errors for stabilized P1 P1 P1 P1 P1 elements (test case 2).
A.2. Convergence Errors 107

hmax θ sx sy p ux uy σxx σxy σyy


9.89−1 6.76−2 2.00−1 1.91−1 1.490 7.69−1 9.18−1 5.10−1 6.96−1 5.70−1
6.34−1 5.59−2 1.76−1 1.16−1 1.090 7.31−1 8.22−1 3.75−1 6.42−1 5.72−1
3.29−1 3.24−2 9.32−2 5.54−2 3.91−1 4.35−1 5.15−1 2.21−1 3.72−1 3.20−1
1.68−1 1.58−2 2.88−2 1.60−2 1.07−1 1.74−1 2.12−1 7.19−2 1.15−1 9.57−2
8.73−2 7.54−3 6.09−3 4.82−3 1.13−2 2.83−2 4.53−2 1.76−2 1.89−2 1.95−2
4.71−2 4.03−3 1.50−3 1.42−3 2.06−3 7.79−3 8.54−3 5.28−3 6.40−3 6.25−3
2.33−2 2.25−3 4.26−4 3.71−4 4.37−4 6.27−3 5.66−3 1.55−3 1.77−3 1.63−3
1.20−2 1.09−3 1.08−4 9.70−5 1.05−4 4.00−3 3.76−3 3.80−4 5.10−4 4.37−4

Table A.20. LinR13: l∞ -errors for stabilized P1 P1 P1 P1 P1 elements (test case 2).
B. Solver Related Data
The implementation of a rarefied gas solver was essential part of this thesis. In terms
of reproducibility for the obtained results, all relevant solver data is listed in its
current form by the date of the submission of this work. An up-to-date version can
be found in the solver repository, as explained in Section 9.2.1. The current solver
documentation in LATEX-format is listed in Appendix B.1 while the relevant parts of
the solver implementation are listed in Appendix B.2.

B.1. Solver Documentation


110 B. Solver Related Data

fenicsR13
Release 1.0

Lambert Theisen

Sep 23 19 at 06:12
B.1. Solver Documentation 111

CONTENTS

1 Installation 2
1.1 Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

2 Documentation 5
2.1 Pre-Build Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Manual Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

3 Developer Legacy Notes 6


3.1 Developer Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.2 Python notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3.3 Create new version tag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.4 Gitlab CI Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.5 macOS Native FEniCS Installation (not recommended) . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.6 Further Installation Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

4 Contact 10

5 Change log 11
5.1 1.0 (2019-09-23) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.2 0.5 (2019-09-13) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.3 0.4 (2019-08-21) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
5.4 0.3 (2019-08-11) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
5.5 0.2 (2019-07-29) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
5.6 0.1 (2019-07-17) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

6 Indices and Tables 13

7 Contents 14
7.1 src . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7.2 heat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.3 stress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7.4 r13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
7.5 examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

Bibliography 34

Python Module Index 35

Index 36

i
112 B. Solver Related Data

fenicsR13, Release 1.0

Last update: Sep 23 19 at 06:12

#extendedGasDynamics #using #FEniCS


Repository for Master thesis project regarding FEM simulations for non-equilibrium gas dynamics.

CONTENTS 1
B.1. Solver Documentation 113

CHAPTER

ONE

INSTALLATION

It is recommended to use the program within a Docker container.

1.1 Docker

First install Docker Desktop for your OS. Then:


# Clone Repository and open main folder
git clone git@git.rwth-aachen.de:lamBOO/fenicsR13.git
cd fenicsR13

# Build and run fenics service


docker-compose build fenics
docker-compose run fenics

### 1.START) Execute lid_driven_cavity example


# Move to folder:
cd examples/lid_driven_cavity
# Create mesh:
python3 create_meshes.py
# Run program with given input file:
python3 ../../src/fenicsR13.py input.yml
# Go to folder with simulation results (=casename in input.yml)
cd lid_driven_cavity
# Perform GUI visualization on XDMF-files with Paraview:
echo "Data files:" $(find . -name "*.xdmf")
echo "E.g.: Open Paraview > File > Open > u_0.xdmf > Apply filters"
# Inspect PDF field plots:
echo "Field plots:" $(find . -name "*.pdf")
# Leave directory:
cd ../..
### 1.END)

### 2.START) Execute channel_flow example


# Move to folder:
cd examples/channel_flow
# Create mesh:
python3 create_meshes.py
# Run program with given input file:
python3 ../../src/fenicsR13.py input.yml
# Go to folder with simulation results (=casename in input.yml)
cd channel_flow
# Perform GUI visualization on XDMF-files with Paraview:
(continues on next page)

2
114 B. Solver Related Data

fenicsR13, Release 1.0

(continued from previous page)


echo "Data files:" $(find . -name "*.xdmf")
echo "E.g.: Open Paraview > File > Open > u_0.xdmf > Apply filters"
# Inspect PDF field plots:
echo "Field plots:" $(find . -name "*.pdf")
# Generate correlation data between Knudsen number and massflow
bash postprocessing.sh
cat table.csv
echo "Knudsen paradox ;-)"
# Leave directory:
cd ../..
### 2.END)

### 3.START) Execute convergence test


# Move to folder:
cd tests/r13
# Meshes already in Git:
ls ../mesh
# Run program with given input file:
python3 ../../src/fenicsR13.py inputs/r13_1_coeffs_nosources_norot_inflow_p1p1p1p1p1_
˓→stab.yml

# Go to folder with simulation results (=casename in input.yml)


cd r13_1_coeffs_nosources_norot_inflow_p1p1p1p1p1_stab
# Open errors:
cat errors.csv
# Open PDF convergence plot:
echo "E.g.: Open Adobe Reader > File > Open > convergence_plot_r13_1_coeffs_nosources_
˓→norot_inflow_p1p1p1p1p1_stab.pdf"

# Perform GUI visualization on XDMF-files with Paraview:


echo "Data files:" $(find . -name "*.xdmf")
echo "E.g.: Open Paraview > File > Open > u_0.xdmf > Apply filters"
# Leave directory:
cd ../..
### 3.END)

# Parallel execution ("-u" to flash stdout)


# Usage: mpirun -n <numberOfProcesses> <serialCommand>
# E.g.: mpirun -n 4 python3 -u ../../src/fenicsR13.py input.yml

The main folder of this repository contains a Dockerfile defining the used environment. Here, we used
the optimized and official FEniCS Docker image and include Gmsh and install some requirements from the
requirements.txt. This can take a while, especially the Gmsh mirror can be quite slow. To avoid very long
execution commands (docker run <..> -v <volume share> <etc..>), a docker-compose.yml is
used to store all these parameters. docker-compose acts as an wrapper for the Docker execution.
The fenics environment (also called service in the docker-compose.yml) first has to be build and can be
executed afterwards. The steps to perform read

# build and run fenics service


docker-compose build fenics
docker-compose run fenics

The whole repository is mounted as a volume under /home/fenics/shared in the container and should
be the default folder on startup. To execute a simulation case, go to to the case folder (e.g. examples/
lid_driven_cavity) and execute the solver main program fenicsR13.py (which is located in the src-
directory in the top level) while specifying the input file as first command line argument. This can be for exam-
ple python3 ../../src/fenicsR13.py inout.yml. Output files should be written to a folder which is
named after the output_folder keyword of the input.yml. Each output field (temperature, pressure,. . . ) has a

1.1. Docker 3
B.1. Solver Documentation 115

fenicsR13, Release 1.0

.h5-file and an .xdmf-file. The XDMF-files can be opened in Paraview to perform visualization.
It is possible to use a Jupyter sever or a X11 forwarding but this is not recommended anymore. All relevant plots are
now written by default without the need for the tricky X11 forwarding or interactive usage with Jupyter.

4 Chapter 1. Installation
116 B. Solver Related Data

CHAPTER

TWO

DOCUMENTATION

Documentation using Sphinx is available.

2.1 Pre-Build Version

Visit the hosted version on Gitlab Pages or download the artifacts from Gitlab’s CI pages-pipeline.

2.2 Manual Generation

# cat .gitlab-ci.yml
cd docs
sphinx-apidoc -o source/src ../src
sphinx-apidoc -o source/tests/heat ../tests/heat
sphinx-apidoc -o source/tests/stress ../tests/stress
sphinx-apidoc -o source/tests/r13 ../tests/r13
sphinx-apidoc -o source/examples ../examples
make html
make latex

5
B.1. Solver Documentation 117

CHAPTER

THREE

DEVELOPER LEGACY NOTES

3.1 Developer Tips

• Monitor the performance of the program with e.g.:

htop -p `{ python3 ../../src/fenicsR13.py inputs/1_coeffs_nosources_norot_


˓→inflow_p1p1p1p1p1_stab.yml > /dev/null & } && echo $!`

• Use doctest with python3 -m doctest -v src/meshes.py


• Run pydocstyle once in a while
• Matplotbib fails when having wrong backend on macOS
– Fix: Add backend: TkAgg to ~/.matplotlib/matplotlibrc file
• Performance in Docker is way better than conda build, especially JIT compilation is faster
• Get C++ inlcude paths: echo | gcc -E -Wp,-v -
• Bessel functions in DOLFIN:
– C++17 functions cannpot be used. Boost functions also not per default.
Expression("boost::math::cyl_bessel_i(0,atan2(x[1], x[0]))",
degree=2) is allowed if one changes in file /usr/local/lib/python3.6/
dist-packages/dolfin/jit/jit.py

_math_header = """
// cmath functions
#include <boost/math/special_functions/bessel.hpp> // Added
%s
"""

3.2 Python notes

• Get current work directory:

import os
cwd = os.getcwd()
print(cwd)

• Latex font for matplotlib:

6
118 B. Solver Related Data

fenicsR13, Release 1.0

# LaTeX text fonts:


# Use with raw strings: r"$\mathcal{O}(h^1)$"
plt.rc('text', usetex=True)
plt.rc('font', family='serif')

• Get system path where modules are searched:


import sys
print(sys.path)

3.3 Create new version tag

1. Add CHANGELOG entry


2. Adapt version in conf.py for docs
3. Change badge in README.rst
4. Change version in program information printing

3.4 Gitlab CI Setup

• The build stage has to be triggered manually when something in the setup changes. This is because it takes a
fair amount of time.
• In ~/.gitlab-runner/config.toml (for the runner):
– change priviliges to true
– Use local images: pull_policy = "if-not-present"
– To [[runners]] add environment = ["DOCKER_TLS_CERTDIR="] (See https://gitlab.
com/gitlab-org/gitlab-ce/issues/64959)
• Run local: gitlab-runner exec docker --docker-privileged build or with build replaced by job name

– maybe local vars have to be change to use local Docker images because CI_REGISTRY,. . . are not
set
An example gitlab runner config/toml in ~/.gitlab-runner can look like:
concurrent = 1
check_interval = 0

[[runners]]
name = "190716-macbookpro"
url = "https://git.rwth-aachen.de/"
token = "<PRIVATE_TOKEN>"
executor = "docker"
environment = ["DOCKER_TLS_CERTDIR="]
[runners.docker]
tls_verify = false
image = "docker:stable"
privileged = true
disable_cache = false
(continues on next page)

3.3. Create new version tag 7


B.1. Solver Documentation 119

fenicsR13, Release 1.0

(continued from previous page)


volumes = ["/cache"]
shm_size = 0
pull_policy = "if-not-present"
[runners.cache]

3.5 macOS Native FEniCS Installation (not recommended)

1. Install miniconda from here


1. If using zsh, add miniconda bins to PATH: export PATH="$HOME/ miniconda3/
bin:$PATH" to ~/.zshrc
2. Maybe, activation has to be done with executing <path to miniconda>/bin/activate
3. Optional: Create separate coda environment: conda creafenics-env
2. Install FEniCS using conda: conda install -c conda-forge fenics
1. Optional: Install matplobib: conda install -c conda-forge matplotlib
2. Optional: Install meshio: conda install -c mrossi meshio
3. Optional (for linting): conda install pylint
4. Install mshr with conda install -c conda-forge mshr
5. Fix macOS bug in matplotbib: mkdir -p ~/.matplotlib; echo "backend: TkAgg"
> ~/.matplotlib/matplotlibrc
6. XCode and command line developer tools msut be installed!
7. Optional: Install Jupyter: conda install -c anaconda jupyter
8. Optional: Install documentation system: conda install -c anaconda sphinx
9. Optional: conda install -c anaconda sympy

3.6 Further Installation Tips

Interactive Jupyter Notebooks with Microsoft’s Visual Studio Code


This is the most convenient solution. Run a file with %run ../../src/fenicsr13.py
X11 Window Forwarding on OSX
See guide for the programs to install. Then source the open-macos-gui-tunnel.
sh with . open-macos-gui-tunnel. Afterwards, start the container and run the
change-matplotbib-backend-tkagg.sh script to set the right matplotlib’s output.
X11 Window Forwarding on Windows
A nice guide can be found here on Dev.to.
The steps can be summarized as:
1. Install the package manager Chocolatey.
REM comment: open cmd.exe as admin
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile
˓→-InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object
(continues on next page)
˓→System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1

˓→'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

8 Chapter 3. Developer Legacy Notes


120 B. Solver Related Data

fenicsR13, Release 1.0

(continued from previous page)

2. Open cmd.exe as admin and install VcXsrv Windows X Server.

choco install vcxsrv

3. Open a X11 server and set the ip variable (that is used in the docker-compose.yml when starting the
Docker container to set export DISPLAY=${ip}:0).

# home of this repo


source sripts/open-windows-gui-tunnel.sh

3.6. Further Installation Tips 9


B.1. Solver Documentation 121

CHAPTER

FOUR

CONTACT

Author
Lambert Theisen
lambert.theisen@rwth-aachen.de
Supervisor
Prof. Dr. Manuel Torrilhon
Lehrstuhl für Mathematik (MathCCES)
RWTH Aachen University
mt@mathcces.rwth-aachen.de
Context
Masterthesis Computational Engineering Science
RWTH Aachen University
Simulation of Non-Equilibrium Gas Flows Using the FEniCS Computing Platform

10
122 B. Solver Related Data

CHAPTER

FIVE

CHANGE LOG

5.1 1.0 (2019-09-23)

• Improve examples
– Remove auxiliary files, clean cases, improve output plots and change some meshes
• Add WELCOME screen to Docker container with link to website and documentation
• Introduce more variables as dolfin.Constant() for parameters study w.o. compiling
• Add time measure for linear solve
• Thesis submission version

5.2 0.5 (2019-09-13)

• Add more examples with tests and documentation:


– Lid-Driven Cavity
– Channel Flow with Knudsen paradox plot
– Knudsen pump
• Add option to perform parameter studies
• Add massflow reporting option for arbitrary BCs
• Fix P1P2P4 stress test case
• Add more printing statement to program output
• Change formulation:
– Rename gamma to epsilon in inflow model
– Rename tau to Knudsen to have real dimensionless equations
– Replace sym(psi) -> psi because symmetric per definition
– Fix stf3d2 for arbitrary
• Extend documentation:
– Extended tutorial
– Move legacy notes to bottom of README

11
B.1. Solver Documentation 123

fenicsR13, Release 1.0

5.3 0.4 (2019-08-21)

• Finish documentation
– Also includes some doctests to test for edge cases
• Introduce develop branch to only have major version at master branch
• Add relative error calculation
• Add channel flow example (to test for Knudsen paradox)
• Fix error calculation for higher-order Ansatz function
– The previous error was based on DOFS (P2 elements therefore differ), the new error is based on vertex
values

5.4 0.3 (2019-08-11)

• Full linear R13 now converges


• Inflow model works
• Restructuration of BC specification
• Minor improvements in plotting routine

5.5 0.2 (2019-07-29)

• Decoupled stress system converges


• Add separated tensor operations module
– This was needed to implement operations on synthetic 3D tensors
• Add pytests for stress
• Add new logo
• Add more Sphinx documentation
• Restructure repository

5.6 0.1 (2019-07-17)

• Add logo
• Add Sphinx documentation
• Add pytests
• Add Gitlab CI scripts

12 Chapter 5. Change log


124 B. Solver Related Data

CHAPTER

SIX

INDICES AND TABLES

• genindex
• modindex
• search

13
B.1. Solver Documentation 125

CHAPTER

SEVEN

CONTENTS

7.1 src

7.1.1 fenicsR13 module

Program to solve linearized R13 equations.


Different modes allow to solved the ecoupled heat or stress system. Different meshes can be used to perform a
convergence study with given exact solution.
fenicsR13.print_information()
Print program name and information.
That is:

-> Version: v1.0


-> Contact: Lambert Theisen <lambert.theisen@rwth-aachen.de>
-> Contact: Manuel Torrilhon <mt@mathcces.rwth-aachen.de>
-> Repository: <https://git.rwth-aachen.de/lamBOO/fenicsR13>
-> Documentation: <https://lamboo.pages.rwth-aachen.de/fenicsR13/>
__ _ ____ _ _____
/ _| ___ _ __ (_) ___ ___| _ \/ |___ /
| |_ / _ \ '_ \| |/ __/ __| |_) | | |_ \
| _| __/ | | | | (__\__ \ _ <| |___) |
|_| \___|_| |_|_|\___|___/_| \_\_|____/

fenicsR13.main()
Execute the main program.
Searches for an "input.yml" file in the current directory to use as input.
Usage:

# Usage: <path_to_program> <input_file>


# Goto case folder:
cd tests/r13
python3 ../../src/fenicsR13.py inputs/ r13_1_coeffs_nosources_norot_
˓→inflow_p1p1p1p1p1_stab.yml

7.1.2 input module

Module for input related Classes.


Contains the Input class.

14
126 B. Solver Related Data

fenicsR13, Release 1.0

class input.Input(yaml_file)
Bases: object
Class to handle the input file in YAML format.
An example input file could look like:
# General
# =======
# - output_folder: Used as output folder
output_folder: r13_1_coeffs_sources_rot_noinflow_p2p2p2p2p2_stab

# Meshes
# ======
# - meshes: List of input meshes in h5 format to run simulations on
meshes:
- ../mesh/ring0.h5
- ../mesh/ring1.h5
- ../mesh/ring2.h5
- ../mesh/ring3.h5
- ../mesh/ring4.h5
# - ../mesh/ring5.h5
# - ../mesh/ring6.h5
# - ../mesh/ring7.h5
# - ../mesh/ring8.h5

# Numerical Parameters
# ====================
# - elements: Must contain the fields: theta, s, p, u, sigma
# - fields: List of FEM parameters (shape, degree)
# - shape: Element shape, e.g. Lagrange
# - degree: Element degree, e.g. 2
# - stabilization: Must contain cip
# - cip: Collection of Continous Interior Penalty (CIP) parameters
# - enable: Enable CIP stabilization
# - delta_1: Stabilization of grad(T)*grad(T_test) over edge
# - delta_2: Stabilization of grad(u)*grad(u_test) over edge
# - delta_3: Stabilization of grad(p)*grad(p_test) over edge
elements:
theta:
shape: Lagrange
degree: 2
s:
shape: Lagrange
degree: 2
p:
shape: Lagrange
degree: 2
u:
shape: Lagrange
degree: 2
sigma:
shape: Lagrange
degree: 2
stabilization:
cip:
enable: True
delta_1: 1.0
delta_2: 1.0
(continues on next page)

7.1. src 15
B.1. Solver Documentation 127

fenicsR13, Release 1.0

(continued from previous page)


delta_3: 0.01

# Formulation Parameters
# ======================
# - nsd: Number of spatial dimensions == 2
# - mode: Formulation mode, one of heat, stress, r13
# - use_coeffs: Use real R13 coefficients, False only valid in heat
# - kn: Knudsen numberkn
# - xi_tilde: Refaction coefficient in Maxwell accomodation model
# - heat_source: Heat source function for mode==heat||r13
# - mass_source: Mass source function for mode==stress||r13
nsd: 2
mode: r13
use_coeffs: True
kn: 1.0
xi_tilde: 1.0
heat_source: 0
mass_source: 1.0 * (1.0 - (5.0*pow(R,2))/(18.0*pow(kn,2))) * cos(phi)

# Boundary Conditions
# ===================
# - bcs: Dictionary of all boundary IDs from mesh
# - bc_id: must contain theta_w, u_t_w, u_n_w, p_w, epsilon_w
# - theta_w: Value for temperature at wall
# - u_t_w: Value for tangential velocity at wall
# - u_n_w: Value for normal velocity at wall
# - p_w: Value for pressure at wall
# - epsilon_w: Inflow-model parameter <=> Weight of pressure
bcs:
3000:
theta_w: 1.0
u_t_w: -10
u_n_w: 0
p_w: 0
epsilon_w: 0
3100:
theta_w: 0.5
u_t_w: 0
u_n_w: 0
p_w: 0
epsilon_w: 0

# Convergence Study
# =================
# - enable: Enable convergence study on given meshes
# - exact_solution: Path to exact solution in cpp-format to compare
# - plot: Show errors in matplotlib window. PDF output is default
# - write_systemmatrix: Writes out systemmatrix (LHS)
# - rescale_pressure: Rescale numerical pressure result for zero mean
# - relative_errors: Use relative errors. If esol=0, use absolute.
convergence_study:
enable: True
exact_solution: esols/1_coeffs_sources_rot_noinflow.cpp
plot: False
write_systemmatrix: False
rescale_pressure: True
relative_error: True
(continues on next page)

16 Chapter 7. Contents
128 B. Solver Related Data

fenicsR13, Release 1.0

(continued from previous page)

# Postprocessing
# ==============
# - write_pdfs: Write all solution fields as PDF plot
# - massflow: List of BC IDs to compute massflow J=int_bc dot(u,n) ds
postprocessing:
write_pdfs: True

# Parameter Study
# ==============
# - enable: Repeat simulation with different p. values (study)
# - parameter_key: Key as list, e.g. ["elemenets", "p", "degree"]
# - parameter_values: List of value for parameter, e.g. [0.01,0.1,1,10]
parameter_study:
enable: True
parameter_key: ["kn"]
parameter_values: [1,2,3]

Further input examples can be found in the tests or examples folders.

Examples

>>> corrupt_input = Input("etc/doctest_data/corrupt_input.yml")


Traceback (most recent call last):
...
Exception: Parsing error

>>> working_input = Input("tests/heat/inputs/heat_01_coeffs_p1p1_stab.yml")


Input:...

__init__(yaml_file)
Construct the Input class.
get_from_input(map_list)
Get value of the input dict by using a list of stacked keys.
Source: https://stackoverflow.com/questions/14692690/.. ..access-nested-dictionary-items-via-a-list-
of-keys/37704379
set_in_input(map_list, value)
Change value of the input dict by using a list of stacked keys.

7.1.3 meshes module

Module to store the mesh classes.


Currently, the only mesh format is h5.
class meshes.H5Mesh(h5_file)
Bases: object
Mesh class.
Raises Exception – File not found

7.1. src 17
B.1. Solver Documentation 129

fenicsR13, Release 1.0

Examples

>>> mesh = H5Mesh("non_existing_mesh.h5")


Traceback (most recent call last):
...
Exception: non_existing_mesh.h5 not found

__init__(h5_file)
Construct the object.
This includes:
(1) Read mesh
(2) Read subdomains
(3) Read boudnaries

7.1.4 postprocessor module

Module to store the postprocessor classes.


class postprocessor.Postprocessor(data, output_folder)
Bases: object
The class for postprocessing.
The constructor expects a list of data dictionaries of the form:
[
{
'h': 0.9886573325052778,
'theta': {'L_2': 0.18459230027019588,
'l_inf': 0.11513941287387819},
'sx': {'L_2': 0.38232086596749726,
'l_inf': 0.30555017240112986},
'sy': {'L_2': 0.51913814853123219,
'l_inf': 0.2889203116681428}
},
{
'h': 0.6340332990709842,
'theta': {'L_2': 0.19952286856390053,
'l_inf': 0.18948213107495288},
'sx': {'L_2': 0.3528639452958619,
'l_inf': 0.30118676712697767},
'sy': {'L_2': 0.34778282823921497,
'l_inf': 0.30792984640130788}
}
]

Parameters
• data (list of dicts) – Data to plot, see above for shape.
• output_folder (string) – output folder for PDF plot.

__init__(data, output_folder)
Construct postprocessor.
Only sets arguments

18 Chapter 7. Contents
130 B. Solver Related Data

fenicsR13, Release 1.0

plot_errors(show_popup)
Use matplotlib to plot all errors in a figure.
A slope marker is added.
Exporting PDFs with:

# Write PDF without access to backend, i.e. use PDF backend


import matplotlib
matplotlib.use('pdf')
import matplotlib.pyplot as plt # pylint: disable=C0413

write_errors()
Write errors to a csv file.
The output file (e.g. error.csv) looks like:

h,p_L_2,p_l_inf,ux_L_2,ux_l_inf,uy_L_2,uy_l_inf,sigmaxx_L_2,...
0.9,0.2,0.2,0.1,0.1,0.2,0.1,0.2,0.5,0.2,0.3,0.2,0.5
0.6,0.1,0.1,0.0,0.1,0.1,0.1,0.0,0.3,0.0,0.3,0.0,0.3

7.1.5 solver module

Solver module, contains the Solver class.


For usage examples, see the solver.Solver description.
class solver.Solver(params, mesh, time)
Bases: object
Class to store the actual solver.
Possible order of methods in context of convergence study (see main program): “mesh=meshes[i=0]”,
“__init__”, “assemble()”, “solve()”, “write()”, “. . . ”, “mesh=meshes[i+1]”, “__init__”, “. . . ”
Parameters
• params (input.Input) – Input parameters
• mesh (meshes.H5Mesh) – Mesh
• time (string) – Identifier for e.g. convergence study naming
Returns Solver object
Return type Solver

Example

>>> # Example usage:


>>> from input import Input
>>> from meshes import H5Mesh
>>> params = Input(
... "tests/heat/inputs/heat_01_coeffs_p1p1_stab.yml"
... ) # doctest: +ELLIPSIS
Input:...
>>> msh = H5Mesh("tests/mesh/ring0.h5")
>>> solver = Solver(params.dict, msh, "0") # "0" means time=0

7.1. src 19
B.1. Solver Documentation 131

fenicsR13, Release 1.0

__init__(params, mesh, time)


Initialize solver and setup variables from input parameters.
params = None
Doctest
assemble()
Assemble the weak form of the system, depending on the mode.
The system results from the two dimensional, linearized R13 equations [TOR2003].
solve()
Solve the previously assembled system.
Some available solver options:

# Some solver params


solver_parameters={
'linear_solver': 'gmres', 'preconditioner': 'ilu' # or
'linear_solver': 'petsc', 'preconditioner': 'ilu' # or
'linear_solver': 'direct' # or
'linear_solver': 'mumps'
}

calculate_errors()
Calculate and return the errors of numerical to exact solution.
This includes all calculated fields.

Note: Usage of np.max() does not work in parallel. So convergence studies have to be performed in serial
for now. Final fields should be right, so MPI can be used for production simulations.

Returns dict – Errors

write_content_to_file(filename, content)
Write content to a file in the output folder.
write()
Write all solver data to separate folder.
This includes the writing of:
(1) Solutions
(2) Parameter functions
(3) System matrices if set in input file
_Solver__calc_field_errors(field_, field_e_, v_field, name_)
Calculate both 𝐿2 and 𝑙∞ errors.
Works for scalars, vectors and tensors. The difference is written to a file. The exact solution is written to a
file. Relative errors are based per field component and the maximum value of the analytical solution. If the
analytical solution is uniformly zero, then the absolute erorrs is used. (equivalent to setting the maximum
to 1)
Parameters
• field_ (DOLFIN function) – calculated field
• field_e_ (DOLFIN function) – exact solution of field

20 Chapter 7. Contents
132 B. Solver Related Data

fenicsR13, Release 1.0

• v_field (DOLFIN fspace) – function space for error calculation


• name_ (string) – name of the field, used to write difference
Returns Dict with an error list for “L_2” and a list for “l_inf”
Return type dict
Raises Nothing –
See also:

calculate_errors() Function to return all field errors

Notes

For other norm types, see DOLFIN documentation1 and search for norms.

References

_Solver__calc_sf_mean(scalar_function)
Calculate the mean of a scalar function.

np.set_printoptions(precision=16)
# Precision is not soo nice, only 9 digits:
print(mean)
# In solve() has m. prec. hmmm:
print(self.__calc_sf_mean(self.sol["p"]))

Note: The following does not work in parallel because the mean is then only local. So convergence
studies have to be performed in serial:

mean = np.mean(scalar_function.compute_vertex_values())

_Solver__check_bcs()
Check if all boundaries from the input mesh have BCs prescribed.
Raises an exception if one BC is missing.
_Solver__createMacroExpr(cpp_string)
Return a DOLFIN expression with predefined macros.
These macros include:

Name Macro CPP Replacement


Radius wrt. to (0, 0) R sqrt(pow(x[0],2)+pow(x[1],2))
Angle wrt. (0, 0) phi atan2(x[1],x[0])
Knudsen number kn self.kn

The following expressions are therefore equal:

1 DOLFIN documentation

7.1. src 21
B.1. Solver Documentation 133

fenicsR13, Release 1.0

# expr1 is equal to expr2


expr1 = self.__createMacroExpr("R*cos(phi)")
expr2 = dolfin.Expression(
"R*cos(phi)",
degree=2,
R=dolfin.Expression("sqrt(pow(x[0],2)+pow(x[1],2))", degree=2),
phi=dolfin.Expression("atan2(x[1],x[0])", degree=2),
)

_Solver__load_exact_solution()
Load exact solution from the location given in input.yml.
The exact solution must be C++ format with a specific syntax. The esol.cpp must contain the classes:

Class mode
Temperature heat or r13
Heatflux heat or r13
Pressure stress or r13
Velocity stress or r13
Stress stress or r13

The file has to follow a specific syntax for DOLFIN. An example file could look like:

#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <cmath>
// additional includes
#include <boost/math/special_functions/bessel.hpp>
using namespace std;
namespace py = pybind11;
#include <dolfin/function/Expression.h>
double lambda_3 = sqrt(3.0/2.0); // some own constants
class Temperature : public dolfin::Expression {
public:
Temperature() : dolfin::Expression() {}
void eval(Eigen::Ref<Eigen::VectorXd> values,
Eigen::Ref<const Eigen::VectorXd> x) const override {
values[0] = 1; // value
}
};
class Heatflux : public dolfin::Expression {
public:
Heatflux() : dolfin::Expression(2) {} // note components=2!
void eval(Eigen::Ref<Eigen::VectorXd> values,
Eigen::Ref<const Eigen::VectorXd> x) const override {
values[0] = 42;
values[1] = 3.14;
}
};
class Pressure : public dolfin::Expression {
public:
Pressure() : dolfin::Expression() {}
void eval(Eigen::Ref<Eigen::VectorXd> values,
Eigen::Ref<const Eigen::VectorXd> x) const override {
values[0] = boost::math::cyl_bessel_i(1,2.71); // external
}
(continues on next page)

22 Chapter 7. Contents
134 B. Solver Related Data

fenicsR13, Release 1.0

(continued from previous page)


};
class Velocity : public dolfin::Expression {
public:
Velocity() : dolfin::Expression(2) {}
void eval(Eigen::Ref<Eigen::VectorXd> values,
Eigen::Ref<const Eigen::VectorXd> x) const override {
values[0] = lambda_3;
values[1] = 2;
}
};
class Stress : public dolfin::Expression {
public:
Stress() : dolfin::Expression(2,2) {} // note dim=2, shape=(2,2)
void eval(Eigen::Ref<Eigen::VectorXd> values,
Eigen::Ref<const Eigen::VectorXd> x) const override {
double xx_val = 1.23;
double xy_val = 1.23;
double yy_val = 1.23;
values[0] = xx_val;
values[1] = xy_val;
values[2] = yy_val;
// values[3] = xy_val // not used due to symmetry, skip
}
};
PYBIND11_MODULE(SIGNATURE, m) { // needed for DOLFIN
py::class_<Temperature, std::shared_ptr<Temperature>,
dolfin::Expression>
(m, "Temperature")
.def(py::init<>());
py::class_<Heatflux, std::shared_ptr<Heatflux>,
dolfin::Expression>
(m, "Heatflux")
.def(py::init<>());
py::class_<Pressure, std::shared_ptr<Pressure>,
dolfin::Expression>
(m, "Pressure")
.def(py::init<>());
py::class_<Velocity, std::shared_ptr<Velocity>,
dolfin::Expression>
(m, "Velocity")
.def(py::init<>());
py::class_<Stress, std::shared_ptr<Stress>, dolfin::Expression>
(m, "Stress")
.def(py::init<>());
}

_Solver__setup_function_spaces()
Set up function spaces for trial and test functions for assembling.
Depends on the mode. Function spaces depend on the choice of the element and its degree (see the input
file input.Input).
The following DOLFIN functions are used:

7.1. src 23
B.1. Solver Documentation 135

fenicsR13, Release 1.0

field DOLFIN Function


theta FiniteElement
s VectorElement
p FiniteElement
u VectorElement
sigma TensorElement

_Solver__write_discrete_system()
Write the discrete system matrix and the RHS vector.
Can be used to analyze e.g. condition number. Include writing of A and b of Ax = b.
File-ending is .mat.
Import the matrices/vectors e.g. into Matlab with:

% Input into MATLAB


At = readtable("A.mat");
bt = readtable("b.mat");
A = table2array(At);
b = table2array(bt);

Example

>>> # Construct LHS


>>> from dolfin import *
>>> mesh = IntervalMesh(2 ,0, 1)
>>> V = FunctionSpace(mesh, "Lagrange", 1)
>>> u = TrialFunction(V)
>>> v = TestFunction(V)
>>> a = inner(grad(u),grad(v))*dx
>>> L = df.Constant(1)*v*dx
>>> lhs = assemble(a)
>>> print(lhs.array())
[[ 2. -2. 0.]
[-2. 4. -2.]
[ 0. -2. 2.]]
>>> rhs = assemble(L)
>>> print(rhs.get_local())
[ 0.25 0.5 0.25]
>>> # Assign LHS to solver
>>> from input import Input
>>> from meshes import H5Mesh
>>> params = Input(
... "tests/heat/inputs/heat_01_coeffs_p1p1_stab.yml"
... ) # doctest: +ELLIPSIS
Input:...
>>> msh = H5Mesh("tests/mesh/ring0.h5")
>>> solver = Solver(params.dict, msh, "0") # "0" means time=0
>>> solver.form_a = a
>>> solver.form_b = L
>>> solver.output_folder = "./"
>>> solver._Solver__write_discrete_system()
>>> print(open("A_0.mat","r").read())
2.0000...00000e+00 -2.0000...00000e+00 0.000...000000e+00
(continues on next page)

24 Chapter 7. Contents
136 B. Solver Related Data

fenicsR13, Release 1.0

(continued from previous page)


-2.0000...00000e+00 4.0000...00000e+00 -2.0000...00000e+00
0.0000...00000e+00 -2.0000...00000e+00 2.000...000000e+00
<BLANKLINE>
>>> print(open("b_0.mat","r").read())
2.500000000000000000e-01
5.000000000000000000e-01
2.500000000000000000e-01
<BLANKLINE>

_Solver__write_parameters()
Write parameter functions for debug reasons.
This includes:
(1) Heat source as f_mass
(2) Mass Source as f_heat
The parameter functions are internally interpolated into a 𝑃1 space before writing.
_Solver__write_solutions()
Write all solutions fields.
_Solver__write_xdmf(name, field, write_pdf )
Write a given field to a XDMF file in the output folder.
Arguments
name The name to be given to the field. Will be used as filename and is the name of the field in e.g.
Paraview.
field The field to write.
write_pdf If true, write a simple PDF plot for all solution fields

7.1.6 tensoroperations module

Module to gather all additional tensor operations not present in UFL.


This especially includes all 3D operations and operations on tensors with rank above 2.
tensoroperations.stf3d2(rank2_2d)
Return the synthetic 3D symmetric and trace-free part of a 2D 2-tensor.
Return the synthetic 3D symmetric and trace-free (dev(sym(.))) 𝐵 ∈ R2×2 of the 2D 2-tensor 𝐴 ∈ R2×2 .
(︂ )︂
𝑎𝑥𝑥 𝑎𝑥𝑦
𝐴=
𝑎𝑦𝑥 𝑎𝑦𝑦
1 1
𝐵 = (𝐴)dev = (𝐴)sym − tr(𝐴)𝐼2×2
2 3

tensoroperations.sym3d3(rank3_3d)
Return the symmetric part of a 3D 3-tensor.
Returns the symmetric part 𝐴(𝑖𝑗𝑘) of a 3D rank-3 tensor 𝐴 ∈ R3×3×3 [STR2005] [TOR2018].

1
𝐴(𝑖𝑗𝑘) = (𝐴𝑖𝑗𝑘 + 𝐴𝑖𝑘𝑗 + 𝐴𝑗𝑖𝑘 + 𝐴𝑗𝑘𝑖 + 𝐴𝑘𝑖𝑗 + 𝐴𝑘𝑗𝑖 )
6

7.1. src 25
B.1. Solver Documentation 137

fenicsR13, Release 1.0

tensoroperations.stf3d3(rank3_3d)
Return the symmetric and trace-free part of a 3D 3-tensor.
Return the symmetric and trace-free part of a 3D 3-tensor. (dev(sym(.)))
Returns the STF part 𝐴⟨𝑖𝑗𝑘⟩ of a rank-3 tensor 𝐴 ∈ R3×3×3 [TOR2018].

1 [︀ ]︀
𝐴⟨𝑖𝑗𝑘⟩ = 𝐴(𝑖𝑗𝑘) − 𝐴(𝑖𝑙) 𝛿𝑗𝑘 + 𝐴(𝑙𝑗𝑙) 𝛿𝑖𝑘 + 𝐴(𝑙𝑙𝑘) 𝛿𝑖𝑗
5
𝜕𝑆⟨𝑖𝑗
A gradient 𝜕𝑥𝑘⟩ of a symmetric 2-tensor 𝑆 ∈ R3×3 has the deviator [STR2005]:
(︂ )︂ [︂(︂ )︂
𝜕𝑆⟨𝑖𝑗 1 𝜕𝑆𝑖𝑗 𝜕𝑆𝑖𝑘 𝜕𝑆𝑗𝑘 1 𝜕𝑆𝑖𝑟 𝜕𝑆𝑖𝑟 𝜕𝑆𝑟𝑟
= + + − + + 𝛿𝑗𝑘
𝜕𝑥𝑘⟩ 3 𝜕𝑥𝑘 𝜕𝑥𝑗 𝜕𝑥𝑖 15 𝜕𝑥𝑟 𝜕𝑥𝑟 𝜕𝑥𝑖
(︂ )︂ (︂ )︂ ]︂
𝜕𝑆𝑗𝑟 𝜕𝑆𝑗𝑟 𝜕𝑆𝑟𝑟 𝜕𝑆𝑘𝑟 𝜕𝑆𝑘𝑟 𝜕𝑆𝑟𝑟
+ + + 𝛿𝑖𝑘 + + + 𝛿𝑖𝑗
𝜕𝑥𝑟 𝜕𝑥𝑟 𝜕𝑥𝑗 𝜕𝑥𝑟 𝜕𝑥𝑟 𝜕𝑥𝑘

tensoroperations.gen3dTF2(rank2_2d)
Generate a 3D tracefree 2-tensor from a 2D 2-tensor.
Returns the synthetic 3D version 𝐴 ∈ R3×3 of a 2D rank-2 tensor 𝐵 ∈ R2×2 .
(︂ )︂
𝑏 𝑏
𝐵 = 𝑥𝑥 𝑥𝑦
𝑏𝑦𝑥 𝑏𝑦𝑦
⎛ ⎞
𝑏𝑥𝑥 𝑏𝑥𝑦 0
𝐴 = ⎝𝑏𝑦𝑥 𝑏𝑦𝑦 0 ⎠
0 0 −(𝑏𝑦𝑥 + 𝑏𝑦𝑦 )

Example

>>> t2d = df.Expression((("1","2"),("3","4")), degree=1)


>>> t3d = gen3dTF2(t2d)
>>> print(
... t3d[0,0]==t2d[0,0] and
... t3d[0,1]==t2d[0,1] and
... t3d[0,2]==0 and
... t3d[1,0]==t2d[1,0] and
... t3d[1,1]==t2d[1,1] and
... t3d[1,2]==0 and
... t3d[2,0]==0 and
... t3d[2,1]==0 and
... t3d[2,2]==-t2d[0,0]-t2d[1,1]
... )
True

tensoroperations.gen3d2(rank2_2d)
Generate a 3D 2-tensor from a 2D 2-tensor (add zeros to last dimensions).
Returns the synthetic 3D version 𝐴 ∈ R3×3 of a 2D rank-2 tensor 𝐵 ∈ R2×2 . The 3D-components are set to
zero.
(︂ )︂
𝑏 𝑏
𝐵 = 𝑥𝑥 𝑥𝑦
𝑏𝑦𝑥 𝑏𝑦𝑦
⎛ ⎞
𝑏𝑥𝑥 𝑏𝑥𝑦 0
𝐴 = 𝑏𝑦𝑥 𝑏𝑦𝑦 0⎠

0 0 0

26 Chapter 7. Contents
138 B. Solver Related Data

fenicsR13, Release 1.0

Example

>>> t2d = df.Expression((("1","2"),("3","4")), degree=1)


>>> t3d = gen3d2(t2d)
>>> print(
... t3d[0,0]==t2d[0,0] and
... t3d[0,1]==t2d[0,1] and
... t3d[0,2]==0 and
... t3d[1,0]==t2d[1,0] and
... t3d[1,1]==t2d[1,1] and
... t3d[1,2]==0 and
... t3d[2,0]==0 and
... t3d[2,1]==0 and
... t3d[2,2]==0
... )
True

tensoroperations.grad3dOf2(rank2_3d)
Return 3D gradient of 3D 2-tensor.
𝜕𝐴𝑖𝑗
Returns the 3D gradient (w.r.t. 𝑥, 𝑦, 𝑧) 𝜕𝑥𝑘 of a 3D 𝑧-independent rank-2 tensor 𝐴(𝑥, 𝑦) ∈ R3×3 where
⎛ ⎞
𝑎𝑥𝑥 (𝑥, 𝑦) 𝑎𝑥𝑦 (𝑥, 𝑦) 𝑎𝑥𝑧 (𝑥, 𝑦)
𝐴𝑖𝑗 = ⎝𝑎𝑦𝑥 (𝑥, 𝑦) 𝑎𝑦𝑦 (𝑥, 𝑦) 𝑎𝑦𝑧 (𝑥, 𝑦)⎠
𝑎𝑧𝑥 (𝑥, 𝑦) 𝑎𝑧𝑦 (𝑥, 𝑦) 𝑎𝑧𝑧 (𝑥, 𝑦)
𝜕𝐴𝑖𝑗
The function then returns the 3-tensor with components 𝜕𝑥𝑘 where
⎛ ⎞
𝜕𝑥 𝑎𝑥𝑥 (𝑥, 𝑦) 𝜕𝑥 𝑎𝑥𝑦 (𝑥, 𝑦) 𝜕𝑥 𝑎𝑥𝑧 (𝑥, 𝑦)
𝜕𝐴𝑖𝑗 𝜕𝐴𝑖𝑗
= = ⎝𝜕𝑥 𝑎𝑦𝑥 (𝑥, 𝑦) 𝜕𝑥 𝑎𝑦𝑦 (𝑥, 𝑦) 𝜕𝑥 𝑎𝑦𝑧 (𝑥, 𝑦)⎠ ,
𝜕𝑥1 𝜕𝑥
𝜕𝑥 𝑎𝑧𝑥 (𝑥, 𝑦) 𝜕𝑥 𝑎𝑧𝑦 (𝑥, 𝑦) 𝜕𝑥 𝑎𝑧𝑧 (𝑥, 𝑦)
⎛ ⎞
𝜕𝑦 𝑎𝑥𝑥 (𝑥, 𝑦) 𝜕𝑦 𝑎𝑥𝑦 (𝑥, 𝑦) 𝜕𝑦 𝑎𝑥𝑧 (𝑥, 𝑦)
𝜕𝐴𝑖𝑗 𝜕𝐴𝑖𝑗
= = ⎝𝜕𝑦 𝑎𝑦𝑥 (𝑥, 𝑦) 𝜕𝑦 𝑎𝑦𝑦 (𝑥, 𝑦) 𝜕𝑦 𝑎𝑦𝑧 (𝑥, 𝑦)⎠ ,
𝜕𝑥2 𝜕𝑦
𝜕𝑦 𝑎𝑧𝑥 (𝑥, 𝑦) 𝜕𝑦 𝑎𝑧𝑦 (𝑥, 𝑦) 𝜕𝑦 𝑎𝑧𝑧 (𝑥, 𝑦)
⎛ ⎞
0 0 0
𝜕𝐴𝑖𝑗
= 0 = 0 0 0⎠

𝜕𝑥3
0 0 0

7.2 heat

7.2.1 test_heat_convergence module

Module to gather tests for convergence of decoupled stress system.


This file is executed by pytest to have good CI.
class test_heat_convergence.TestHeatConvergence
Bases: object
Class to bundle all stress convergence tests.
All tests are compared against reference errors.
working_dir = 'tests/heat'
solver_path = '../../src/fenicsR13.py'

7.2. heat 27
B.1. Solver Documentation 139

fenicsR13, Release 1.0

run_solver(inputfile)
Run the solver as subprocess with the given input file.
Test fails if subprocess return Exception or error.
compare_errors(errorsfile, ref_errorsfile)
Check against reference errors. Compares absolute differences.
Absolute Error allowed: 1E-10 Return exception if diff returns with !=0 A comparison for complete
equalness can be obtained with:
subprocess.check_call([
"diff", "-u", "--strip-trailing-cr", errorsfile, ref_errorsfile
], cwd=self.working_dir)

create_meshes()
Create the test meshes. Executed before any test of the class.
Often not needed if meshes are in Git through LFS for reproducability.
test_heat_01_coeffs_p1p1_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
Formulation Coefficients
Elements 𝑃1 𝑃1
Stabilization CIP, 𝛿1 = 1

test_heat_10_coeffs_p2p2_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 10.0
Formulation Coefficients
Elements 𝑃2 𝑃2
Stabilization CIP, 𝛿1 = 1

test_heat_01_coeffs_p2p2_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
Formulation Coefficients
Elements 𝑃2 𝑃2
Stabilization CIP, 𝛿1 = 1

test_heat_01_coeffs_p1p2_nostab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
Formulation Coefficients
Elements 𝑃2 𝑃1
Stabilization CIP, 𝛿1 = 1

28 Chapter 7. Contents
140 B. Solver Related Data

fenicsR13, Release 1.0

test_heat_01_nocoeffs_p1p2_nostab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
Formulation No Coefficients
Elements 𝑃2 𝑃1
Stabilization CIP, 𝛿1 = 1

7.3 stress

7.3.1 test_stress_convergence module

Module to gather tests for convergence of decoupled stress system.


This file is executed by pytest to have good CI.
class test_stress_convergence.TestStressConvergence
Bases: object
Class to bundle all stress convergence tests.
All tests are compared against reference errors.
working_dir = 'tests/stress'
solver_path = '../../src/fenicsR13.py'
run_solver(inputfile)
Run the solver as subprocess with the given input file.
Test fails if subprocess return Exception or error.
compare_errors(errorsfile, ref_errorsfile)
Check against reference errors. Compares absolute differences.
Absolute Error allowed: 1E-10 Return exception if diff returns with !=0 A comparison for complete
equalness can be obtained with:
subprocess.check_call([
"diff", "-u", "--strip-trailing-cr", errorsfile, ref_errorsfile
], cwd=self.working_dir)

create_meshes()
Create the test meshes. Executed before any test of the class.
Often not needed if meshes are in Git through LFS for reproducability.
test_stress_01_nosource_rot_p1p1p1_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
𝑓mass 0
𝑣𝑡1 10.0
Elements 𝑃1 𝑃1 𝑃1
Stabilization CIP, 𝛿2 = 1, 𝛿3 = 0.01

7.3. stress 29
B.1. Solver Documentation 141

fenicsR13, Release 1.0

test_stress_01_source_norot_p1p1p1_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
5𝑅2
𝑓mass 0.4(1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑣𝑡1 0
Elements 𝑃1 𝑃 1 𝑃1
Stabilization CIP, 𝛿2 = 1, 𝛿3 = 0.01

test_stress_01_source_rot_p1p1p1_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
5𝑅2
𝑓mass 0.4(1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑣𝑡1 10.0
Elements 𝑃1 𝑃 1 𝑃1
Stabilization CIP, 𝛿2 = 1, 𝛿3 = 0.01

test_stress_01_source_rot_p1p2p3_nostab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
5𝑅2
𝑓mass 0.4(1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑣𝑡1 10.0
Elements 𝑃1 𝑃 2 𝑃3
Stabilization CIP, 𝛿2 = 1, 𝛿3 = 0.01

test_stress_01_source_rot_p2p2p2_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 0.1
5𝑅2
𝑓mass 0.4(1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑣𝑡1 10.0
Elements 𝑃2 𝑃 2 𝑃2
Stabilization CIP, 𝛿2 = 1, 𝛿3 = 0.01

test_stress_10_source_rot_p1p1p1_stab()
Execute decoupled heat system test and check with reference errors.

Parameter Value
𝐾𝑛 10.0
5𝑅2
𝑓mass 0.4(1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑣𝑡1 10.0
Elements 𝑃1 𝑃 1 𝑃1
Stabilization CIP, 𝛿2 = 1, 𝛿3 = 0.01

30 Chapter 7. Contents
142 B. Solver Related Data

fenicsR13, Release 1.0

7.4 r13

7.4.1 test_r13_convergence module

Module to gather tests for convergence of decoupled stress system.


This file is executed by pytest to have good CI.
class test_r13_convergence.TestR13Convergence
Bases: object
Class to bundle all stress convergence tests.
All tests are compared against reference errors.
working_dir = 'tests/r13'
solver_path = '../../src/fenicsR13.py'
run_solver(inputfile)
Run the solver as subprocess with the given input file.
Test fails if subprocess return Exception or error.
compare_errors(errorsfile, ref_errorsfile)
Check against reference errors. Compares absolute differences.
Absolute Error allowed: 1E-10 Return exception if diff returns with !=0 A comparison for complete
equalness can be obtained with:

subprocess.check_call([
"diff", "-u", "--strip-trailing-cr", errorsfile, ref_errorsfile
], cwd=self.working_dir)

create_meshes()
Create the test meshes. Executed before any test of the class.
Often not needed if meshes are in Git through LFS for reproducability.
test_r13_1_coeffs_sources_rot_noinflow_p1p1p1p1p1_stab()
Execute full linear R13 system test and check with reference errors.
Test case is similar to [WES2019].

7.4. r13 31
B.1. Solver Documentation 143

fenicsR13, Release 1.0

Parameter Value
𝐾𝑛 1.0
5𝑅2
𝑓mass (1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑓heat 0
1
𝜃𝑤 1.0
𝑣𝑡1 10.0
𝑣𝑛1 0
𝑝1𝑤 0
𝜖1𝑤 0
2
𝜃𝑤 0.5
𝑣𝑡2 0.0
𝑣𝑛2 0
𝑝2𝑤 0
𝜖2𝑤 0
Elements 𝑃1 𝑃1 𝑃1 𝑃1 𝑃1
Stabilization CIP: 𝛿1 , 𝛿2 = 1, 𝛿3 = 0.01

test_r13_1_coeffs_sources_rot_noinflow_p2p2p2p2p2_stab()
Execute full linear R13 system test and check with reference errors.
Test case is similar to [WES2019].

Parameter Value
𝐾𝑛 1.0
5𝑅2
𝑓mass (1 − 18𝐾𝑛 2 ) cos(𝜑)

𝑓heat 0
1
𝜃𝑤 1.0
𝑣𝑡1 10.0
𝑣𝑛1 0
𝑝1𝑤 0
𝜖1𝑤 0
2
𝜃𝑤 0.5
𝑣𝑡2 0.0
𝑣𝑛2 0
𝑝2𝑤 0
𝜖2𝑤 0
Elements 𝑃2 𝑃2 𝑃2 𝑃2 𝑃2
Stabilization CIP: 𝛿1 , 𝛿2 = 1, 𝛿3 = 0.01

test_r13_1_coeffs_nosources_norot_inflow_p1p1p1p1p1_stab()
Execute full linear R13 system test and check with reference errors.
Test case is similar to [TOR2017].

32 Chapter 7. Contents
144 B. Solver Related Data

fenicsR13, Release 1.0

Parameter Value
𝐾𝑛 1.0
𝑓mass 0
𝑓heat 0
1
𝜃𝑤 1.0
𝑣𝑡1 0
𝑣𝑛1 0
𝑝1𝑤 0
𝜖1𝑤 10−3
2
𝜃𝑤 2.0
𝑣𝑡2 −1.00 sin(𝜑)
𝑣𝑛2 +1.00 cos(𝜑)
𝑝2𝑤 −0.27 cos(𝜑)
𝜖2𝑤 103
Elements 𝑃1 𝑃1 𝑃1 𝑃1 𝑃1
Stabilization CIP: 𝛿1 , 𝛿2 = 1, 𝛿3 = 0.01

7.5 examples

7.5.1 test_examples module

Module to perform tests for all example cases.


This file is executed by pytest to have good CI.
class test_examples.TestExamples
Bases: object
Class to bundle all examples tests.
All tests are compared against reference errors.
solver_path = '../../src/fenicsR13.py'
run_solver(inputfile, working_dir_)
Run the solver as subprocess with the given input file.
Test fails if subprocess return Exception or error.
create_meshes(working_dir_)
Create the test meshes. Executed before any test of the class.
Often not needed if meshes are in Git through LFS for reproducability.
test_lid_driven_cavity()
Test the lid driven cavity case.
test_channel_flow()
Test the channel flow case and generate table with Kn vs. massflow.
test_knudsen_pump()
Test the knudsen pump case.

7.5. examples 33
B.1. Solver Documentation 145

BIBLIOGRAPHY

[TOR2003] H Struchtrup, M Torrilhon (2003). Regularization of Grad’s 13 moment equations: derivation and linear
analysis.
[STR2005] Struchtrup, H. (2005). Macroscopic transport equations for rarefied gas flows. In Macroscopic Transport
Equations for Rarefied Gas Flows. Springer, Berlin, Heidelberg.
[TOR2018] Torrilhon, M. et al. (2018). “Kinetic Theory of Non-Equilibrium Gas Flows: Theory and Computations”.
Lecture Notes. Indian Institute of Technology Madras.
[WES2019] A. Westerkamp and M. Torrilhon. “Finite Element Methods for the Linear Regularized 13-Moment Equa-
tions Describing Slow Rarefied Gas Flows”. In: Journal of Computational Physics 389 (2019).
[TOR2017] Torrilhon, M. et al. (2017). “Hierarchical Boltzmann simulations and model error estimation”. In: Journal
of Computational Physics 342 (2017), pp. 66–84.

34
146 B. Solver Related Data

PYTHON MODULE INDEX

f
fenicsR13, 14

i
input, 14

m
meshes, 17

p
postprocessor, 18

s
solver, 19

t
tensoroperations, 25
test_examples, 33
test_heat_convergence, 27
test_r13_convergence, 31
test_stress_convergence, 29

35
B.1. Solver Documentation 147

INDEX

Symbols
_Solver__calc_field_errors() (solver.Solver method), 20
_Solver__calc_sf_mean() (solver.Solver method), 21
_Solver__check_bcs() (solver.Solver method), 21
_Solver__createMacroExpr() (solver.Solver method), 21
_Solver__load_exact_solution() (solver.Solver method), 22
_Solver__setup_function_spaces() (solver.Solver method), 23
_Solver__write_discrete_system() (solver.Solver method), 24
_Solver__write_parameters() (solver.Solver method), 25
_Solver__write_solutions() (solver.Solver method), 25
_Solver__write_xdmf() (solver.Solver method), 25
__init__() (input.Input method), 17
__init__() (meshes.H5Mesh method), 18
__init__() (postprocessor.Postprocessor method), 18
__init__() (solver.Solver method), 19

A
assemble() (solver.Solver method), 20

C
calculate_errors() (solver.Solver method), 20
compare_errors() (test_heat_convergence.TestHeatConvergence method), 28
compare_errors() (test_r13_convergence.TestR13Convergence method), 31
compare_errors() (test_stress_convergence.TestStressConvergence method), 29
create_meshes() (test_examples.TestExamples method), 33
create_meshes() (test_heat_convergence.TestHeatConvergence method), 28
create_meshes() (test_r13_convergence.TestR13Convergence method), 31
create_meshes() (test_stress_convergence.TestStressConvergence method), 29

F
fenicsR13 (module), 14

G
gen3d2() (in module tensoroperations), 26
gen3dTF2() (in module tensoroperations), 26
get_from_input() (input.Input method), 17
grad3dOf2() (in module tensoroperations), 27

H
H5Mesh (class in meshes), 17

36
148 B. Solver Related Data

fenicsR13, Release 1.0

I
Input (class in input), 14
input (module), 14

M
main() (in module fenicsR13), 14
meshes (module), 17

P
params (solver.Solver attribute), 20
plot_errors() (postprocessor.Postprocessor method), 19
Postprocessor (class in postprocessor), 18
postprocessor (module), 18
print_information() (in module fenicsR13), 14

R
run_solver() (test_examples.TestExamples method), 33
run_solver() (test_heat_convergence.TestHeatConvergence method), 27
run_solver() (test_r13_convergence.TestR13Convergence method), 31
run_solver() (test_stress_convergence.TestStressConvergence method), 29

S
set_in_input() (input.Input method), 17
solve() (solver.Solver method), 20
Solver (class in solver), 19
solver (module), 19
solver_path (test_examples.TestExamples attribute), 33
solver_path (test_heat_convergence.TestHeatConvergence attribute), 27
solver_path (test_r13_convergence.TestR13Convergence attribute), 31
solver_path (test_stress_convergence.TestStressConvergence attribute), 29
stf3d2() (in module tensoroperations), 25
stf3d3() (in module tensoroperations), 25
sym3d3() (in module tensoroperations), 25

T
tensoroperations (module), 25
test_channel_flow() (test_examples.TestExamples method), 33
test_examples (module), 33
test_heat_01_coeffs_p1p1_stab() (test_heat_convergence.TestHeatConvergence method), 28
test_heat_01_coeffs_p1p2_nostab() (test_heat_convergence.TestHeatConvergence method), 28
test_heat_01_coeffs_p2p2_stab() (test_heat_convergence.TestHeatConvergence method), 28
test_heat_01_nocoeffs_p1p2_nostab() (test_heat_convergence.TestHeatConvergence method), 29
test_heat_10_coeffs_p2p2_stab() (test_heat_convergence.TestHeatConvergence method), 28
test_heat_convergence (module), 27
test_knudsen_pump() (test_examples.TestExamples method), 33
test_lid_driven_cavity() (test_examples.TestExamples method), 33
test_r13_1_coeffs_nosources_norot_inflow_p1p1p1p1p1_stab()
(test_r13_convergence.TestR13Convergence method), 32
test_r13_1_coeffs_sources_rot_noinflow_p1p1p1p1p1_stab() (test_r13_convergence.TestR13Convergence
method), 31
test_r13_1_coeffs_sources_rot_noinflow_p2p2p2p2p2_stab() (test_r13_convergence.TestR13Convergence
method), 32
test_r13_convergence (module), 31

Index 37
B.1. Solver Documentation 149

fenicsR13, Release 1.0

test_stress_01_nosource_rot_p1p1p1_stab() (test_stress_convergence.TestStressConvergence
method), 29
test_stress_01_source_norot_p1p1p1_stab() (test_stress_convergence.TestStressConvergence
method), 29
test_stress_01_source_rot_p1p1p1_stab() (test_stress_convergence.TestStressConvergence method),
30
test_stress_01_source_rot_p1p2p3_nostab() (test_stress_convergence.TestStressConvergence
method), 30
test_stress_01_source_rot_p2p2p2_stab() (test_stress_convergence.TestStressConvergence method),
30
test_stress_10_source_rot_p1p1p1_stab() (test_stress_convergence.TestStressConvergence method),
30
test_stress_convergence (module), 29
TestExamples (class in test_examples), 33
TestHeatConvergence (class in test_heat_convergence), 27
TestR13Convergence (class in test_r13_convergence), 31
TestStressConvergence (class in test_stress_convergence), 29

W
working_dir (test_heat_convergence.TestHeatConvergence attribute), 27
working_dir (test_r13_convergence.TestR13Convergence attribute), 31
working_dir (test_stress_convergence.TestStressConvergence attribute), 29
write() (solver.Solver method), 20
write_content_to_file() (solver.Solver method), 20
write_errors() (postprocessor.Postprocessor method), 19

38 Index
150 B. Solver Related Data

B.2. Solver Source Code


1 import sys
2 import gc
3 import dolfin as df
4
5 import meshes
6 from input import Input
7 from solver import Solver
8 from postprocessor import Postprocessor
9
10 def print_information():
11 print(r"""-> Version: v0.5
12 -> Contact: Lambert Theisen <lambert.theisen@rwth-aachen.de>
13 -> Contact: Manuel Torrilhon <mt@mathcces.rwth-aachen.de>
14 -> Repository: <https://git.rwth-aachen.de/lamBOO/fenicsR13>
15 -> Documentation: <https://lamboo.pages.rwth-aachen.de/fenicsR13/>
16 __ _ ____ _ _____
17 / _| ___ _ __ (_) ___ ___| _ \/ |___ /
18 | |_ / _ \ ’_ \| |/ __/ __| |_) | | |_ \
19 | _| __/ | | | | (__\__ \ _ <| |___) |
20 |_| \___|_| |_|_|\___|___/_| \_\_|____/
21 """)
22
23 def main():
24 print_information()
25
26 # Dolfin settings
27 df.set_log_level(100) # 1: all logs
28 df.parameters["ghost_mode"] = "shared_vertex"
29
30 inputfile = sys.argv[1] if len(sys.argv) == 2 else "input.yml"
31
32 input_file = Input(inputfile)
33 params = input_file.dict
34
35 # Setup parameter study loop
36 if params["parameter_study"]["enable"]:
37 parameter_values = params["parameter_study"]["parameter_values"]
38 parameter_key = params["parameter_study"]["parameter_key"]
39 else:
40 parameter_values = [""]
41 parameter_key = [""]
42 initial_output_folder = params["output_folder"]
43 for parameter_value in parameter_values:
44 input_file.set_in_input(parameter_key, parameter_value)
45 params["output_folder"] = initial_output_folder + str(parameter_value)
46 print("Study", parameter_key, ":", parameter_value)
47
48 # Ususal code:
49 mesh_names = params["meshes"]
50
51 convergence_study = params["convergence_study"]["enable"]
52 show_plot = params["convergence_study"]["plot"]
53
54 data = []
55
56 for p, mesh_name in enumerate(mesh_names):
57
58 print("Mesh: " + mesh_name)
59
60 mesh_name = mesh_names[p]
61
62 current_mesh = meshes.H5Mesh(mesh_name)
63 solver = Solver(params, current_mesh, p)
64
65 solver.assemble()
66 solver.solve()
67 solver.write()
68
69 if convergence_study:
70
71 errors = solver.calculate_errors()
72
73 data.append({
74 "h": current_mesh.mesh.hmax(),
75 **errors
76 })
77
78 if p == len(mesh_names)-1: # after last mesh
79 postp = Postprocessor(data, params["output_folder"])
80 postp.write_errors()
81 postp.plot_errors(show_plot)
82
B.2. Solver Source Code 151

83 solver = None
84 gc.collect()
85
86 if __name__ == ’__main__’:
87 main()

Listing B.1 Solver file: src/fenicsR13.py .

1 from functools import reduce


2 import operator
3 from json import dumps
4 import yaml
5 from cerberus import Validator
6
7 class Input:
8
9 def __init__(self, yaml_file):
10 """Construct the Input class."""
11 with open(yaml_file, "r") as stream:
12 self.dict = yaml.safe_load(stream)
13
14 val = Validator()
15 input_schema = {
16 "meshes": {
17 "type": "list",
18 "required": True,
19 "schema": {"type": "string"}
20 },
21 "nsd": {
22 "type": "integer",
23 "required": True,
24 "allowed": [2]
25 },
26 "mode": {
27 "type": "string",
28 "required": True,
29 "allowed": ["heat", "stress", "r13"]
30 },
31 "use_coeffs": {
32 "type": "boolean",
33 "required": True,
34 },
35 "kn": {
36 "type": "float",
37 "required": True,
38 "min": 0.000000001
39 },
40 "xi_tilde": {
41 "type": "float",
42 "required": True,
43 "min": 0.000000001
44 },
45 "bcs": {
46 "type": "dict",
47 "required": True,
48 "keysrules": {"type": "integer"},
49 "valuesrules": {
50 "type": "dict",
51 "schema": {
52 "theta_w": {
53 "anyof": [{"type": "string"}, {"type": "float"}],
54 "required": True,
55 },
56 "u_t_w": {
57 "anyof": [{"type": "string"}, {"type": "float"}],
58 "required": True
59 },
60 "u_n_w": {
61 "anyof": [{"type": "string"}, {"type": "float"}],
62 "required": True
63 },
64 "p_w": {
65 "anyof": [{"type": "string"}, {"type": "float"}],
66 "required": True
67 },
68 "epsilon_w": {
69 "anyof": [{"type": "string"}, {"type": "float"}],
70 "required": True
71 },
72 }
73 }
74 },
75 "heat_source": {
76 "anyof": [{"type": "string"}, {"type": "float"}],
152 B. Solver Related Data

77 "required": True,
78 },
79 "mass_source": {
80 "anyof": [{"type": "string"}, {"type": "float"}],
81 "required": True,
82 },
83 "postprocessing": {
84 "type": "dict",
85 "required": True,
86 "schema": {
87 "write_pdfs": {
88 "type": "boolean",
89 "required": True
90 },
91 "massflow": {
92 "type": "list",
93 "required": True,
94 "schema": {"type": "integer"}
95 },
96 }
97 },
98 "parameter_study": {
99 "type": "dict",
100 "required": True,
101 "schema": {
102 "enable": {
103 "type": "boolean",
104 "required": True
105 },
106 "parameter_key": {
107 "type": "list",
108 "required": True,
109 },
110 "parameter_values": {
111 "type": "list",
112 "required": True,
113 },
114 }
115 },
116 "convergence_study": {
117 "type": "dict",
118 "required": True,
119 "schema": {
120 "enable": {
121 "type": "boolean",
122 "required": True
123 },
124 "exact_solution": {
125 "type": "string",
126 "required": True
127 },
128 "plot": {
129 "type": "boolean",
130 "required": True
131 },
132 "write_systemmatrix": {
133 "type": "boolean",
134 "required": True
135 },
136 "rescale_pressure": {
137 "type": "boolean",
138 "required": True
139 },
140 "relative_error": {
141 "type": "boolean",
142 "required": True
143 },
144 }
145 },
146 "output_folder": {
147 "type": "string",
148 "required": True,
149 },
150 "stabilization": {
151 "type": "dict",
152 "required": True,
153 "keysrules": {"type": "string", "regex": "cip"},
154 "valuesrules": {
155 "type": "dict",
156 "schema": {
157 "enable": {
158 "type": "boolean",
159 "required": True
160 },
161 "delta_1": {
B.2. Solver Source Code 153

162 "type": "float",


163 "required": True
164 },
165 "delta_2": {
166 "type": "float",
167 "required": True
168 },
169 "delta_3": {
170 "type": "float",
171 "required": True
172 },
173 }
174 }
175 },
176 "elements": {
177 "type": "dict",
178 "required": True,
179 "schema": {
180 "theta": {
181 "type": "dict",
182 "required": True,
183 "schema": {
184 "shape": {
185 "type": "string",
186 "required": True
187 },
188 "degree": {
189 "type": "integer",
190 "required": True,
191 "min": 0
192 }
193 }
194 },
195 "s": {
196 "type": "dict",
197 "required": True,
198 "schema": {
199 "shape": {
200 "type": "string",
201 "required": True
202 },
203 "degree": {
204 "type": "integer",
205 "required": True,
206 "min": 0
207 }
208 }
209 },
210 "p": {
211 "type": "dict",
212 "required": True,
213 "schema": {
214 "shape": {
215 "type": "string",
216 "required": True
217 },
218 "degree": {
219 "type": "integer",
220 "required": True,
221 "min": 0
222 }
223 }
224 },
225 "u": {
226 "type": "dict",
227 "required": True,
228 "schema": {
229 "shape": {
230 "type": "string",
231 "required": True
232 },
233 "degree": {
234 "type": "integer",
235 "required": True,
236 "min": 0
237 }
238 }
239 },
240 "sigma": {
241 "type": "dict",
242 "required": True,
243 "schema": {
244 "shape": {
245 "type": "string",
246 "required": True
154 B. Solver Related Data

247 },
248 "degree": {
249 "type": "integer",
250 "required": True,
251 "min": 0
252 }
253 }
254 },
255 }
256 }
257 }
258
259 if not val.validate(self.dict, input_schema):
260 print(val.errors)
261 raise Exception("Parsing error")
262
263 print("Input:\n" + dumps(self.dict, indent=None))
264
265 def get_from_input(self, map_list):
266 try:
267 return reduce(operator.getitem, map_list, self.dict)
268 except:
269 raise Exception("Dict has no entry with the key:", map_list)
270
271 def set_in_input(self, map_list, value):
272 self.get_from_input(map_list[:-1])[map_list[-1]] = value

Listing B.2 Solver file: src/input.py .

1 """
2 Module to store the mesh classes.
3
4 Currently, the only mesh format is h5.
5 """
6
7 import os
8 import dolfin as df
9
10 class H5Mesh:
11
12 def __init__(self, h5_file):
13 if not os.path.isfile(h5_file):
14 raise Exception(f"{h5_file} not found")
15
16 self.mesh = df.Mesh()
17
18 hdf = df.HDF5File(self.mesh.mpi_comm(), h5_file, "r")
19
20 hdf.read(self.mesh, "/mesh", False)
21 dim = self.mesh.topology().dim()
22
23 self.subdomains = df.MeshFunction("size_t", self.mesh, dim)
24 hdf.read(self.subdomains, "/subdomains")
25
26 self.boundaries = df.MeshFunction("size_t", self.mesh, dim - 1)
27 hdf.read(self.boundaries, "/boundaries")

Listing B.3 Solver file: src/meshes.py .

1 # pylint: disable=invalid-name
2
3 """
4 Module to store the postprocessor classes.
5
6 Currently, only Psotprocessor class present.
7 """
8
9 from itertools import chain
10
11 import csv
12 from math import sqrt, ceil, log, log10
13 import numpy as np
14 import matplotlib.pyplot as plt
15
16 class Postprocessor:
17
18 def __init__(self, data, output_folder):
19 self.data = data
20 self.output_folder = output_folder
21
22 def plot_errors(self, show_popup):
B.2. Solver Source Code 155

23 filename = "convergence_plot_" + self.output_folder + ".pdf"


24
25 if not show_popup:
26 plt.switch_backend(’agg’)
27 plt.figure(num=None, figsize=(16, 9), dpi=100)
28
29 data = self.data
30 h = [df["h"] for df in data]
31
32 raw_data = [key for key in data[0] if key != "h"]
33 num_fields = len(raw_data)
34
35 plt_sidelength_x = ceil(sqrt(num_fields))
36 plt_sidelength_y = plt_sidelength_x
37 while plt_sidelength_y * (plt_sidelength_x-1) >= num_fields:
38 plt_sidelength_x -= 1
39
40 for i, key in enumerate(raw_data):
41 field = [df[key] for df in data]
42 plot_i = plt.subplot(plt_sidelength_x, plt_sidelength_y, (i+1))
43 for j, etype in enumerate(field[0]):
44 values = [df[etype] for df in field]
45
46 # Plot actual data
47 plt.loglog(h, values, "-o", label=etype)
48
49 # Add slope marker
50 use_top = j == 1
51 bot = np.array([
52 [h[-1], 0.5*values[-1]],
53 [h[-2], 0.5*values[-1]],
54 [h[-2], 0.5*values[-2]]
55 ])
56 top = np.array([
57 [h[-1], 2.0*values[-1]],
58 [h[-2], 2.0*values[-2]],
59 [h[-1], 2.0*values[-2]]
60 ])
61 tria = top if use_top else bot
62 slope_marker = plt.Polygon(
63 tria, color=plot_i.get_lines()[-1].get_color(),
64 alpha=1.5, fill=False
65 )
66 plot_i.add_patch(slope_marker)
67
68 conv_rate = (
69 (log(values[-2]) - log(values[-1]))
70 / (log(h[-2]) - log(h[-1]))
71 )
72 anchor_x = h[-1] if use_top else h[-2]
73 anchor_y = (
74 10**(
75 (log10(2.0*values[-2])+log10(2.0*values[-1]))/2
76 ) if use_top
77 else 10**((log10(0.5*values[-2])+log10(0.5*values[-1]))/2)
78 )
79 h_align = "left" if use_top else "right"
80 plot_i.text(
81 anchor_x, anchor_y, str(round(conv_rate, 2)),
82 alpha=1.0, ha=h_align, va="center"
83 )
84
85 # Add order slopes
86 plt.loglog(h, np.array(2*np.power(h, 1)), "--", label="O(h^1)")
87 plt.loglog(h, np.array(0.02*np.power(h, 2)), "--", label="O(h^2)")
88 plt.loglog(h, np.array(0.02*np.power(h, 3)), "--", label="O(h^3)")
89
90 # Add information
91 plt.xlabel("max(h)")
92 plt.ylabel("Error")
93 plt.title(key)
94 plt.legend(loc=’lower right’)
95
96 plt.tight_layout()
97
98 output_path = self.output_folder + "/" + filename
99 print("Write {}".format(output_path))
100 plt.savefig(output_path, dpi=150)
101 if show_popup:
102 plt.show()
103
104 def write_errors(self):
105 filename = "errors.csv"
106 output_path = self.output_folder + "/" + filename
107 data = self.data
156 B. Solver Related Data

108
109 with open(output_path, mode=’w’) as file:
110 print("Write {}".format(output_path))
111 writer = csv.writer(file, delimiter=’,’, quotechar=’"’)
112
113 # header
114 writer.writerow(
115 ["h"] + list(
116 chain.from_iterable([
117 [first+"_"+second for second in data[0].get(first, "")]
118 for first in [
119 first for first in data[0] if first != "h"
120 ]
121 ])
122 )
123 )
124
125 # data of all runs
126 for run in data:
127 writer.writerow(
128 [run["h"]] + list(chain.from_iterable([
129 [run[field]["L_2"], run[field]["l_inf"]]
130 for field in [f for f in run if f != "h"]
131 ]))
132 )

Listing B.4 Solver file: src/postprocessor.py .

1 import os
2 import copy
3 import time as time_module
4 import dolfin as df
5 import ufl
6 import numpy as np
7 import tensoroperations as to
8
9
10 class Solver:
11
12 def __init__(self, params, mesh, time):
13 """Initialize solver and setup variables from input parameters."""
14 self.params = params #: Doctest
15 self.mesh = mesh.mesh
16 self.boundaries = mesh.boundaries
17 self.cell = self.mesh.ufl_cell()
18 self.time = time
19 self.mode = params["mode"]
20 self.use_coeffs = params["use_coeffs"]
21 self.kn = params["kn"]
22 self.xi_tilde = params["xi_tilde"]
23 self.use_cip = self.params["stabilization"]["cip"]["enable"]
24 self.delta_1 = self.params["stabilization"]["cip"]["delta_1"]
25 self.delta_2 = self.params["stabilization"]["cip"]["delta_2"]
26 self.delta_3 = self.params["stabilization"]["cip"]["delta_3"]
27
28 self.write_pdfs = self.params["postprocessing"]["write_pdfs"]
29 self.massflow = self.params["postprocessing"]["massflow"]
30
31 # Create boundary field and sources expressions
32 self.bcs = copy.deepcopy(self.params["bcs"])
33 for edge_id in self.bcs:
34 for field in self.bcs[edge_id].keys():
35 self.bcs[edge_id][field] = self.__createMacroExpr(
36 self.bcs[edge_id][field]
37 )
38 self.heat_source = self.__createMacroExpr(self.params["heat_source"])
39 self.mass_source = self.__createMacroExpr(self.params["mass_source"])
40
41 self.exact_solution = self.params["convergence_study"]["exact_solution"]
42 self.write_systemmatrix = self.params["convergence_study"][
43 "write_systemmatrix"
44 ]
45 self.rescale_p = self.params["convergence_study"]["rescale_pressure"]
46 self.relative_error = self.params["convergence_study"][
47 "relative_error"
48 ]
49 self.output_folder = self.params["output_folder"] + "/"
50 self.var_ranks = {
51 "theta": 0,
52 "s": 1,
53 "p": 0,
54 "u": 1,
55 "sigma": 2,
56 }
B.2. Solver Source Code 157

57 self.elems = {
58 "theta": None,
59 "s": None,
60 "p": None,
61 "u": None,
62 "sigma": None,
63 }
64 self.fspaces = {
65 "theta": None,
66 "s": None,
67 "p": None,
68 "u": None,
69 "sigma": None,
70 }
71 self.mxd_elems = {
72 "heat": None,
73 "stress": None,
74 "r13": None,
75 }
76 self.mxd_fspaces = {
77 "heat": None,
78 "stress": None,
79 "r13": None,
80 }
81 self.form_a = None
82 self.form_b = None
83 self.sol = {
84 "theta": None,
85 "s": None,
86 "p": None,
87 "u": None,
88 "sigma": None,
89 }
90 self.esol = {
91 "theta": None,
92 "s": None,
93 "p": None,
94 "u": None,
95 "sigma": None,
96 }
97 self.errors = {}
98
99 def __createMacroExpr(self, cpp_string):
100 R = df.Expression("sqrt(pow(x[0],2)+pow(x[1],2))", degree=2)
101 phi = df.Expression("atan2(x[1],x[0])", degree=2)
102 kn = self.kn
103 return df.Expression(
104 str(cpp_string),
105 degree=2,
106 kn=kn,
107 phi=phi,
108 R=R
109 )
110
111 def __setup_function_spaces(self):
112 # Setup elements for all fields
113 cell = self.cell
114 msh = self.mesh
115 for var in self.elems:
116 e = self.params["elements"][var]["shape"]
117 deg = self.params["elements"][var]["degree"]
118 if self.var_ranks[var] == 0:
119 self.elems[var] = df.FiniteElement(e, cell, deg)
120 elif self.var_ranks[var] == 1:
121 self.elems[var] = df.VectorElement(e, cell, deg)
122 elif self.var_ranks[var] == 2:
123 self.elems[var] = df.TensorElement(e, cell, deg, symmetry=True)
124 self.fspaces[var] = df.FunctionSpace(msh, self.elems[var])
125
126 # Bundle elements per mode into ‘mxd_elems‘ dict
127 # 1) heat
128 heat_elems = [self.elems["theta"], self.elems["s"]]
129 self.mxd_elems["heat"] = df.MixedElement(heat_elems)
130 self.mxd_fspaces["heat"] = df.FunctionSpace(
131 msh, self.mxd_elems["heat"]
132 )
133 # 2) stress
134 stress_elems = [self.elems["p"], self.elems["u"], self.elems["sigma"]]
135 self.mxd_elems["stress"] = df.MixedElement(stress_elems)
136 self.mxd_fspaces["stress"] = df.FunctionSpace(
137 msh, self.mxd_elems["stress"]
138 )
139 # 3) r13
140 r13_elems = heat_elems + stress_elems
141 self.mxd_elems["r13"] = df.MixedElement(r13_elems)
158 B. Solver Related Data

142 self.mxd_fspaces["r13"] = df.FunctionSpace(


143 msh, self.mxd_elems["r13"]
144 )
145
146 def __check_bcs(self):
147 boundary_ids = self.boundaries.array()
148 bcs_specified = list(self.bcs.keys())
149
150 for edge_id in boundary_ids:
151 if not edge_id in [0] + bcs_specified: # inner zero allowed
152 raise Exception("Mesh edge id {} has no bcs!".format(edge_id))
153
154 def assemble(self):
155 # Check if all mesh boundaries have bcs presibed frm input
156 self.__check_bcs()
157
158 # Setup required function spaces
159 self.__setup_function_spaces()
160
161 # Get local variables
162 mesh = self.mesh
163 boundaries = self.boundaries
164 bcs = self.bcs
165 kn = df.Constant(self.kn)
166 xi_tilde = df.Constant(self.xi_tilde)
167 delta_1 = df.Constant(self.delta_1)
168 delta_2 = df.Constant(self.delta_2)
169 delta_3 = df.Constant(self.delta_3)
170
171 # Normal and tangential components
172 # - tangential (tx,ty) = (-ny,nx) = perp(n) only for 2D
173 n = df.FacetNormal(mesh)
174 t = ufl.perp(n)
175
176 # Define custom measeasure for boundaries
177 df.ds = df.Measure("ds", domain=mesh, subdomain_data=boundaries)
178 df.dS = df.Measure("dS", domain=mesh, subdomain_data=boundaries)
179
180 h = df.CellDiameter(mesh)
181 h_avg = (h("+") + h("-"))/2.0 # pylint: disable=not-callable
182
183 # Setup function spaces
184 w_heat = self.mxd_fspaces["heat"]
185 w_stress = self.mxd_fspaces["stress"]
186 w_r13 = self.mxd_fspaces["r13"]
187 if self.mode == "r13":
188 (theta, s, p, u, sigma) = df.TrialFunctions(w_r13)
189 (kappa, r, q, v, psi) = df.TestFunctions(w_r13)
190 else:
191 # Pure heat or pure stress: setup all functions..
192 (theta, s) = df.TrialFunctions(w_heat)
193 (kappa, r) = df.TestFunctions(w_heat)
194 (p, u, sigma) = df.TrialFunctions(w_stress)
195 (q, v, psi) = df.TestFunctions(w_stress)
196
197 # Setup projections
198 s_n = df.dot(s, n)
199 r_n = df.dot(r, n)
200 s_t = df.dot(s, t)
201 r_t = df.dot(r, t)
202 sigma_nn = df.dot(sigma*n, n)
203 psi_nn = df.dot(psi*n, n)
204 sigma_tt = df.dot(sigma*t, t)
205 psi_tt = df.dot(psi*t, t)
206 sigma_nt = df.dot(sigma*n, t)
207 psi_nt = df.dot(psi*n, t)
208
209 # Setup source functions
210 f_heat = self.heat_source
211 f_mass = self.mass_source
212
213 if self.mode == "r13":
214 cpl = 1.0
215 else:
216 cpl = 0.0
217
218 # Setup both weak forms
219 if self.use_coeffs:
220 a1 = (
221 + 12/5 * kn * df.inner(to.stf3d2(df.grad(s)), df.grad(r))
222 + 2/3 * (1/kn) * df.inner(s, r)
223 - (5/2) * theta * df.div(r)
224 + cpl * df.dot(df.div(sigma), r) # SAME RESULTS
225 # - cpl * df.inner(sigma, df.grad(r)) # SAME RESULTS
226 ) * df.dx + (
B.2. Solver Source Code 159

227 + (
228 + 5/(4*xi_tilde) * s_n
229 - cpl * 5/8 * sigma_nn
230 # + cpl * sigma_nn # SAME RESULTS
231 ) * r_n
232 + (
233 + 11/10 * xi_tilde * s_t
234 + cpl * 1/10 * xi_tilde * s_t
235 - cpl * 1/2 * sigma_nt
236 # + cpl * sigma_nt # SAME RESULTS
237 ) * r_t
238 ) * df.ds
239 l1 = sum([
240 - 5.0/2.0 * r_n * bcs[bc]["theta_w"] * df.ds(bc)
241 for bc in bcs.keys()
242 ])
243
244 a2 = - (df.div(s) * kappa) * df.dx
245 l2 = - (f_heat * kappa) * df.dx
246
247 a3 = (
248 + 2 * kn * df.inner(
249 to.stf3d3(to.grad3dOf2(to.gen3dTF2(sigma))),
250 to.grad3dOf2(to.gen3dTF2(psi))
251 )
252 + (1/kn) * df.inner(to.gen3dTF2(sigma), to.gen3dTF2(psi))
253 - 2 * df.dot(u, df.div(psi))
254 + cpl * 4/5 * df.inner(to.stf3d2(df.grad(s)), psi)
255 ) * df.dx + (
256 + (
257 + 21/10 * xi_tilde * sigma_nn
258 + cpl * 3/20 * xi_tilde * sigma_nn
259 - cpl * 3/10 * s_n
260 ) * psi_nn
261 + 2 * xi_tilde * (
262 (sigma_tt + (1/2)*sigma_nn)*(psi_tt + (1/2)*psi_nn)
263 )
264 + (
265 + (2/xi_tilde) * sigma_nt
266 - cpl * 2/5 * s_t
267 ) * psi_nt
268 ) * df.ds + 2 * sum([
269 bcs[bc]["epsilon_w"] * (p + sigma_nn) * psi_nn * df.ds(bc)
270 for bc in bcs.keys()
271 ])
272 l3 = sum([
273 - 2.0 * psi_nt * bcs[bc]["u_t_w"] * df.ds(bc)
274 - 2.0 * (
275 - bcs[bc]["epsilon_w"] * bcs[bc]["p_w"]
276 + bcs[bc]["u_n_w"]
277 ) * psi_nn * df.ds(bc)
278 for bc in bcs.keys()
279 ])
280
281 a4 = (
282 + df.dot(df.div(sigma), v)
283 + df.dot(df.grad(p), v)
284 ) * df.dx
285 l4 = + df.Constant(0) * df.div(v) * df.dx
286
287 a5 = + (
288 df.dot(u, df.grad(q))
289 ) * df.dx - sum([
290 bcs[bc]["epsilon_w"] * (p + sigma_nn) * q * df.ds(bc)
291 for bc in bcs.keys()
292 ])
293 l5 = - (f_mass * q) * df.dx + sum([
294 (
295 - bcs[bc]["epsilon_w"] * bcs[bc]["p_w"]
296 + bcs[bc]["u_n_w"]
297 ) * q * df.ds(bc)
298 for bc in bcs.keys()
299 ])
300 else:
301 a1 = (
302 kn * df.inner(to.stf3d2(df.grad(s)), df.grad(r))
303 + (1/kn) * df.inner(s, r)
304 - theta * df.div(r)
305 ) * df.dx + (
306 + 1/(xi_tilde) * s_n * r_n
307 + xi_tilde * s_t * r_t
308 ) * df.ds
309 a2 = - (df.div(s) * kappa) * df.dx
310 l1 = sum([
311 - 1 * r_n * bcs[bc]["theta_w"] * df.ds(bc)
160 B. Solver Related Data

312 for bc in bcs.keys()


313 ])
314 l2 = - (f_heat * kappa) * df.dx
315
316 # stabilization
317 if self.use_cip:
318 stab_heat = - (
319 delta_1 * h_avg**3 *
320 df.jump(df.grad(theta), n) * df.jump(df.grad(kappa), n)
321 ) * df.dS
322
323 stab_stress = (
324 + delta_2 * h_avg**3 *
325 df.dot(df.jump(df.grad(u), n), df.jump(df.grad(v), n))
326 - delta_3 * h_avg *
327 df.jump(df.grad(p), n) * df.jump(df.grad(q), n)
328 ) * df.dS
329 else:
330 stab_heat = 0
331 stab_stress = 0
332
333 # Combine all equations
334 if self.mode == "heat":
335 self.form_a = a1 + a2 + stab_heat
336 self.form_b = l1 + l2
337 elif self.mode == "stress":
338 self.form_a = a3 + a4 + a5 + stab_stress
339 self.form_b = l3 + l4 + l5
340 elif self.mode == "r13":
341 self.form_a = a1 + a2 + stab_heat + a3 + a4 + a5 + stab_stress
342 self.form_b = l1 + l2 + l3 + l4 + l5
343
344 def solve(self):
345 if self.mode == "heat":
346 w = self.mxd_fspaces["heat"]
347 elif self.mode == "stress":
348 w = self.mxd_fspaces["stress"]
349 elif self.mode == "r13":
350 w = self.mxd_fspaces["r13"]
351
352 print("Start solving system..")
353 start_t = time_module.time()
354 sol = df.Function(w)
355 df.solve(
356 self.form_a == self.form_b, sol, [],
357 solver_parameters={"linear_solver": "mumps"}
358 )
359 end_t = time_module.time()
360 print("Finished solving system in: {}".format(str(end_t - start_t)))
361
362 if self.mode == "heat":
363 (self.sol["theta"], self.sol["s"]) = sol.split()
364 elif self.mode == "stress":
365 (self.sol["p"], self.sol["u"], self.sol["sigma"]) = sol.split()
366 elif self.mode == "r13":
367 (
368 self.sol["theta"], self.sol["s"],
369 self.sol["p"], self.sol["u"], self.sol["sigma"]
370 ) = sol.split()
371
372 if self.mode == "stress" or self.mode == "r13":
373 if self.rescale_p:
374 # Scale pressure to have zero mean
375 p_i = df.interpolate(self.sol["p"], self.fspaces["p"])
376 mean_p_value = self.__calc_sf_mean(p_i)
377 mean_p_fct = df.Function(self.fspaces["p"])
378 mean_p_fct.assign(df.Constant(mean_p_value))
379 p_i.assign(p_i - mean_p_fct)
380 self.sol["p"] = p_i
381
382 # Calculate mass flows
383 for bc_id in self.massflow:
384 if bc_id not in self.boundaries.array():
385 raise Exception("Massflow: {} is no boundary.".format(bc_id))
386 n = df.FacetNormal(self.mesh)
387 mass_flow_rate = df.assemble(
388 df.inner(self.sol["u"], n)*df.ds(bc_id)
389 )
390 print("mass flow rate of BC", bc_id, ":", mass_flow_rate)
391 self.write_content_to_file("massflow_"+str(bc_id), mass_flow_rate)
392
393 def __load_exact_solution(self):
394 if self.mode == "heat" or self.mode == "r13":
395
396 with open(self.exact_solution, "r") as file:
B.2. Solver Source Code 161

397 exact_solution_cpp_code = file.read()


398
399 esol = df.compile_cpp_code(exact_solution_cpp_code)
400
401 self.esol["theta"] = df.CompiledExpression(
402 esol.Temperature(), degree=2
403 )
404
405 self.esol["s"] = df.CompiledExpression(
406 esol.Heatflux(), degree=2
407 )
408 if self.mode == "stress" or self.mode == "r13":
409
410 with open(self.exact_solution, "r") as file:
411 exact_solution_cpp_code = file.read()
412
413 esol = df.compile_cpp_code(exact_solution_cpp_code)
414
415 self.esol["p"] = df.CompiledExpression(
416 esol.Pressure(), degree=2
417 )
418
419 self.esol["u"] = df.CompiledExpression(
420 esol.Velocity(), degree=2
421 )
422
423 self.esol["sigma"] = df.CompiledExpression(
424 esol.Stress(), degree=2
425 )
426
427 def __calc_sf_mean(self, scalar_function):
428 v = scalar_function.compute_vertex_values()
429 mean = np.mean(v)
430 return mean
431
432 def __calc_field_errors(self, field_, field_e_, v_field, name_):
433 field_e_i = df.interpolate(field_e_, v_field)
434 field_i = df.interpolate(field_, v_field)
435
436 difference = df.project(field_e_i - field_i, v_field)
437 self.__write_xdmf("difference_{}".format(name_), difference, False)
438
439 dofs = len(field_e_i.split()) or 1
440
441 if dofs == 1:
442 # scalar
443 errs_f_L2 = [df.errornorm(field_e_i, field_i, "L2")]
444 errs_v_linf = [
445 np.max(
446 np.abs(
447 field_e_i.compute_vertex_values()
448 - field_i.compute_vertex_values()
449 )
450 )
451 ]
452 else:
453 # vector or tensor
454 errs_f_L2 = [df.errornorm(
455 field_e_i.split()[i], field_i.split()[i], "L2"
456 ) for i in range(dofs)]
457 errs_v_linf = [
458 np.max(
459 np.abs(
460 field_e_i.split()[i].compute_vertex_values()
461 - field_i.split()[i].compute_vertex_values()
462 )
463 )
464 for i in range(dofs)
465 ]
466
467 if self.relative_error:
468 if dofs == 1:
469 # scalar
470 max_esols = [
471 np.max(np.abs(field_e_i.compute_vertex_values())) or 1
472 ]
473 else:
474 # vector or tensor
475 max_esols = [
476 np.max(
477 np.abs(field_e_i.split()[i].compute_vertex_values())
478 )
479 for i in range(dofs)
480 ]
481 errs_f_L2 = [x/y for x, y in zip(errs_f_L2, max_esols)]
162 B. Solver Related Data

482 errs_v_linf = [x/y for x, y in zip(errs_v_linf, max_esols)]


483
484 print("Error " + str(name_) + " L_2:", errs_f_L2)
485 print("Error " + str(name_) + " l_inf:", errs_v_linf)
486
487 self.__write_xdmf(name_ + "_e", field_e_i, False)
488
489 return [{
490 "L_2": errs_f_L2[i],
491 "l_inf": errs_v_linf[i],
492 } for i in range(dofs)]
493
494 def calculate_errors(self):
495 print("Calculate errors..")
496
497 self.__load_exact_solution()
498
499 if self.mode == "heat" or self.mode == "r13":
500 se = self.__calc_field_errors(
501 self.sol["theta"], self.esol["theta"],
502 self.fspaces["theta"], "theta"
503 )
504 ve = self.__calc_field_errors(
505 self.sol["s"], self.esol["s"],
506 self.fspaces["s"], "s"
507 )
508 ers = self.errors
509 ers["theta"] = se[0]
510 ers["sx"] = ve[0]
511 ers["sy"] = ve[1]
512 if self.mode == "stress" or self.mode == "r13":
513 se = self.__calc_field_errors(
514 self.sol["p"], self.esol["p"],
515 self.fspaces["p"], "p"
516 )
517 ve = self.__calc_field_errors(
518 self.sol["u"], self.esol["u"],
519 self.fspaces["u"], "u"
520 )
521 te = self.__calc_field_errors(
522 self.sol["sigma"], self.esol["sigma"],
523 self.fspaces["sigma"], "sigma"
524 )
525 ers = self.errors
526 ers["p"] = se[0]
527 ers["ux"] = ve[0]
528 ers["uy"] = ve[1]
529 ers["sigmaxx"] = te[0]
530 ers["sigmaxy"] = te[1]
531 ers["sigmayy"] = te[2]
532
533 return self.errors
534
535 def write_content_to_file(self, filename, content):
536 """Write content to a file in the output folder."""
537 path = self.output_folder + "/" + filename
538 os.makedirs(os.path.dirname(path), exist_ok=True)
539 with open(path, mode=’w’) as file:
540 print("Write: {}".format(path))
541 file.write(str(content))
542
543 def write(self):
544 print("Write fields..")
545
546 self.__write_solutions()
547 self.__write_parameters()
548 if self.write_systemmatrix:
549 self.__write_discrete_system()
550
551 def __write_solutions(self):
552 sols = self.sol
553 for field in sols:
554 if sols[field] is not None:
555 self.__write_xdmf(field, sols[field], self.write_pdfs)
556
557 def __write_parameters(self):
558 # Interpolation setup
559 el_str = "Lagrange"
560 deg = 1
561 el = df.FiniteElement(el_str, degree=deg, cell=self.cell)
562 V = df.FunctionSpace(self.mesh, el)
563
564 # Heat source
565 f_heat = df.interpolate(self.heat_source, V)
566 self.__write_xdmf("f_heat", f_heat, False)
B.2. Solver Source Code 163

567
568 # Mass source
569 f_mass = df.interpolate(self.mass_source, V)
570 self.__write_xdmf("f_mass", f_mass, False)
571
572 def __write_discrete_system(self):
573 file_ending = ".mat"
574 np.savetxt(
575 self.output_folder + "A_{}".format(self.time) + file_ending,
576 df.assemble(self.form_a).array()
577 )
578 np.savetxt(
579 self.output_folder + "b_{}".format(self.time) + file_ending,
580 df.assemble(self.form_b)
581 )
582
583 def __write_xdmf(self, name, field, write_pdf):
584 fname_xdmf = (
585 self.output_folder + name + "_" + str(self.time) + ".xdmf"
586 )
587 with df.XDMFFile(self.mesh.mpi_comm(), fname_xdmf) as file:
588 for degree in range(5): # test until degree five
589 # Writing symmetric tensors crashes.
590 # Therefore project symmetric tensor in nonsymmetric space
591 # This is only a temporary fix, see:
592 # https://fenicsproject.discourse.group/t/...
593 # ...writing-symmetric-tensor-function-fails/1136
594 el_symm = df.TensorElement(
595 df.FiniteElement(
596 "Lagrange", df.triangle, degree+1
597 ), symmetry=True
598 ) # symmetric tensor element
599 el_sol = field.ufl_function_space().ufl_element()
600 if el_sol == el_symm:
601 # Remove symmetry with projection
602 field = df.project(
603 field, df.TensorFunctionSpace(
604 self.mesh, "Lagrange", degree+1
605 )
606 )
607 break
608
609 field.rename(name, name)
610
611 print("Write {}".format(fname_xdmf))
612 file.write(field, self.time)
613
614 if write_pdf:
615 import matplotlib.pyplot as plt
616 plt.switch_backend(’agg’)
617 dimension = len(field.value_shape())
618
619 if dimension < 2:
620 # skip tensors
621 fname_pdf = (
622 self.output_folder + name + "_" + str(self.time) + ".pdf"
623 )
624 plot = df.plot(field)
625 plt.colorbar(plot)
626 plt.xlabel("x")
627 plt.ylabel("y")
628 plt.title(field)
629 print("Write {}".format(fname_pdf))
630 plt.savefig(fname_pdf, dpi=150)
631 plt.close()
632
633 if dimension > 0:
634 # skip scalars
635 components = len(field.split())
636 indexMap = {
637 1: {
638 1: "x",
639 2: "y"
640 },
641 2: {
642 1: "xx",
643 2: "xy",
644 3: "yx",
645 4: "yy",
646 }
647 }
648 for i in range(components):
649 fieldname = name + "_" + str(indexMap[dimension][i+1])
650 fname_pdf = (
651 self.output_folder + fieldname
164 B. Solver Related Data

652 + "_" + str(self.time) + ".pdf"


653 )
654 plot = df.plot(field.split()[i])
655 plt.colorbar(plot)
656 plt.xlabel("x")
657 plt.ylabel("y")
658 plt.title(fieldname)
659 print("Write {}".format(fname_pdf))
660 plt.savefig(fname_pdf, dpi=150)
661 plt.close()

Listing B.5 Solver file: src/solver.py .

1 import dolfin as df
2 import ufl
3
4 def stf3d2(rank2_2d):
5 symm = 1/2 * (rank2_2d + ufl.transpose(rank2_2d))
6 return (
7 symm
8 - (1/3) * ufl.tr(symm) * ufl.Identity(2)
9 )
10
11 def sym3d3(rank3_3d):
12 i, j, k = ufl.indices(3)
13 symm_ijk = 1/6 * (
14 # all permutations
15 + rank3_3d[i, j, k]
16 + rank3_3d[i, k, j]
17 + rank3_3d[j, i, k]
18 + rank3_3d[j, k, i]
19 + rank3_3d[k, i, j]
20 + rank3_3d[k, j, i]
21 )
22 return ufl.as_tensor(symm_ijk, (i, j, k))
23
24 def stf3d3(rank3_3d):
25 i, j, k, l = ufl.indices(4)
26 delta = df.Identity(3)
27
28 sym_ijk = sym3d3(rank3_3d)[i, j, k]
29 traces_ijk = 1/5 * (
30 + sym3d3(rank3_3d)[i, l, l] * delta[j, k]
31 + sym3d3(rank3_3d)[l, j, l] * delta[i, k]
32 + sym3d3(rank3_3d)[l, l, k] * delta[i, j]
33 )
34 tracefree_ijk = sym_ijk - traces_ijk
35 return ufl.as_tensor(tracefree_ijk, (i, j, k))
36
37 def gen3dTF2(rank2_2d):
38 return df.as_tensor([
39 [rank2_2d[0, 0], rank2_2d[0, 1], 0],
40 [rank2_2d[1, 0], rank2_2d[1, 1], 0],
41 [0, 0, -rank2_2d[0, 0]-rank2_2d[1, 1]]
42 ])
43
44 def gen3d2(rank2_2d):
45 return df.as_tensor([
46 [rank2_2d[0, 0], rank2_2d[0, 1], 0],
47 [rank2_2d[1, 0], rank2_2d[1, 1], 0],
48 [0, 0, 0]
49 ])
50
51 def grad3dOf2(rank2_3d):
52 grad2d = df.grad(rank2_3d)
53 dim3 = df.as_tensor([
54 [0, 0, 0],
55 [0, 0, 0],
56 [0, 0, 0],
57 ])
58 grad3d = df.as_tensor([
59 grad2d[:, :, 0], # pylint: disable=unsubscriptable-object
60 grad2d[:, :, 1], # pylint: disable=unsubscriptable-object
61 dim3[:, :]
62 ])
63 return grad3d

Listing B.6 Solver file: src/tensoroperations.py .


C. Solver Input Files for Application
Cases
1 output_folder: results_lid_driven_cavity
2 meshes:
3 - lid5.h5
4 elements:
5 theta:
6 shape: Lagrange
7 degree: 1
8 s:
9 shape: Lagrange
10 degree: 1
11 p:
12 shape: Lagrange
13 degree: 1
14 u:
15 shape: Lagrange
16 degree: 1
17 sigma:
18 shape: Lagrange
19 degree: 1
20 stabilization:
21 cip:
22 enable: True
23 delta_1: 1.0
24 delta_2: 1.0
25 delta_3: 0.1
26 nsd: 2
27 mode: r13
28 use_coeffs: True
29 kn: 0.08
30 xi_tilde: 1.0
31 heat_source: 0
32 mass_source: 0
33 bcs:
34 3000: # upper slip wall
35 theta_w: 1
36 u_t_w: -1
37 u_n_w: 0
38 p_w: 0
39 epsilon_w: 0
40 3100: # left bottom right noslip walls
41 theta_w: 1
42 u_t_w: 0
43 u_n_w: 0
44 p_w: 0
45 epsilon_w: 0
46 convergence_study:
47 enable: False
48 exact_solution: esols/01_coeffs.cpp
49 plot: False # to avoid error exit code due to $DISPLAY
50 write_systemmatrix: False
51 rescale_pressure: True
52 relative_error: True
53 postprocessing:
54 write_pdfs: True
55 massflow: []
56 parameter_study:
57 enable: False
58 parameter_key: []
59 parameter_values: []

Listing C.1 Lid-driven cavity input file: input.yml

1 output_folder: results_channel_flow
166 C. Solver Input Files for Application Cases

2 meshes:
3 - channel5.h5
4 elements:
5 theta:
6 shape: Lagrange
7 degree: 1
8 s:
9 shape: Lagrange
10 degree: 1
11 p:
12 shape: Lagrange
13 degree: 1
14 u:
15 shape: Lagrange
16 degree: 1
17 sigma:
18 shape: Lagrange
19 degree: 1
20 stabilization:
21 cip:
22 enable: True
23 delta_1: 1.0
24 delta_2: 1.0
25 delta_3: 0.1
26 nsd: 2
27 mode: r13
28 use_coeffs: True
29 kn: 1.0
30 xi_tilde: 1.0
31 heat_source: 0
32 mass_source: 0
33 bcs:
34 3001: # bot
35 theta_w: 1
36 u_t_w: 0
37 u_n_w: 0
38 p_w: 0
39 epsilon_w: pow(10,-3)
40 3002: # right
41 theta_w: 1
42 u_t_w: 0
43 u_n_w: 0
44 p_w: 0
45 epsilon_w: pow(10,+3)
46 3003: # top
47 theta_w: 1
48 u_t_w: 0
49 u_n_w: 0
50 p_w: 0
51 epsilon_w: pow(10,-3)
52 3004: # left
53 theta_w: 1
54 u_t_w: 0
55 u_n_w: 0
56 p_w: 1
57 epsilon_w: pow(10,+3)
58 convergence_study:
59 enable: False
60 exact_solution: esols/01_coeffs.cpp
61 plot: False # to avoid error exit code due to $DISPLAY
62 write_systemmatrix: False
63 rescale_pressure: True
64 relative_error: True
65 postprocessing:
66 write_pdfs: True
67 massflow: [3002]
68 parameter_study:
69 enable: True
70 parameter_key: ["kn"]
71 parameter_values: [0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4,
1.5, 1.6, 1.7, 1.8, 1.9, 2.0]

Listing C.2 Channel flow input file: input.yml

1 output_folder: results_knudsen_pump
2 meshes:
3 - knudsen_pump3.h5
4 elements:
5 theta:
6 shape: Lagrange
7 degree: 1
8 s:
9 shape: Lagrange
10 degree: 1
167

11 p:
12 shape: Lagrange
13 degree: 1
14 u:
15 shape: Lagrange
16 degree: 1
17 sigma:
18 shape: Lagrange
19 degree: 1
20 stabilization:
21 cip:
22 enable: True
23 delta_1: 1.0
24 delta_2: 1.0
25 delta_3: 0.1
26 nsd: 2
27 mode: r13
28 use_coeffs: True
29 kn: 0.1
30 xi_tilde: 1.0
31 heat_source: 0
32 mass_source: 0
33 bcs:
34 3010: # inner top
35 theta_w: x[0]/1.0
36 u_t_w: 0
37 u_n_w: 0
38 p_w: 0
39 epsilon_w: 0
40 3011: # inner left
41 theta_w: -(atan2(x[1],-(x[0]+1.0)))/(M_PI/2.0)
42 u_t_w: 0
43 u_n_w: 0
44 p_w: 0
45 epsilon_w: 0
46 3012: # inner bottom
47 theta_w: -x[0]/1.0
48 u_t_w: 0
49 u_n_w: 0
50 p_w: 0
51 epsilon_w: 0
52 3013: # inner right
53 theta_w: (atan2(x[1],(x[0]-1.0)))/(M_PI/2.0)
54 u_t_w: 0
55 u_n_w: 0
56 p_w: 0
57 epsilon_w: 0
58 3020: # outer top
59 theta_w: x[0]/1.0
60 u_t_w: 0
61 u_n_w: 0
62 p_w: 0
63 epsilon_w: 0
64 3021: # outer left
65 theta_w: -(atan2(x[1],-(x[0]+1.0)))/(M_PI/2.0)
66 u_t_w: 0
67 u_n_w: 0
68 p_w: 0
69 epsilon_w: 0
70 3022: # outer bottom
71 theta_w: -x[0]/1.0
72 u_t_w: 0
73 u_n_w: 0
74 p_w: 0
75 epsilon_w: 0
76 3023: # outer right
77 theta_w: (atan2(x[1],(x[0]-1.0)))/(M_PI/2.0)
78 u_t_w: 0
79 u_n_w: 0
80 p_w: 0
81 epsilon_w: 0
82 convergence_study:
83 enable: False
84 exact_solution: esols/01_coeffs.cpp
85 plot: False # to avoid error exit code due to $DISPLAY
86 write_systemmatrix: False
87 rescale_pressure: True
88 relative_error: True
89 postprocessing:
90 write_pdfs: True
91 massflow: []
92 parameter_study:
93 enable: False
94 parameter_key: []
95 parameter_values: []
168 C. Solver Input Files for Application Cases

Listing C.3 Knudsen pump input file: input.yml


D. FEniCS Example Programs
The following FEniCS examples can be found in [18] and are included for reproducibil-
ity.

D.1. Poisson Problem Using First-Order Lagrange


Elements
1 from dolfin import *
2
3 # Create mesh and define function space
4 mesh = UnitSquareMesh(32, 32)
5 V = FunctionSpace(mesh, "Lagrange", 1)
6
7 # Define Dirichlet boundary (x = 0 or x = 1)
8 def boundary(x):
9 return x[0] < DOLFIN_EPS or x[0] > 1.0 - DOLFIN_EPS
10
11 # Define boundary condition
12 u0 = Constant(0.0)
13 bc = DirichletBC(V, u0, boundary)
14
15 # Define variational problem
16 u = TrialFunction(V)
17 v = TestFunction(V)
18 f = Expression(
19 "10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)",
20 degree=2
21 )
22 g = Expression("sin(5*x[0])", degree=2)
23 a = inner(grad(u), grad(v))*dx
24 L = f*v*dx + g*v*ds
25
26 # Compute solution
27 u = Function(V)
28 solve(a == L, u, bc)
29
30 # Save solution in VTK format
31 file = File("poisson.pvd")
32 file << u
33
34 # Plot solution
35 import matplotlib.pyplot as plt
36 plot(u)
37 plt.show()

Listing D.1 FEniCS demo program to solve the Poisson problem using linear P1 elements
(demo_poisson.py in [14, 18]).

D.2. Stokes Problem Using Taylor–Hood Elements


170 D. FEniCS Example Programs

1 import matplotlib.pyplot as plt


2 from dolfin import *
3 # Load mesh and subdomains
4 mesh = Mesh("dolfin_fine.xml.gz")
5 sub_domains = MeshFunction("size_t", mesh, "dolfin_fine_subdomains.xml.gz")
6
7 # Define function spaces
8 P2 = VectorElement("Lagrange", mesh.ufl_cell(), 2)
9 P1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1)
10 TH = P2 * P1
11 W = FunctionSpace(mesh, TH)
12
13 # No-slip boundary condition for velocity
14 # x1 = 0, x1 = 1 and around the dolphin
15 noslip = Constant((0, 0))
16 bc0 = DirichletBC(W.sub(0), noslip, sub_domains, 0)
17 # Inflow boundary condition for velocity
18 # x0 = 1
19 inflow = Expression(("-sin(x[1]*pi)", "0.0"), degree=2)
20 bc1 = DirichletBC(W.sub(0), inflow, sub_domains, 1)
21 # Collect boundary conditions
22 bcs = [bc0, bc1]
23
24 # Define variational problem
25 (u, p) = TrialFunctions(W)
26 (v, q) = TestFunctions(W)
27 f = Constant((0, 0))
28 a = (inner(grad(u), grad(v)) - div(v)*p + q*div(u))*dx
29 L = inner(f, v)*dx
30 # Compute solution
31 w = Function(W)
32 solve(a == L, w, bcs)
33
34 # # Split the mixed solution using a shallow copy
35 (u, p) = w.split()
36 # Save solution in VTK format
37 ufile_pvd = File("velocity.pvd")
38 ufile_pvd << u
39 pfile_pvd = File("pressure.pvd")
40 pfile_pvd << p
41
42 # Plot solution
43 plt.figure()
44 plot(u, title="velocity")
45 plt.figure()
46 plot(p, title="pressure")
47 # Display plots
48 plt.show()

Listing D.2 FEniCS demo program to solve the Stokes problem using Taylor–Hood elements
P2 P1 (demo_stokes-taylor-hood.py in [15, 18]).
Bibliography
[1] D. Abel. Regelungstechnik und Ergänzungen (Höhere Regelungstechnik). Um-
druck zur Vorlesung. Verlag Mainz, 2018. URL: https : / / www . verlag -
mainz . de / regelungstechnik - und - erg % C3 % A4nzungen - h % C3 % B6here -
regelungstechnik.html (visited on 09/10/2019).
[2] M. S. Alnæs, A. Logg, K. B. Ølgaard, M. E. Rognes, and G. N. Wells. “Unified
Form Language: A Domain-Specific Language for Weak Formulations of Partial
Differential Equations”. In: ACM Transactions on Mathematical Software
(TOMS) 40.2 (2014), p. 9. URL: https : / / doi . org / 10 . 1145 / 1731022 .
1731030 (visited on 08/28/2019).
[3] M. Alnæs, J. Blechta, J. Hake, A. Johansson, B. Kehlet, A. Logg, C. Richardson,
J. Ring, M. E. Rognes, and G. N. Wells. “The FEniCS Project Version 1.5”.
In: Archive of Numerical Software 3.100 (2015). URL: https://doi.org/10.
11588/ans.2015.100.20553 (visited on 08/21/2019).
[4] K. Aoki, P. Degond, L. Mieussens, M. Nishioka, and S. Takata. “Numerical
Simulation of a Knudsen Pump Using the Effect of Curvature of the Channel”.
In: Rarefied Gas Dynamics (2007), pp. 1079–1084. URL: https : / / www .
math.u- bordeaux.fr/~lmieusse/PAGE_WEB/PUBLICATIONS/2007/admnt-
final.pdf (visited on 09/13/2019).
[5] D. N. Arnold and A. Logg. “Periodic Table of the Finite Elements”. In: SIAM
News 47.9 (2014), pp. 1, 8–9. URL: https://sinews.siam.org/Current-
Issue/Issue-Archives/Issue-Archives-ListView/PID/2282/mcat/2279/
evl/0/TagID/247?TagName=Volume-47-%7C-Number-9-%7C-November-2014
(visited on 08/21/2019).
[6] M. Behr. “Finite Elements in Fluids”. Lecture Notes WS18/19. RWTH
Aachen University, 2018. URL: https : / / www . cats . rwth - aachen . de /
cms/CATS/Studium/Lehre/Wintersemester/~ogqr/Finite- Elemente- in-
Fluiddynamik/?lidx=1 (visited on 09/03/2019).
[7] R. B. Bird, W. E. S. Stewart, and E. N. Lightfoot. Transport Phenom-
ena. John Wiley & Sons, 2006. URL: https : / / www . wiley . com / en -
us / Transport + Phenomena % 2C + Revised + 2nd + Edition - p - 9780470115398
(visited on 08/25/2019).
172 Bibliography

[8] E. Bombieri, S. Cook, P. Deligne, C. L. Fefferman, J. Gray, A. Jaffe, J. Milnor,


A. Wiles, and E. Witten. The Millennium Prize Problems. Ed. by J. A.
Carlson, A. Jaffe, and A. Wiles. Providence, RI: American Mathematical
Society, 2006. URL: http://www.claymath.org/publications/special-
editions/millennium-prize-problems (visited on 08/24/2019).
[9] Boost C++ Libraries. Boost, 2019. URL: https://www.boost.org/ (visited
on 09/06/2019).
[10] E. Burman and P. Hansbo. “Edge Stabilization for the Generalized Stokes
Problem: A Continuous Interior Penalty Method”. In: Computer Methods
in Applied Mechanics and Engineering 195.19 (2006), pp. 2393–2410. URL:
https://doi.org/10.1016/j.cma.2005.05.009 (visited on 09/06/2019).
[11] Z. Cai. Research Alumni Program: Bright Minds Interview. RWTH Aachen Uni-
versity, 2016. URL: http://www.rwth-aachen.de/cms/root/Studium/Nach-
dem - Studium / Alumni / Infos - Forscher - Alumni / Kluge - Koepfe / ~khtw /
Zhenning-Cai/?lidx=1 (visited on 08/29/2019).
[12] T. A. Davis. “Algorithm 832: UMFPACK V4.3 — An Unsymmetric-Pattern
Multifrontal Method”. In: ACM Transactions on Mathematical Software 30.2
(2004), pp. 196–199. URL: http://doi.acm.org/10.1145/992200.992206
(visited on 09/06/2019).
[13] DOLFIN Documentation. Version 2019.1.0. FEniCS Project, 2019. URL:
https://fenicsproject.org/docs/dolfin/2019.1.0/python (visited on
09/03/2019).
[14] DOLFIN Documentation: Poisson Equation. Version 2019.1.0. FEniCS Project,
2019. URL: https://fenicsproject.org/docs/dolfin/2019.1.0/python/
demos/poisson/demo_poisson.py.html (visited on 09/03/2019).
[15] DOLFIN Documentation: Stokes Equations With Taylor-Hood Elements. Ver-
sion 1.6.0. FEniCS Project, 2016. URL: https://fenicsproject.org/docs/
dolfin/1.6.0/python/demo/documented/stokes- taylor- hood/python/
documentation.html (visited on 09/04/2019).
[16] J. Donea and A. Huerta. Finite Element Methods for Flow Problems. John
Wiley & Sons, 2003. URL: https://doi.org/10.1002/0470013826 (visited
on 08/22/2019).
[17] L. C. Evans. Partial Differential Equations. 2nd ed. Vol. 19. Graduate
Studies in Mathematics. American Mathematical Society, 2010. URL: http:
//dx.doi.org/10.1090/gsm/019 (visited on 09/03/2019).
[18] FEniCS Project: Bitbucket Repositories. FEniCS Project. URL: https://
bitbucket.org/fenics-project/ (visited on 08/28/2019).
Bibliography 173

[19] C. Geller, S. Märtens, L. Reiher, and L. Theisen. “Numerical Study of Fluid-


Structure Interaction in a Piston Ring Pack”. Project Thesis. RWTH Aachen
University, 2017, to appear. URL: http://www.cats.rwth-aachen.de/cms/
CATS/Der-Lehrstuhl/Team/~ouvj/vonDanwitz-Max (visited on 08/22/2019).
[20] C. Geuzaine and J.-F. Remacle. “Gmsh: A 3-D Finite Element Mesh Generator
With Built-In Pre- and Post-Processing Facilities”. In: International Journal
for Numerical Methods in Engineering 79.11 (2009), pp. 1309–1331. URL:
https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.2579 (visited
on 09/06/2019).
[21] GNU Lesser General Public License (LGPLv3). Version 3. Free Software
Foundation. 2007. URL: https://www.gnu.org/licenses/lgpl-3.0.en.
html (visited on 08/26/2019).
[22] Google Trends. Google Search Trends: FEniCS Project, deal.II, Code_Aster,
FreeFem++, Elmer (2010–today). 2019. URL: https://trends.google.com/
trends/explore?date=2010- 01- 01%202019- 08- 26&q=%2Fm%2F04cv70s,
%2Fm % 2F0knv5d0 , %2Fg % 2F11bc5d3gvw , %2Fm % 2F0gyrzn0 , %2Fm % 2F07xd10
(visited on 08/27/2019).
[23] Google Trends. Google Search Trends: Python, C++, C (2010–today). 2019.
URL: https : / / trends . google . com / trends / explore ? date = 2010 - 01 -
01%202019-08-26&q=%2Fm%2F05z1_,%2Fm%2F0jgqg,%2Fm%2F01t6b (visited
on 08/27/2019).
[24] H. Grad. “Principles of the Kinetic Theory of Gases”. In: Thermodynamik
der Gase / Thermodynamics of Gases. Ed. by S. Flügge. Berlin, Heidelberg:
Springer Berlin Heidelberg, 1958, pp. 205–294. URL: https://doi.org/10.
1007/978-3-642-45892-7_3 (visited on 09/10/2019).
[25] C. J. Greenshields. OpenFOAM User Guide 5.0. The OpenFOAM Foundation
Ltd. 2017. URL: http : / / foam . sourceforge . net / docs / Guides - a4 /
OpenFOAMUserGuide-A4.pdf (visited on 09/15/2019).
[26] D. Ham. EU Regional School: Automated Simulation from Equations to Com-
putation with Firedrake. RWTH Aachen University, 2019. URL: https://www.
youtube.com/watch?v=WOifRLs08Lo (visited on 08/28/2019).
[27] J. Hoffman, J. Jansson, and N. Jansson. “FEniCS-HPC: Automated Predictive
High-Performance Finite Element Computing with Applications in Aerodynam-
ics”. In: International Conference on Parallel Processing and Applied Mathe-
matics. Springer. 2015, pp. 356–365. URL: https://doi.org/10.1007/978-
3-319-32149-3_34 (visited on 08/28/2019).
[28] J. Hoffman and A. Logg. DOLFIN: Dynamic Object oriented Library for FINite
element computation. Tech. rep. 2002. URL: http://urn.kb.se/resolve?
urn=urn%3Anbn%3Ase%3Akth%3Adiva-235788 (visited on 08/28/2019).
174 Bibliography

[29] S. Kandlikar, S. Garimella, D. Li, S. Colin, and M. R. King. Heat Transfer


and Fluid Flow in Minichannels and Microchannels. 2nd ed. Butterworth-
Heinemann, 2013. URL: https://doi.org/10.1016/C2011- 0- 07521- X
(visited on 08/24/2019).
[30] R. C. Kirby. “Algorithm 839: FIAT, A New Paradigm for Computing Finite
Element Basis Functions”. In: ACM Transactions on Mathematical Software
(TOMS) 30.4 (2004), pp. 502–516. URL: https://doi.org/10.1145/1039813.
1039820 (visited on 08/28/2019).
[31] R. C. Kirby and A. Logg. “A Compiler for Variational Forms”. In: ACM
Transactions on Mathematical Software (TOMS) 32.3 (2006), pp. 417–444. URL:
http://dx.doi.org/10.1145/1163641.1163644 (visited on 08/28/2019).
[32] H. P. Langtangen, A. Logg, and A. Tveito. Solving PDEs in Python: The
FEniCS Tutorial I. Springer International Publishing, 2016. URL: https :
//doi.org/10.1007/978-3-319-52462-7 (visited on 08/27/2019).
[33] V. Leontidis, J. Chen, L. Baldas, and S. Colin. “Numerical Design of a Knudsen
Pump With Curved Channels Pperating in the Slip Flow Regime”. In: Heat
and Mass Transfer 50.8 (2014), pp. 1065–1080. URL: https://doi.org/10.
1007/s00231-014-1314-4 (visited on 09/13/2019).
[34] A. Logg. FEniCS’15: Implementing Mathematics: Domain-Specific Languages
and Automated Computing. Imperial College London, 2015. URL: https :
//www.youtube.com/watch?v=BzvJHsR_RSI (visited on 08/28/2019).
[35] A. Logg, K.-A. Mardal, G. Wells, et al. Automated Solution of Differential
Equations by the Finite Element Method: The FEniCS Book. Ed. by A. Logg,
K.-A. Mardal, and G. Wells. Lecture Notes in Computational Science and
Engineering. Berlin Heidelberg: Springer-Verlag, 2012. URL: https://doi.
org/10.1007/978-3-642-23099-8 (visited on 08/21/2019).
[36] A. Logg and G. N. Wells. “DOLFIN: Automated Finite Element Comput-
ing”. In: ACM Transactions on Mathematical Software (TOMS) 37.2 (2010),
p. 20. URL: http://dx.doi.org/10.1145/1731022.1731030 (visited on
08/28/2019).
[37] I. Müller and W. H. Müller. Fundamentals of Thermodynamics and Aappli-
cations: With Historical Annotations and Many Citations from Avogadro to
Zermelo. Springer-Verlag Berlin Heidelberg, 2009. URL: https://doi.org/
10.1007/978-3-540-74648-5 (visited on 08/25/2019).
[38] I. Müller and T. Ruggeri. Extended Thermodynamics. 1st ed. Vol. 37. Springer
Tracts in Natural Philosophy. Springer-Verlag New York, 1993. URL: https:
//doi.org/10.1007/978-1-4684-0447-0 (visited on 08/25/2019).
Bibliography 175

[39] I. Müller and T. Ruggeri. Rational Extended Thermodynamics. 2nd ed. Vol. 37.
Springer Tracts in Natural Philosophy. Springer-Verlag New York, 1998. URL:
https://doi.org/10.1007/978-1-4612-2210-1 (visited on 08/25/2019).
[40] K. Müller. “Computing Rarefied External Flows Using Extended Fluid Dynam-
ics”. Diploma Thesis. Swiss Federal Institute of Technology Zurich (ETH
Zürich), 2010. URL: http : / / www . csc . kth . se / ~kasparm / #outline -
container-sec-4 (visited on 08/30/2019).
[41] R. Rajput. Thermal Engineering. Laxmi Publications Pvt Limited, 2010.
URL: https : / / books . google . de / books ? id = 65gxCX2dC84C (visited on
08/25/2019).
[42] A. S. Rana and H. Struchtrup. “Thermodynamically Admissible Boundary
Conditions for the Regularized 13 Moment Equations”. In: Physics of Fluids
28.2 (2016), p. 027105. URL: https://doi.org/10.1063/1.4941293 (visited
on 09/10/2019).
[43] A. Rana, M. Torrilhon, and H. Struchtrup. “A Robust Numerical Method
for the R13 Equations of Rarefied Gas Dynamics: Application to Lid Driven
Cavity”. In: Journal of Computational Physics 236 (2013), pp. 169–186. URL:
https://doi.org/10.1016/j.jcp.2012.11.023 (visited on 08/25/2019).
[44] F. Rathgeber, D. A. Ham, L. Mitchell, M. Lange, F. Luporini, A. T. McRae,
G.-T. Bercea, G. R. Markall, and P. H. Kelly. “Firedrake: Automating the
Finite Element Method by Composing Abstractions”. In: ACM Transactions
on Mathematical Software (TOMS) 43.3 (2017), p. 24. URL: https://doi.
org/10.1145/2998441 (visited on 08/28/2019).
[45] C. N. Richardson and G. N. Wells. Parallel Scaling of DOLFIN on ARCHER.
2015. URL: https://doi.org/10.6084/m9.figshare.1304537.v1 (visited
on 08/28/2019).
[46] Roadmap 2019–2020. FEniCS Project, 2019. URL: https://fenicsproject.
org/fenics-project-roadmap-2019/ (visited on 08/28/2019).
[47] H. Schade and K. Neemann. Tensoranalysis. De Gruyter Lehrbuch. De Gruyter,
2009. URL: https://www.degruyter.com/viewbooktoc/product/40403
(visited on 08/25/2019).
[48] R. Schärer. “Entropy-Based Moment Closures for Rarefied Gases and Plasmas”.
PhD Thesis. RWTH Aachen University, 2016. URL: http://www.mathcces.
rwth-aachen.de/3teaching/00projects/archive (visited on 08/24/2019).
[49] B. Stamm. “Stabilization Strategies for Discontinuous Galerkin Methods”. PhD
Thesis. Lausanne: IACS, 2008. URL: http://dx.doi.org/10.5075/epfl-
thesis-4135 (visited on 09/06/2019).
176 Bibliography

[50] H. Struchtrup. Macroscopic Transport Equations for Rarefied Gas Flows. Inter-
action of Mechanics and Mathematics. Springer-Verlag Berlin Heidelberg, 2005.
URL: https://doi.org/10.1007/3-540-32386-4 (visited on 08/25/2019).
[51] H. Struchtrup and P. Taheri. “Macroscopic Transport Models for Rarefied Gas
Flows: A Brief Review”. In: IMA Journal of Applied Mathematics 76.5 (2011),
pp. 672–697. URL: https://doi.org/10.1093/imamat/hxr004 (visited on
08/24/2019).
[52] H. Struchtrup and M. Torrilhon. “Regularization of Grad’s 13 Moment Equa-
tions: Derivation and Linear Analysis”. In: Physics of Fluids 15.9 (2003),
pp. 2668–2680. URL: https://doi.org/10.1063/1.1597472 (visited on
08/25/2019).
[53] L. Theisen. “Automated Boundary Layer Mesh Generation for Simulation
of Convective Cooling”. Bachelor Thesis. RWTH Aachen University, 2018,
to appear. URL: http : / / www . mathcces . rwth - aachen . de / 3teaching /
00projects/archive (visited on 09/19/2019).
[54] L. Theisen. P3 Finite Element Basis Functions on 2-Simplex. 2019. URL:
https://figshare.com/articles/P3_Finite_Element_Basis_Functions_
on_2-Simplex_/9767021/1 (visited on 09/04/2019).
[55] L. Theisen. “Shear-Slip Mesh Update Method for Compressible Flow Simu-
lations Involving Rotating Sub-Domains”. Seminar Thesis. RWTH Aachen
University, 2019, to appear. URL: http://www.mathcces.rwth-aachen.de/
3teaching/0classes/ceswseminar/bisher (visited on 08/22/2019).
[56] L. Theisen, L. Reiher, and C. Geller. Mathematische Modelle der Natur- und In-
genieurwissenschaften (WS18/19): Hausaufgabe 5. RWTH Aachen University,
2019. URL: http://www.mathcces.rwth-aachen.de/3teaching/0classes/
pdemodeling (visited on 08/22/2019).
[57] L. Theisen and M. Torrilhon. fenicsR13: Solver Documentation. RWTH
Aachen University, 2019. URL: https : / / lamboo . pages . rwth - aachen .
de/fenicsR13/ (visited on 09/15/2019).
[58] L. Theisen and M. Torrilhon. fenicsR13: Solver Repository. RWTH Aachen
University, 2019. URL: https://git.rwth-aachen.de/lamBOO/fenicsR13
(visited on 09/15/2019).
[59] M. Torrilhon and H. Struchtrup. “Regularized 13-Moment Equations: Shock
Structure Calculations and Comparison to Burnett Models”. In: Journal of
Fluid Mechanics 513 (2004), pp. 171–198. URL: https://doi.org/10.1017/
S0022112004009917 (visited on 08/25/2019).
Bibliography 177

[60] M. Torrilhon. “Das Riemann-Problem in der Erweiterten Thermodynamik”.


Diploma Thesis. Technische Universität Berlin, 1999. URL: www.mathcces.
rwth-aachen.de/torrilhon/Torrilhon_Diplomarbeit1999.pdf (visited on
08/25/2019).
[61] M. Torrilhon. “Mathematical Models in Science and Engineering (PDEs)”.
Lecture Notes WS18/19. RWTH Aachen University, 2018. URL: http://www.
mathcces.rwth-aachen.de/3teaching/0classes/pdemodeling (visited on
08/22/2019).
[62] M. Torrilhon. “Modeling Nonequilibrium Gas Flow Based on Moment Equa-
tions”. In: Annual Review of Fluid Mechanics 48 (2016), pp. 429–458. URL:
https : / / doi . org / 10 . 1146 / annurev - fluid - 122414 - 034259 (visited on
08/24/2019).
[63] M. Torrilhon. “Regularization of Grad’s 13-Moment-Equations in Kinetic Gas
Theory”. RTO-EN-AVT-194. RTO Technical Report. NATO Research and
Technology Organisation, 2011. URL: https : / / apps . dtic . mil / docs /
citations/ADA587233 (visited on 08/10/2019).
[64] M. Torrilhon, V. K. Gupta, and P. Shukla. “Kinetic Theory of Non-Equilibrium
Gas Flows: Theory and Computations”. Lecture Notes. Indian Institute of
Technology Madras, 2018. URL: http://www.gian.iitkgp.ac.in/ccourses/
approvecourses2 (visited on 08/20/2019).
[65] M. Torrilhon and N. Sarna. “Hierarchical Boltzmann Simulations and Model
Error Estimation”. In: Journal of Computational Physics 342 (2017), pp. 66–
84. URL: https : / / doi . org / 10 . 1016 / j . jcp . 2017 . 04 . 041 (visited on
08/30/2019).
[66] M. Torrilhon and H. Struchtrup. “Boundary Conditions for Regularized 13-
Moment-Equations for Micro-Channel-Flows”. In: Journal of Computational
Physics 227.3 (2008), pp. 1982–2011. URL: https://doi.org/10.1016/j.
jcp.2007.10.006 (visited on 09/12/2019).
[67] UFL Documentation: Form Language. Version 2019.1.0. FEniCS Project,
2019. URL: https://fenics.readthedocs.io/projects/ufl/en/latest/
manual/form_language.html (visited on 09/05/2019).
[68] A. Westerkamp and M. Torrilhon. “Finite Element Methods for the Linear
Regularized 13-Moment Equations Describing Slow Rarefied Gas Flows”. In:
Journal of Computational Physics 389 (2019), pp. 1–21. URL: https://doi.
org/10.1016/j.jcp.2019.03.022 (visited on 08/30/2019).
[69] A. Westerkamp. “A Continuous Interior Penalty Method for the Linear Reg-
ularized 13-Moment Equations Describing Rarefied Gas Flows”. PhD The-
sis. RWTH Aachen University, 2017. URL: http://www.mathcces.rwth-
aachen.de/3teaching/00projects/archive (visited on 08/30/2019).
178 Bibliography

[70] A. Westerkamp. “Finite Element Discretizations for Extended Gas Dynam-


ics”. Diploma Thesis. RWTH Aachen University, 2012. URL: http://www.
mathcces . rwth - aachen . de / 3teaching / 00projects / archive (visited on
08/30/2019).
[71] A. Westerkamp and M. Torrilhon. “Curvature-Induced Instability of a Stokes-
Like Problem with Non-Standard Boundary Conditions”. In: Applied Numerical
Mathematics 121 (2017), pp. 96–114. URL: https://doi.org/10.1016/j.
apnum.2017.06.012 (visited on 08/30/2019).
[72] A. Westerkamp and M. Torrilhon. “Stabilization Techniques in Finite Element
Discretizations for Moment Approximations”. In: AIP Conference Proceedings.
Vol. 1628. 1. AIP. 2014, pp. 1016–1023. URL: https://doi.org/10.1063/1.
4902705 (visited on 08/30/2019).
[73] Wikipedia — The Free Encyklopedia. Integration by Parts. 2019. URL: https:
//en.wikipedia.org/wiki/Integration_by_parts (visited on 09/03/2019).
[74] Wikipedia — The Free Encyklopedia. Knudsen Paradox. 2019. URL: https:
//en.wikipedia.org/wiki/Knudsen_paradox (visited on 09/18/2019).
[75] Wikipedia — The Free Encyklopedia. Microtechnology. 2019. URL: https:
//en.wikipedia.org/wiki/Microtechnology (visited on 08/29/2019).
[76] Wikipedia — The Free Encyklopedia. Perfect Gas. 2019. URL: https :
//en.wikipedia.org/wiki/Perfect_gas (visited on 08/07/2019).
[77] Wikipedia — The Free Encyklopedia. Stokes Flow. 2019. URL: https :
//en.wikipedia.org/wiki/Stokes_flow (visited on 09/04/2019).
[78] C. Winkelmann. “Interior Penalty Finite Element Approximation of Navier-
Stokes Equations and Application to Free Surface Flows”. PhD Thesis. Lau-
sanne: IACS, 2007. URL: http://dx.doi.org/10.5075/epfl-thesis-3971
(visited on 09/06/2019).

Das könnte Ihnen auch gefallen