Building a Cmesh by hand - DLR-AMR/t8code GitHub Wiki

Build Cmesh

In this tutorial we will learn how to create a user defined cmesh by hand. This is suitable for small to medium sized cmeshes where you want to fully control all parameters. For beginners we recommend to not follow this route but to read a cmesh from file using t8_cmesh_vtk_reader (vor VTK files) or t8_cmesh_from_msh_file (for Gmsh created .msh files).

You will find the code to this example in the tutorials/general/t8_tutorial_build_cmesh* files and it creates the executables tutorials/general/t8_tutorial_build_cmesh.

In the last tutorials we learned how to create a forest, adapt it, and how to store data. We also learned about algorithms for partitioning, balancing and creating a ghost layer. In all these previous tutorials predefined meshes were used. In this tutorial we learn how to define a user defined mesh in two- and three dimensions. In both examples we define and use different tree classes and join the different trees to create the domain. In order to be able to reflect the results, the meshes are stored in .vtu files.

Steps of how to define a mesh

The cmesh defines the topology of a given domain and is the coarsest possible mesh.

1. Definition of all vertices

In a first step an array with all is defined. Independent of the fact if a mesh is defined two- or three dimensional, each point is defined by three coordinates. The vertices are ordered in a listing of points for each cell. Thus, there can be duplicates in the list.

double vertices[numberOfValues] = {

  //point values for tree 1
  x_1,y_1,z_1         //(x,y,z) of first point of tree 1
  x_2,y_2,z_2         //(x,y,z) of second point of tree 1
      .
      .
      .
  x_n,y_n,z_n         //(x,y,z) of nth point (last point) of tree 1

  //point values for tree 2
  x_1,y_1,z_1         //(x,y,z) of first point of tree 2
  x_2,y_2,z_2         //(x,y,z) of second point of tree 2
       .
       .
       .
  x_m,y_m,z_m         //(x,y,z) of nth point (last point) of tree 2

       .
       .
       .

  //point values for the last tree
  x_1,y_1,z_1         //(x,y,z) of first point of the last tree
  x_2,y_2,z_2         //(x,y,z) of second point of the last tree
       .
       .
       .
  x_o,y_o,z_o         //(x,y,z) of nth point (last point) of the last tree
};

2. Initialization of the mesh

Before creating a mesh, it has, of course, to be initialized using t8_cmesh_init.

3. Definition of the geometry

A cmesh can have different types of geometry which are set using the function t8_cmesh_register_geometry. The use of curved meshes is described in the tutorial Feature Curved meshes. In this tutorial we will use meshes with a linear geometry.

4. Definition of the classes of the different trees

t8code supports eight different basic tree shapes for the cmesh, see also t8_eclass.h:

element shape description Number of vertices
T8_ECLASS_VERTEX 0D points 1
T8_ECLASS_LINE 1D lines 2
T8_ECLASS_TRIANGLE 2D triangles 3
T8_ECLASS_QUAD 2D quadrilaterals 4
T8_ECLASS_TET 3D tetrahedra 4
T8_ECLASS_PYRAMID 3D pyramids 5
T8_ECLASS_PRISM 3D prisms 6
T8_ECLASS_HEX 3D hexahedra 8

Using the function t8_cmesh_set_tree_class the tree class of each tree is set.

Parameter Description
cmesh The cmesh to be updated.
tree_id The global number of the tree.
tree_class The element class of this tree.

Definition of the classes of the different trees - each tree is defined by one cell

//Class of the first tree
t8_cmesh_set_tree_class (cmesh, 0, T8_ECLASS_[TYPE]);
//Class of the second tree
t8_cmesh_set_tree_class (cmesh, 1, T8_ECLASS_[TYPE]);
         .
         .
         .
//Class of the last tree
t8_cmesh_set_tree_class (cmesh, x, T8_ECLASS_[TYPE]);

5. Classification of the vertex coordinates for each tree

Vertex IDs for the two two-dimensional trees:

Each tree can assign coordinates to its vertices. This is done using t8_cmesh_set_tree_vertices. It is not allowed to call this function after t8_cmesh_commit.

Parameter Description
cmesh The cmesh to be updated.
tree_id The global number of the tree.
*vertices Array of 3 * num_vertices doubles storing the vertex coordinates. Can be overwritten or deleted after the function call.
num_vertices Number of the vertices of the tree (i.e. 3 for a triangle tree, 4 for a quad tree, ...).
// Set vertex coordinates of tree 0
t8_cmesh_set_tree_vertices (cmesh, 0, [pointerToVertexCoordinatesOfTreeZero], [numberOfVerticesTreeZero]);
// Set vertex coordinates of tree 1
t8_cmesh_set_tree_vertices (cmesh, 1, [pointerToVertexCoordinatesOfTreeOne] , [numberOfVerticesTreeOne]);
     .
     .
     .
