Reference documentation for deal.II version Git 8a320d4 2018-02-19 08:48:02 +0100
The step-1 tutorial program

1. Introduction
2. The commented program
1. Results
2. The plain program

# Introduction

Since this is the first tutorial program, let us comment first on how this tutorial and the rest of the deal.II documentation is supposed to work. The documentation for deal.II comes essentially at three different levels:

• The tutorial: This is a collection of programs that shows how deal.II is used in practice. It doesn't typically discuss individual functions at the level of individual arguments, but rather wants to give the big picture of how things work together. In other words, it discusses "concepts": what are the building blocks of deal.II and how are they used together in finite element programs.
• The manual: This is the documentation of every single class and every single (member) function in deal.II. You get there if, for example, you click on the "Main page" or "Classes" tab at the top of this page. This is the place where you would look up what the second argument of Triangulation::create_triangulation_compatibility means, to give just one slightly obscure example. You need this level of documentation for when you know what you want to do, but forgot how exactly the function was named, what its arguments are, or what it returns. Note that you also get into the manual whenever you read through the tutorial and click on any of the class or function names, i.e. the tutorial contains a great many links into the manual for whenever you need a more detailed description of a function or class. On the other hand, the manual is not a good place to learn deal.II since it gives you a microscopic view of things without telling you how a function might fit into the bigger picture.
• Modules: These are groups of classes and functions that work together or have related functionality. If you click on the "Modules" tab at the top of this page, you end up on a page that lists a number of such groups. Each module discusses the underlying principles of these classes; for example, the Sparsity patterns module talks about all sorts of different issues related to storing sparsity patterns of matrices. This is documentation at an intermediate level: they give you an overview of what's there in a particular area. For example when you wonder what finite element classes exist, you would take a look at the Finite element space descriptions module. The modules are, of course, also cross-linked to the manual (and, at times, to the tutorial); if you click on a class name, say on Triangulation, would will also at the very top right under the class name get a link to the modules this class is a member of if you want to learn more about its context.

Let's come back to the tutorial, since you are looking at the first program (or "step") of it. Each tutorial program is subdivided into the following sections:

1. Introduction: This is a discussion of what the program does, including the mathematical model, and what programming techniques are new compared to previous tutorial programs.
2. The commented program: An extensively documented listing of the source code. Here, we often document individual lines, or blocks of code, and discuss what they do, how they do it, and why. The comments frequently reference the introduction, i.e. you have to understand what the program wants to achieve (a goal discussed in the introduction) before you can understand how it intends to get there.
3. Results: The output of the program, with comments and interpretation. This section also frequently has a subsection that gives suggestions on how to extend the program in various direction; in the earlier programs, this is intended to give you directions for little experiments designed to make your familiar with deal.II, while in later programs it is more about how to use more advanced numerical techniques.
4. The plain program: The source code stripped of all comments. This is useful if you want to see the "big picture" of the code, since the commented version of the program has so much text in between that it is often difficult to see the entire code of a single function on the screen at once.

