Skip to content

Step A Mesh Competences

lenaploetzke edited this page Oct 16, 2026 · 3 revisions

Step A – Competence Packs and Custom Competences with Mesh Handle

In this tutorial, we will learn how to use the predefined competence packs of the mesh handle interface and how to implement our own custom competences. These features are native to the mesh handle only and can’t be used in the default t8code library. You will find the code to this example in the tutorials/mesh_handle/t8_mesh_stepA_competences.cxx file and it creates the executable tutorials/mesh_handle/t8_mesh_stepA_competences. The so-called competences are a way to extend the functionality of the mesh handle and its elements. The competences are organized in different types, depending on the functionality. Element data competences let you attach your own user-defined data to each element and work with it, e.g. exchange it between processes for ghost elements. You may have already seen this in action, because the element data competences were already used in step 5 of the mesh handle tutorials. Cache competences store the results of per-element computations, such as the volume, centroid, vertex coordinates or face neighbors, when they are first requested, so they don't have to be recomputed on later calls. The key point about competences though is, that you can create your own competence packs with the competence mix you want to use and then use this pack to create a mesh handle with all the functionality you need. This can be further expanded by helper competences for “discontinuous Galerkin methods” or even creating your own competences. These then can be added to your competence pack, making the mesh handle really flexible and individual for each use case.

Element Data Competences

As already explained in step 5, we can store custom element data, such as the volume of each element.

How to use Competences

Generally, there are different competence packs predefined. Each of them includes several competences, but you can also combine two packs into one with the help of competence_pack_union or even build your packs individually by defining them and listing your needed competences. We will start by fusing two of the predefined competence packs into one with the help of competence_pack_union. The first one is the predefined default competence pack for all cache competences and the second one is for all the element data competences.

using element_competences = union_competence_packs_type<all_cache_element_competences, data_element_competences_basic>;

Please note that this doesn’t have to be done, but it is a neat way to keep things organized and keep the code cleaner. After this step we will have to define our mesh type with

using mesh_type = mesh<element_competences, mesh_competences>;

Inside this definition, we are assigning the mesh_type with several different competence packs. Firstly, the union we created before and secondly, with another predefined competence pack for all the standard mesh competences. Our defined element_competences pack broadens the number of functions we can call regarding our individual mesh elements. For example, we can now call element.set_element_data() or element.vertex_cache_filled(). The mesh_competences on the other hand increase the number of functions we can call regarding our mesh. This could be mesh.repartition_element_data() or mesh.set_interpolate_callback. With this mesh type we are now able to define our mesh:

auto base_mesh = handle_hypercube_hybrid_uniform_default<mesh_type>( level, comm );

This is now a generic hybrid hypercube mesh as used in the previous steps but with the difference, that this mesh now includes all the competences we listed above.

Cache Competences

As you may have already noticed above, we didn’t just include the element data competences but also the cache competences. One key competence type of the mesh handle is caching, so there are a lot of values that can get cached. Simply explained, this means that we save values after they get calculated once in a separate memory, so they don’t have to be calculated until the next commit, saving us a lot of run time. Please note, that although you can save a lot of run time, this also comes at the expense of storage capacity. You should always decide individually regarding your context, whether you want to use caching or calculating for your values. Caching makes the most sense, if your individual calculations are expensive timewise. In this example we are caching the vertex coordinates of each element. This happens automatically with the function call elem.get_vertex_coordinates();(if the competence is included in mesh_type), but we can show this by calling the function elem.vertex_cache_filled() before and after the calculation of the vertex coordinates and then also printing both values to compare them.

Custom Mesh Competences

Lastly, you can also implement your own custom competences. In this tutorial, we will implement a simple custom competence, that just returns the square of the volume of each element. The implementation of this custom competence is done via a C++ struct:

template <typename TUnderlying>
struct volume_squared_custom_competence: public t8_crtp_operator<TUnderlying, volume_squared_custom_competence>

This pattern of defining a custom competence is really important for it to work, as the competence needs to inherit from t8_crtp_operator<TUnderlying, Competence> In this struct, we can then define the different functions of our competence, in this case only one, called get_squared_volume():

double get_squared_volume() const
        {
            double volume = this->underlying().get_volume();
            return volume * volume; 
        }

After that, we can use or custom competence pack as before and define a custom mesh_type with it. Lastly, we only need to create our mesh with the mesh_type so the competence is transferred onto the mesh. We can then use the competence on the mesh by calling the implemented function get_squared_volume() on the first and the last element of the mesh:

auto first_elem = mesh.cbegin ();  /**< Get the first element of this MPI process. */
first_elem->get_squared_volume ()); /**< Computing the squared volume using the custom competence. */

In the end, we can also look at the calculated squared volume in Paraview:

image

These competences work, because the mesh or element class is inheriting from all competences. When you are defining a function in a competence, every mesh that is defined using this competence is able to use the defined function. The custom competences are the most important aspect of the mesh handle, as they allow every user to extend the functionality of the mesh and its elements to suit their needs.

Clone this wiki locally