// Set vertex coordinates of tree x
t8_cmesh_set_tree_vertices (cmesh, x, [pointerToVertexCoordinatesOfTree(x)] , [numberOfVerticesTree(x)]);

After the cmesh was committed you can retrieve the coordinates by calling double *t8_cmesh_get_tree_vertices (t8_cmesh_t cmesh, t8_locidx_t ltreeid).

6. Definition of global vertex Ids

Since https://github.com/DLR-AMR/t8code/pull/960 (t8code version 4.0.0) it is possible to assign global vertex Ids to the tree vertices. That is, if two trees are connected to each other via two of their vertices, this vertex becomes a single global vertex with a unique Id. These Ids have to be set manually using t8_cmesh_set_global_vertices_of_tree. It is not allowed to call this function after t8_cmesh_commit.

Parameter Description
cmesh The cmesh to be updated.
tree The gobale tree id of a tree in the cmesh
global_tree_vertices An array of num_vertices many integers specifying the global vertex Ids of the tree's local vertices.
num_vertices The number of (local) vertices of the tree (i.e. 3 for a triangle, 4 for a quad).
// Set the global vertex Ids of tree 0
t8_cmesh_set_global_vertices_of_tree (cmesh, 0, [pointerToGlobalVertexIdsOfTreeZero], [numberOfVerticesTreeZero]);
// Set the global vertex Ids of tree 1
t8_cmesh_set_global_vertices_of_tree (cmesh, 1, [pointerToGlobalVertexIdsOfTreeOne], [numberOfVerticesTreeOne]);
...

After the cmesh is committed you can retrieve the global vertex Ids via t8_cmesh_get_global_vertices_of_tree to get the global Ids of a single tree's vertices or via t8_cmesh_get_vertex_to_tree_list (currently WIP and not yet available) to get for each global vertex Id a list of all (tree,vertex) pairs that are connected to it.

7. Definition of the face neighbors between the different trees

Edge IDs for the corresponding to the vertices can be seen in the previous figure (f_i). In this step all connections (face neighbors) between the different trees are set using t8_cmesh_set_join.

Parameter Description
cmesh The cmesh to be updated.
tree1 The tree id of the first of the two trees.
tree2 The tree id of the second of the two trees.
face1 The face number of the first tree.
face2 The face number of the second tree.
orientation Specify how face1 and face2 are oriented to each other

The orientation is determined as follows. Let my_face and other_face be the two face numbers of the connecting trees. We chose a main_face from them as follows: Either both trees have the same element class, then the face with the lower face number is the main_face o the trees belong to different classes in which case the face belonging to the tree with the lower class according to the ordering triangle < square, hex < tet < prism < pyramid, is the main_face. Then face corner 0 of the main_face connects to a face corner k in the other face. The face orientation is defined as the number k.

// List of all face neighboor connections
t8_cmesh_set_join (cmesh, [treeId1], [treeId2], [faceIdInTree1], [faceIdInTree2], [orientation]);
t8_cmesh_set_join (cmesh, [treeId1], [treeId2], [faceIdInTree1], [faceIdInTree2], [orientation]);
     .
     .
     .
t8_cmesh_set_join (cmesh, [treeId1], [treeId2], [faceIdInTree1], [faceIdInTree2], [orientation]);

8. Commit the mesh

The last step of creating a user defined mesh is committing the mesh using t8_cmesh_commit.

2D Example

In this two-dimensional example four triangles and two quads are used. We will look at the following example. In the left you can see the order of the vertices and in the right the edge IDs.

The vertices of the trees have the following coordinate:

tree vertices
triangle 1 {(0, 0, 0), (0.5, 0, 0), (0.5, 0.5, 0)}
triangle 2 {(0, 0, 0), (0.5, 0.5, 0), (0, 0.5, 0)}
triangle 3 {(0.5, 0.5, 0), (1, 0.5, 0), (1, 1, 0)}
triangle 4 {(0.5, 0.5, 0), (1, 1, 0), (0.5, 1, 0)}
quad 1 {(0.5, 0, 0), (1, 0, 0), (0.5, 0.5, 0), (1, 0.5, 0)}
quad 2 {(0, 0.5, 0), (0.5, 0.5, 0), (0, 1, 0), (0.5, 1, 0)}

