Skip to content

Step 1 Creating a coarse mesh

Chiara Hergl edited this page May 6, 2024 · 16 revisions

Step 1 - Creating a coarse mesh

In this example we will build our first coarse mesh and write it to .vtu files in order to visualize it with Paraview.

You will find the code to this example in tutorials/general/step1_coarsemesh.c and it creates the executable tutorials/general/step1_coarsemesh.

What is a coarse mesh?

In order to build an adaptive mesh t8code actually manages two meshes. The coarse mesh (cmesh) and the forest mesh (forest).

The coarse mesh is the initial input that describes the geometrical and topological properties of the meshed domain. It describes the coarsest possible mesh. It is a classical unstructured mesh as it would be generated by a standard mesh generator such as gmsh. It can be as simple as a single hexahedron specifying a cube domain, or as complex as billions of tetrahedra, prisms, pyramids and hexahedra describing an airplane geometry.

We view each element of the coarse mesh as the root of recursive refinement. The forest is the collection of these refined elements. Due to the tree-like nature of recursive refinement, we call the elements of the cmesh trees.

The cmesh will (usually) not change during a computation. A cmesh can be partitioned among the processes or each process can hold a copy of the complete cmesh.

These two pictures illustrate the coarse and the forest mesh for a square shaped domain with a circular hole. Left: The coarse mesh that describes the geometry and tree shapes. Right: The forest mesh. Each tree in the coarse mesh is refined to a certain refinement level. In this example the refinement criterion was chosen to refine elements that are close to the domain boundary. The colors represent a distribution of the elements among 4 MPI processes.

Creating a simple coarse mesh

t8code offers different ways to create coarse meshes, which can be found in t8_cmesh.h

The most important are

  • reading files from mesh generators gmsh, Tetgen or TRIANGLE
  • Building a cmesh by hand by specifying its trees and neighbor connections
  • Using one of the provided t8_cmesh_new_* functions

In this example we use t8_cmesh_new_hypercube to create a cmesh that models a cube domain.

  cmesh = t8_cmesh_new_hypercube (T8_ECLASS_TET, comm, 0, 0, 0);

It offers various parameters that control what kind of cube is created and how it is meshed. It also gets an sc_MPI_Comm parameter to specify on which processes this cmesh should live (usually sc_MPI_COMM_WORLD).

The first parameter T8_ECLASS_TET specifies the kind of geometric trees to use to model the cube and also changes the dimension. t8code supports eight different basic tree shapes for the cmesh, see also t8_eclass.h:

element shape description Number of elements in cube
T8_ECLASS_VERTEX 0D points 1
T8_ECLASS_LINE 1D lines 1
T8_ECLASS_QUAD 2D quadrilaterals 1
T8_ECLASS_TRIANGLE 2D triangles 2
T8_ECLASS_HEX 3D hexahedra 1
T8_ECLASS_TET 3D tetrahedra 6
T8_ECLASS_PRISM 3D prisms 2
T8_ECLASS_PYRAMID 3D pyramids 3

Each of these can be chosen as first parameter for t8_cmesh_new_hypercube and will create a [0,1]^d cube of the specified dimension and tree type.

The other parameters are flags that control whether the cmesh should be created on root and broadcasted to the other processes, whether it should be partitioned among the processes, and whether it should have periodic boundaries.

Getting the number of trees

In t8_cmesh.h you find various functions to gather information about our coarse mesh.

We now want to get the global number of trees (6) and the number of process local trees (depends on the number of processes and whether the coarse mesh is partitioned or not, ca. 6/#ranks) and print them.

To do this we use the two function calls:

  t8_locidx_t local_num_trees = t8_cmesh_get_num_local_trees (cmesh);
  t8_gloidx_t global_num_trees = t8_cmesh_get_num_trees (cmesh);

which we then print with:

  t8_global_productionf (" [step2] Local number of trees:\t%i\n", local_num_trees);
  t8_global_productionf (" [step2] Global number of trees:\t%li\n", global_num_trees);

Note that due to using t8_global_productionf opposed to t8_productionf only rank 0 will print its local number of trees.

If you use t8_productionf instead, you can compare the local tree numbers for different processes.

Also note that since in this example the coarse mesh is not partitioned, each process has a complete copy of the coarse mesh and hence each process has 6 local and 6 global trees.

Side note Index types:

To index t8code trees (or similar entities such as elements or ghosts) we have two integer types that are used:

t8_locidx_t is used for everything that is process local, such as local number of elements or trees.

t8_gloidx_t is used for everything that is global across all processes, such as global number of elements or trees.

There is also the third type t8_linearidx_t that is explicitly used to store space-filling curve indices.

Writing a cmesh to .vtu

After creating the cmesh we can write it out to .vtu files in order to view it in Paraview.

We do this with the function call

  t8_cmesh_vtk_write_file (cmesh, prefix, 1.0);

which will create the files prefix.pvtu and prefix_0000.vtu. If the cmesh was partitioned then it would create one file prefix_MPIRANK.vtu for each MPI rank. Since in this example it is not partitioned, we only get the file from the root process with rank 0.

To view it with Paraview, open the file prefix.pvtu. The t8_step1_coarsemesh example will create the file t8_step1_tetcube.pvtu.

As we can see our geometry is a three-dimensional cube that consists of six tetrahedral trees.

Clone this wiki locally