The tutorials are not only meant to be static documentation, but you should play with them. To this end, go to the examples/step-1 directory (or whatever the number of the tutorial is that you're interested in) and type

cmake .
make
make run

The first command sets up the files that describe which include files this tutorial program depends on, how to compile it and how to run it. This command should find the installed deal.II libraries as well that were generated when you compiled and installed everything as described in the README file. If this command should fail to find the deal.II library, then you need to provide the path to the installation using the command

cmake -DDEAL_II_DIR=/path/to/installed/deal.II .

The second of the commands above compiles the sources into an executable, while the last one executes it (strictly speaking, make run will also compile the code if the executable doesn't exist yet, so you could have skipped the second command if you wanted). This is all that's needed to run the code and produce the output that is discussed in the "Results" section of the tutorial programs. This sequence needs to be repeated in all of the tutorial directories you want to play with.

When learning the library, you need to play with it and see what happens. To this end, open the examples/step-1/step-1.cc source file with your favorite editor and modify it in some way, save it and run it as above. A few suggestions for possibly modifications are given at the end of the results section of this program, where we also provide a few links to other useful pieces of information.

### Video lectures on tutorial programs

This and several of the other tutorial programs are also discussed and demonstrated in Wolfgang Bangerth's video lectures on deal.II and computational science. In particular, you can see the steps he executes to run this and other programs, and you will get a much better idea of the tools that can be used to work with deal.II. In particular, lectures 2 and 4 give an overview of deal.II and of the building blocks of any finite element code. (See also video lecture 2, video lecture 4.)

If you are not yet familiar with using Linux and running things on the command line, you may be interested in watching lectures 2.9 and 2.91. (See also video lecture 2.9, video lecture 2.91.) These give overviews over the command line and on what happens when compiling programs, respectively.

Note that deal.II is actively developed, and in the course of this development we occasionally rename or deprecate functions or classes that are still referenced in these video lectures. For example, the step-1 code shown in video lecture 5 uses a class HyperShellBoundary which was replaced with SphericalManifold class later on. However, in spite of such kind of details the content of the lectures is still very relevant.

### What this program does

Let's come back to step-1, the current program. In this first example, we don't actually do very much, but show two techniques: what is the syntax to generate triangulation objects, and some elements of simple loops over all cells. We create two grids, one which is a regularly refined square (not very exciting, but a common starting grid for some problems), and one more geometric attempt: a ring-shaped domain, which is refined towards the inner edge. Through this, you will get to know three things every finite element program will have to have somewhere: An object of type Triangulation for the mesh; a call to the GridGenerator functions to generate a mesh; and loops over all cells that involve iterators (iterators are a generalization of pointers and are frequently used in the C++ standard library; in the context of deal.II, the Iterators on mesh-like containers module talks about them).

The program is otherwise small enough that it doesn't need a whole lot of introduction.

Note
The material presented here is also discussed in video lecture 5, video lecture 6. (All video lectures are also available here.)

### About scientific computing in general

If you are reading through this tutorial program, chances are that you are interested in continuing to use deal.II for your own projects. Thus, you are about to embark on an exercise in programming using a large-scale scientific computing library. Unless you are already an experienced user of large-scale programming methods, this may be new territory for you — with all the new rules that go along with it such as the fact that you will have to deal with code written by others, that you may have to think about documenting your own code because you may not remember what exactly it is doing a year down the road (or because others will be using it as well), or coming up with ways to test that your program is doing the right thing. None of this is something that we typically train mathematicians, engineers, or scientists in but that is important when you start writing software of more than a few hundred lines. Remember: Producing software is not the same as just writing code.

To make your life easier on this journey let us point to three resources that are worthwhile browsing through before you start any large-scale programming:

• The deal.II FAQ has a good number of answers to questions about particular aspects of deal.II, but also to more general questions such as "How do I debug scientific computing codes?" or "Can I train myself to write code that has fewer bugs?".
• The Software Carpentry project that provides introductions to many topics that are important to dealing with software, such as version control, make files, testing, etc. It is specifically written for scientists and engineers, not for computer scientists, and has a focus on short, practical lessons.
• An article on Best Practices for Scientific Computing that gives an introduction to many of the ways by which you can make sure you are an efficient programmer writing programs that work.

As a general recommendation: If you expect to spend more than a few days writing software in the future, do yourself the favor of learning tools that can make your life more productive, in particular debuggers and integrated development environments. (See also video lecture 7, video lecture 8, video lecture 8.01, video lecture 25.) You will find that you will get the time spent learning these tools back severalfold soon by being more productive! Several of the video lectures referenced above show how to use tools such as integrated development environments or debuggers.

# The commented program

### Include files

The most fundamental class in the library is the Triangulation class, which is declared here:

#include <deal.II/grid/tria.h>

We need the following two includes for loops over cells and/or faces:

#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>

Here are some functions to generate standard grids:

#include <deal.II/grid/grid_generator.h>

We would like to use faces and cells which are not straight lines, or bi-linear quads, so we import some classes which predefine some manifold descriptions:

#include <deal.II/grid/manifold_lib.h>

Output of grids in various graphics formats:

#include <deal.II/grid/grid_out.h>

This is needed for C++ output:

#include <iostream>
#include <fstream>

And this for the declarations of the sqrt' and fabs' functions:

#include <cmath>

The final step in importing deal.II is this: All deal.II functions and classes are in a namespace dealii, to make sure they don't clash with symbols from other libraries you may want to use in conjunction with deal.II. One could use these functions and classes by prefixing every use of these names by ::, but that would quickly become cumbersome and annoying. Rather, we simply import the entire deal.II namespace for general use:

using namespace dealii;

### Creating the first mesh

In the following, first function, we simply use the unit square as domain and produce a globally refined grid from it.

void first_grid ()
{

The first thing to do is to define an object for a triangulation of a two-dimensional domain:

Triangulation<2> triangulation;

Here and in many following cases, the string "<2>" after a class name indicates that this is an object that shall work in two space dimensions. Likewise, there are versions of the triangulation class that are working in one ("<1>") and three ("<3>") space dimensions. The way this works is through some template magic that we will investigate in some more detail in later example programs; there, we will also see how to write programs in an essentially dimension independent way.

Next, we want to fill the triangulation with a single cell for a square domain. The triangulation is the refined four times, to yield $$4^4=256$$ cells in total:

GridGenerator::hyper_cube (triangulation);
triangulation.refine_global (4);

Now we want to write a graphical representation of the mesh to an output file. The GridOut class of deal.II can do that in a number of different output formats; here, we choose encapsulated postscript (eps) format:

std::ofstream out ("grid-1.eps");
GridOut grid_out;
grid_out.write_eps (triangulation, out);
std::cout << "Grid written to grid-1.eps" << std::endl;
}

### Creating the second mesh

The grid in the following, second function is slightly more complicated in that we use a ring domain and refine the result once globally.

void second_grid ()
{

We start again by defining an object for a triangulation of a two-dimensional domain:

Triangulation<2> triangulation;

We then fill it with a ring domain. The center of the ring shall be the point (1,0), and inner and outer radius shall be 0.5 and 1. The number of circumferential cells could be adjusted automatically by this function, but we choose to set it explicitly to 10 as the last argument:

const Point<2> center (1,0);
10);

By default, the triangulation assumes that all boundaries are straight lines, and all cells are bi-linear quads or tri-linear hexes, and that they are defined by the cells of the coarse grid (which we just created). Unless we do something special, when new points need to be introduced; the domain is assumed to be delineated by the straight lines of the coarse mesh, and new points will simply be in the middle of the surrounding ones. Here, however, we know that the domain is curved, and we would like to have the Triangulation place new points according to the underlying geometry. Fortunately, some good soul implemented an object which describes a spherical domain, of which the ring is a section; it only needs the center of the ring and automatically figures out how to instruct the Triangulation where to place the new points. The way this works in deal.II is that you tag parts of the triangulation you want to be curved with a number that is usually referred to as "manifold indicator" and then tell the triangulation to use a particular "manifold object" for all places with this manifold indicator. How exactly this works is not important at this point (you can read up on it in step-53 and Manifold description for triangulations). Here, for simplicity, we will choose the manifold id to be zero. By default, all cells and faces of the Triangulation have their manifold_id set to numbers::invalid_manifold_id, which is the default if you want a manifold that produces straight edges, but you can change this number for individual cells and faces. In that case, the curved manifold thus associated with number zero will not apply to those parts with a non-zero manifold indicator, but other manifold description objects can be associated with those non-zero indicators. If no manifold description is associated with a particular manifold indicator, a manifold that produces straight edges is implied. (Manifold indicators are a slightly complicated topic; if you're confused about what exactly is happening here, you may want to look at the glossary entry on this topic".)

const SphericalManifold<2> manifold_description(center);
triangulation.set_manifold (0, manifold_description);
triangulation.set_all_manifold_ids(0);

In order to demonstrate how to write a loop over all cells, we will refine the grid in five steps towards the inner circle of the domain:

for (unsigned int step=0; step<5; ++step)
{

Next, we need to loop over the active cells of the triangulation. You can think of a triangulation as a collection of cells. If it was an an array, you would just get a pointer that you increment from one element to the next using the operator ++. The cells of a triangulation aren't stored as a simple array, but the concept of an iterator generalizes how pointers work to arbitrary collections of objects (see wikipedia for more information). Typically, any container type in C++ will return an iterator pointing to the start of the collection with a method called begin, and an iterator point to 1 past the end of the collection with a method called end. We can increment an iterator it with the operator ++it, dereference it to get the underlying data with *it, and check to see if we're done by comparing it != collection.end().

The second important piece is that we only need the active cells. Active cells are those that are not further refined, and the only ones that can be marked for further refinement. deal.II provides iterator categories that allow us to iterate over all cells (including the parent cells of active ones) or only over the active cells. Because we want the latter, we need to call the method Triangulation::active_cell_iterators().

Putting all of this together, we can loop over all the active cells of a triangulation with

for (auto it = triangulation.active_cell_iterators().begin();
it != triangulation.active_cell_iterators().end();
++it)
{
auto cell = *it;
// Then a miracle occurs...
}

In the initializer of this loop, we've used the auto keyword for the type of the iterator it. The auto keyword means that the type of the object being declared will be inferred from the context. This keyword is useful when the actual type names are long or possibly even redundant. If you're unsure of what the type is and want to look up what operations the result supports, you can go to the documentation for the method Triangulation::active_cell_iterators(). In this case, the type of it is Triangulation::active_cell_iterator.

While the auto keyword can save us from having to type out long names of data types, we still have to type a lot of redundant declarations about the start and end iterator and how to increment it. Instead of doing that, we'll use range- based for loops, which wrap up all of the syntax shown above into a much shorter form:

for (auto cell: triangulation.active_cell_iterators())
{
Note
See Iterators on mesh-like containers for more information about the iterator classes used in deal.II, and deal.II and the C++11 standard for more information about range-based for loops and the auto keyword.

Next, we want to loop over all vertices of the cells. Since we are in 2d, we know that each cell has exactly four vertices. However, instead of penning down a 4 in the loop bound, we make a first attempt at writing it in a dimension-independent way by which we find out about the number of vertices of a cell. Using the GeometryInfo class, we will later have an easier time getting the program to also run in 3d: we only have to change all occurrences of <2> to <3>, and do not have to audit our code for the hidden appearance of magic numbers like a 4 that needs to be replaced by an 8:

for (unsigned int v=0;
v < GeometryInfo<2>::vertices_per_cell;
++v)
{

If this cell is at the inner boundary, then at least one of its vertices must sit on the inner ring and therefore have a radial distance from the center of exactly 0.5, up to floating point accuracy. Compute this distance, and if we have found a vertex with this property flag this cell for later refinement. We can then also break the loop over all vertices and move on to the next cell.

const double distance_from_center
= center.distance (cell->vertex(v));
if (std::fabs(distance_from_center - inner_radius) < 1e-10)
{
cell->set_refine_flag ();
break;
}
}
}

Now that we have marked all the cells that we want refined, we let the triangulation actually do this refinement. The function that does so owes its long name to the fact that one can also mark cells for coarsening, and the function does coarsening and refinement all at once:

}

Finally, after these five iterations of refinement, we want to again write the resulting mesh to a file, again in eps format. This works just as above:

std::ofstream out ("grid-2.eps");
GridOut grid_out;
grid_out.write_eps (triangulation, out);
std::cout << "Grid written to grid-2.eps" << std::endl;

At this point, all objects created in this function will be destroyed in reverse order. Unfortunately, we defined the manifold object after the triangulation, which still has a pointer to it and the library will produce an error if the manifold object is destroyed before the triangulation. We therefore have to release it, which can be done as follows. Note that this sets the manifold object used for part "0" of the domain back to a default object, over which the triangulation has full control.

triangulation.set_manifold (0);

An alternative to doing so, and one that is frequently more convenient, would have been to declare the manifold object before the triangulation object. In that case, the triangulation would have let lose of the manifold object upon its destruction, and everything would have been fine.

}

### The main function

Finally, the main function. There isn't much to do here, only to call the two subfunctions, which produce the two grids.

int main ()
{
first_grid ();
second_grid ();
}

# Results

Running the program produces graphics of two grids (grid-1.eps and grid-2.eps). They look like this:

The left one, well, is not very exciting. The right one is — at least — unconventional.

While the second mesh is entirely artificial and made-up, and certainly not very practical in applications, to everyone's surprise it has found its way into the literature: see the paper by M. Mu titled "PDE.MART: A network-based problem-solving environment", ACM Trans. Math. Software, vol. 31, pp. 508-531, 2005. Apparently it is good for some things at least.

### Possible extensions

This program obviously does not have a whole lot of functionality, but in particular the second_grid function has a bunch of places where you can play with it. For example, you could modify the criterion by which we decide which cells to refine. An example would be to change the condition to this:

for (auto cell: triangulation.active_cell_iterators())
if (cell->center()[1] > 0)
cell->set_refine_flag ();

This would refine all cells for which the $$y$$-coordinate of the cell's center is greater than zero (the TriaAccessor::center function that we call by dereferencing the cell iterator returns a Point<2> object; subscripting [0] would give the $$x$$-coordinate, subscripting [1] the $$y$$-coordinate). By looking at the functions that TriaAccessor provides, you can also use more complicated criteria for refinement.

#### Different geometries

Another possibility would be to generate meshes of entirely different geometries altogether. While for complex geometries there is no way around using meshes obtained from mesh generators, there is a good number of geometries for which deal.II can create meshes using the functions in the GridGenerator namespace. Take a look at what it provides and see how it could be used in a program like this.

We also discuss a variety of other ways to create and manipulate meshes to step-49.

We close with a comment about modifying or writing programs with deal.II in general. When you start working with tutorial programs or your own applications, you will find that mistakes happen: your program will contain code that either aborts the program right away or bugs that simply lead to wrong results. In either case, you will find it extremely helpful to know how to work with a debugger: you may get by for a while by just putting debug output into your program, compiling it, and running it, but ultimately finding bugs with a debugger is much faster, much more convenient, and more reliable because you don't have to recompile the program all the time and because you can inspect the values of variables and how they change.

Rather than postponing learning how to use a debugger till you really can't see any other way to find a bug, here's the one piece of advice we will provide in this program: learn how to use a debugger as soon as possible. It will be time well invested. The deal.II Frequently Asked Questions (FAQ) page linked to from the top-level deal.II webpage also provides a good number of hints on debugging deal.II programs.

# The plain program

/* ---------------------------------------------------------------------
*
* Copyright (C) 1999 - 2017 by the deal.II authors
*
* This file is part of the deal.II library.
*
* The deal.II library is free software; you can use it, redistribute
* it, and/or modify it under the terms of the GNU Lesser General
* version 2.1 of the License, or (at your option) any later version.
* The full text of the license can be found in the file LICENSE at
* the top level of the deal.II distribution.
*
* ---------------------------------------------------------------------
*/
#include <deal.II/grid/tria.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/manifold_lib.h>
#include <deal.II/grid/grid_out.h>
#include <iostream>
#include <fstream>
#include <cmath>
using namespace dealii;
void first_grid ()
{
Triangulation<2> triangulation;
GridGenerator::hyper_cube (triangulation);
triangulation.refine_global (4);
std::ofstream out ("grid-1.eps");
GridOut grid_out;
grid_out.write_eps (triangulation, out);
std::cout << "Grid written to grid-1.eps" << std::endl;
}
void second_grid ()
{
Triangulation<2> triangulation;
const Point<2> center (1,0);
10);
const SphericalManifold<2> manifold_description(center);
triangulation.set_manifold (0, manifold_description);
triangulation.set_all_manifold_ids(0);
for (unsigned int step=0; step<5; ++step)
{
for (auto cell: triangulation.active_cell_iterators())
{
for (unsigned int v=0;
v < GeometryInfo<2>::vertices_per_cell;
++v)
{
const double distance_from_center
= center.distance (cell->vertex(v));
if (std::fabs(distance_from_center - inner_radius) < 1e-10)
{
cell->set_refine_flag ();
break;
}
}
}
}
std::ofstream out ("grid-2.eps");
GridOut grid_out;
grid_out.write_eps (triangulation, out);
std::cout << "Grid written to grid-2.eps" << std::endl;
triangulation.set_manifold (0);
}
int main ()
{
first_grid ();
second_grid ();
}