The tree class for the triangles is T8_ECLASS_TRIANGLE and this for the quad is T8_ECLASS_QUAD:

// definition of the tree classes (you need one classification for each tree)
t8_cmesh_set_tree_class (cmesh, [treeID], T8_ECLASS_TRIANGLE);
t8_cmesh_set_tree_class (cmesh, [treeID], T8_ECLASS_QUAD);

Each edge of the tree has an ID. The IDs for this example can be seen in the figure. For the direct neighbor information, the following trees are connected:

ID of first tree ID of second tree ID of face (first tree) ID of face (second tree)
0 1 1 2
0 2 0 0
1 3 0 2
2 4 3 2
3 5 1 1
4 5 1 2
// definition of the face neighboors
t8_cmesh_set_join (cmesh, 0, 1, 1, 2, 0);
t8_cmesh_set_join (cmesh, 0, 2, 0, 0, 0);
t8_cmesh_set_join (cmesh, 1, 3, 0, 2, 1); 
t8_cmesh_set_join (cmesh, 2, 4, 3, 2, 0);
t8_cmesh_set_join (cmesh, 3, 5, 1, 1, 0);
t8_cmesh_set_join (cmesh, 4, 5, 1, 2, 0);

As this cmesh has periodic boundaries, there are also the connections

ID of first tree ID of second tree ID of face of first tree ID of face of second tree
0 3 2 3
1 2 1 1
2 5 2 0
3 4 0 0
// definition of the face neighboors for the periodic boundaries
t8_cmesh_set_join (cmesh, 0, 3, 2, 3, 0); 
t8_cmesh_set_join (cmesh, 1, 2, 1, 1, 0);
t8_cmesh_set_join (cmesh, 2, 5, 2, 0, 1);
t8_cmesh_set_join (cmesh, 3, 4, 0, 0, 0);

3D Example

In this three dimensional example two tetrahedra, two prisms, one pyramid, and one hexahedron is used. We will look at the following example. In the left you can see the order of the vertices and in the right the edge IDs.

The vertices of the trees have the following coordinate:

tree vertices
tetrahedron 1 {(0.43, 0, 2), (0, 0, 1), (0.86, -0.5, 1), (0.86, 0.5, 1)}
tetrahedron 2 {(2.29, 0, 2), (1.86, -0.5, 1), (2.72, 0, 1), (1.86, 0.5, 1)}
prism 1 {(0, 0, 0), (0.86, -0.5, 0), (0.86, 0.5, 0), (0, 0, 1), (0.86, -0.5, 1), (0.86, 0.5, 1)}
prism 2 {(1.86, -0.5, 0), (2.72, 0, 0), (1.86, 0.5, 0), (1.86, -0.5, 1), (2.72, 0, 1), (1.86, 0.5, 1)}
pyramid {(0.86, 0.5, 0), (1.86, 0.5, 0), (0.86, -0.5, 0), (1.86, -0.5, 0), (1.36, 0, -0.5)}
hexahedron {(0.86, -0.5, 0), (1.86, -0.5, 0), (0.86, 0.5, 0), (1.86, 0.5, 0), (0.86, -0.5, 1), (1.86, -0.5, 1),(0.86, 0.5, 1), (1.86, 0.5, 1)}

The tree class for the tetrahedra is T8_ECLASS_TET, this for the prisms is T8_ECLASS_PRISM, and this for the hexahedron is T8_ECLASS_HEX:

// definition of the tree classes (you need one classification for each tree)
t8_cmesh_set_tree_class (cmesh,  [treeID], T8_ECLASS_TET);
t8_cmesh_set_tree_class (cmesh,  [treeID], T8_ECLASS_PRISM);
t8_cmesh_set_tree_class (cmesh,  [treeID], T8_ECLASS_PYRAMID);
t8_cmesh_set_tree_class (cmesh,  [treeID], T8_ECLASS_HEX);

As the mesh has no periodic boundaries, there are only direct neighbors. These are encoded by the face connections between the trees:

ID of first tree ID of second tree ID of face (first tree) ID of face (second tree)
0 2 0 4
1 3 4 4
2 5 0 0
3 5 1 1
4 5 4 4
// definition of the face neighboors
t8_cmesh_set_join (cmesh, 0, 2, 0, 4, 0);
t8_cmesh_set_join (cmesh, 1, 3, 0, 4, 0);
t8_cmesh_set_join (cmesh, 2, 5, 0, 0, 0);
t8_cmesh_set_join (cmesh, 3, 5, 1, 1, 0);
t8_cmesh_set_join (cmesh, 4, 5, 4, 4, 2);