-
Notifications
You must be signed in to change notification settings - Fork 52
Step 5 Store element data
In this tutorial we will learn how to associate custom data with the elements of a forest and how to exchange data over the ghost elements of a parallel partition.
You will find the code to this example in the tutorials/general/step5*
files and it creates the executables tutorials/general/t8_step5_element_data
and tutorials/general/t8_step5_element_data_c_interface
. The latter is an implementation of the example using our pure C
interface.
In the last tutorials we learned how to create a forest and adapt it. In step 4 we also learned about algorithms for partitioning, balancing and creating a ghost layer. In this tutorial we will start by performing all these operations in one step. Then, when we have our forest, we will continue with how to build a data array and gather data for the local elements of our process. Further we exchange the data values of the ghost elements and output the volume data to our .vtu
file.
As announced in step4 - order of execution, it is possible to mix the t8_forest_set*
functions together as you wish (see t8_forest.h
).
We can thus in one step adapt a forest, balance it, partition it and create a ghost layer with
t8_forest_t new_forest;
t8_forest_init (&new_forest);
t8_forest_set_user_data (new_forest, &data);
t8_forest_set_adapt (new_forest, forest, CALLBACK, recursive_flag);
t8_forest_set_partition (new_forest, forest, 0);
t8_forest_set_balance (new_forest, forest, no_partition_flag);
t8_forest_set_ghost (new_forest, 1, T8_GHOST_FACES);
t8_forest_commit (new_forest);
The order in which the t8_forest_set*
functions are called is not relevant.
Let us execute the example with 4 MPI processes.
mpirun -np 4 ./t8_step5_element_data
As expected, the resulting forest looks like the adapted forest from step 4 (executed with 4 MPI processes) before it gets unbalanced.
To build a data array, we first need to allocate memory at run time, which should happen via T8_ALLOC
. The syntax of T8_ALLOC
is similar to malloc
. It requires a datatype and size to allocate the necessary memory. In our case we want to store the level and volume of each element. To do so, we create a struct with level
and volume
variables inside.
struct t8_step5_data_per_element
{
int level;
double volume;
};
Since we have activated ghost layer, the required size is the number of local elements plus the number of ghost elements. A look into t8_forest.h
shows, that there are two handy functions.
num_local_elements = t8_forest_get_local_num_elements (forest);
num_ghost_elements = t8_forest_get_num_ghosts (forest);
Having all requirements set, we can now allocate the memory.
element_data = T8_ALLOC (struct t8_step5_data_per_element, num_local_elements + num_ghost_elements);
In the latter case you need to use T8_FREE
in order to free the memory as you would do with malloc
.
Let us now fill the data with something. For this, we iterate through all trees and for each tree through all its elements, calling t8_forest_get_element_in_tree
to get a pointer to the current element.
Note, that this is the recommended and most performant way. An alternative is to iterate over the number of local elements and use t8_forest_get_element
. However, this function needs to perform a binary search for the element and the tree it is in, while t8_forest_get_element_in_tree
has a constant look up time. You should only use t8_forest_get_element
if you do not know in which tree an element is.
The reason for the iteration through the trees and then through its elements is, that each tree may have a different element class and therefore also a different way to interpret its elements. You used different element classes already in step 1 - Creating a simple coarse mesh. In t8_eclass.h
you will find all possible element classes (i.e. triangle, tetrahedron, square, etc.). You also may remember seeing or even using the eclass_scheme
in step 3 - The adaptation callback before. The eclass_scheme
was a parameter of the t8_forest_adapt_t
callback function. In order to be able to handle elements of a tree, we need to get its eclass_scheme
.
The eclass_scheme
stores for each element shape, one member of type t8_eclass_scheme_c
which provides the necessary functions for this element shape. We could for example use t8_element_level
to compute the element's level.
Finally, we can now fill the first num_local_elements
entries of our array.
Until now, each process has computed the data entries for its local elements. In order to get the values for the ghost elements, we use t8_forest_ghost_exchange_data
. Calling this function will fill all the ghost entries of our element data array with the value on the process that owns the corresponding element. This means that we don't have to iterate over all the elements again. t8code takes care of exchanging the data for us.
However, we have to do a little preparatory work, since t8_forest_ghost_exchange_data
expects an sc_array
. Therefore, we wrap our data array to an sc_array
.
sc_array_wrapper = sc_array_new_data (element_data, sizeof(struct t8_step5_data_per_element), num_local_elements + num_ghost_elements);
Carry out the data exchange.
t8_forest_ghost_exchange_data (forest, sc_array_wrapper);
Note that this function does not need an index. A request regarding the number of local elements on the respective process takes place in the background. Thus, all entries with an index greater than num_local_elements
will be overwritten.
Destroy the wrapper array by calling sc_array_destroy
. This will not free the data memory since we used sc_array_new_data
.
After creating the forest
and storing elements' volumes and levels in an array we will write the forest
and the elements' volumes out to .vtu
files in order to view it in Paraview
.
In the last steps we just called the following function
t8_forest_write_vtk (forest, prefix);
to write a forest as vtu
. But to write also user defined data, we need an extended output function t8_forest_vtk_write_file
.
t8code supports writing element based data to vtu
as long as its stored as doubles
. Each of the data fields to write has to be provided in its own array of length num_local_elements
. We support two types:
VTK variable type | double per element |
---|---|
T8_VTK_SCALAR | 1 |
T8_VTK_VECTOR | 3 |
Therefore, we need to allocate a new array to store the volumes on their own. This array has one entry per local element.
double *element_volumes = T8_ALLOC (double, num_local_elements);
Despite writing user data, t8_forest_vtk_write_file
also offers more control over which properties of the forest to write.
Parameter | Description |
---|---|
forest | The forest to write |
fileprefix | The prefix of the files |
write_treeid | If true, the global tree id is written for each element |
write_mpirank | If true, the mpirank is written for each element |
write_level | If true, the refinement level is written for each element |
write_element_id | If true, the global element id is written for each element |
write_ghosts | If true, each process additionally writes its ghost elements |
curved_flag | If true, write the elements as curved element types |
do_not_use_API | Do not use the VTK API, even if linked and available |
num_data | Number of user defined double valued data fields to write |
data | Array of t8_vtk_data_field_t of length num_data
|
For more documentation take a look into t8_forest.h
.
Note, you can store even more data to the vtu
file. The number of user defined data fields is defined by the parameter num_data
. For each user defined data field we need one t8_vtk_data_field_t
variable. In our case, it is only one.
t8_vtk_data_field_t vtk_data;
Next, set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR
vtk_data.type = T8_VTK_SCALAR;
Also the name of the field as should be written to the file.
strcpy (vtk_data.description, "Element volume");
Finally, copy the elements' volumes from our data array to the output array and pass it to t8_forest_write_vtk_ext
.
We decided to only set write_treeid
, write_mpirank
, write_level
and write_element_id
true as it would be in t8_forest_write_vtk
. In this way, the only difference to t8_forest_vtk_write_file
is the addition of the volume of the elements.
In Paraview
we can now display the volumes of the elements and work with them.
Installation Guide
Configure Options
Setup t8code on JUWELS and other Slurm based systems
Setup t8code for VTK
General
Step 0 Hello World
Step 1 Creating a coarse mesh
Step 2 Creating a uniform forest
Step 3 Adapting a forest
Step 4 Partition,-Balance,-Ghost
Step 5 Store element data
Step 6 Computing stencils
Step 7 Interpolation
Features
Documentation
Tree Indexing
Element Indexing
Running on JUWELS using Slurm
Overview of the most used API functions
Known issues
Workflow - FreeCAD to t8code
Reproducing Scaling Resluts
Coding Guidelines
Tips
Debugging with gdb
Debugging with valgrind
Test driven development
Testing with GoogleTest
Writing C interface code