Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/tutorials/general/t8_step5_element_data_c_interface.c
903 views
1
/*
2
This file is part of t8code.
3
t8code is a C library to manage a collection (a forest) of multiple
4
connected adaptive space-trees of general element types in parallel.
5
6
Copyright (C) 2015 the developers
7
8
t8code is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2 of the License, or
11
(at your option) any later version.
12
13
t8code is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
17
18
You should have received a copy of the GNU General Public License
19
along with t8code; if not, write to the Free Software Foundation, Inc.,
20
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
*/
22
23
/* See also: https://github.com/DLR-AMR/t8code/wiki/Step-5---Store-element-data
24
*
25
* This is step5 of the t8code tutorials using the C interface of t8code.
26
* In the following we will store data in the individual elements of our forest.
27
* To do this, we will again create a uniform forest, which will get adapted as in step4,
28
* with the difference that we partition, balance and create ghost elements all in the same step.
29
* After adapting the forest we will learn how to build a data array and gather data for
30
* the local elements. Furthermore, we exchange the data values of the ghost elements and
31
* output the volume data to vtu.
32
*
33
* How you can experiment here:
34
* - Look at the paraview output files of the adapted forest.
35
* You can apply a clip filter to look into the cube. Also you can apply (in addition)
36
* the threshold filter to display only elements with certain properties.
37
* But at first you may just want to enter the tooltip selection mode 'Hover Cells On'
38
* to display cell information when hover over them.
39
* - Change the adaptation criterion as you wish to adapt elements or families as desired.
40
* - Store even more data per element, for instance the coordinates of its midpoint.
41
* You can again apply the threshold filter to your new data. Don't forget to write the
42
* data into the output file.
43
* */
44
45
#include <sc_containers.h> /* sc library. */
46
#include <t8.h> /* General t8code header, always include this. */
47
#include <t8_cmesh/t8_cmesh.h> /* cmesh definition and basic interface. */
48
#include <t8_cmesh/t8_cmesh_examples.h> /* A collection of exemplary cmeshes. */
49
#include <t8_forest/t8_forest_general.h> /* forest definition and basic interface. */
50
#include <t8_forest/t8_forest_geometrical.h> /* geometrical information of a forest. */
51
#include <t8_forest/t8_forest_io.h> /* save forest. */
52
#include <t8_schemes/t8_default/t8_default_c_interface.h> /* default refinement scheme. */
53
#include <t8_schemes/t8_scheme.h> /* C interface of the forest scheme */
54
#include <tutorials/general/t8_step3.h>
55
56
T8_EXTERN_C_BEGIN ();
57
58
/* The data that we want to store for each element.
59
* In this example we want to store the element's level and volume. */
60
struct t8_step5_data_per_element
61
{
62
int level;
63
double volume;
64
};
65
66
static t8_forest_t
67
t8_step5_build_forest (sc_MPI_Comm comm, int level)
68
{
69
t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (comm, 0, 0);
70
const t8_scheme_c *scheme = t8_scheme_new_default ();
71
struct t8_step3_adapt_data adapt_data = {
72
{ 0.5, 0.5, 1 }, /* Midpoints of the sphere. */
73
0.2, /* Refine if inside this radius. */
74
0.4 /* Coarsen if outside this radius. */
75
};
76
/* Start with a uniform forest. */
77
t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, comm);
78
t8_forest_t forest_apbg;
79
80
/* Adapt, partition, balance and create ghost elements all in the same step. */
81
t8_forest_init (&forest_apbg);
82
t8_forest_set_user_data (forest_apbg, &adapt_data);
83
t8_forest_set_adapt (forest_apbg, forest, t8_step3_adapt_callback, 0);
84
t8_forest_set_partition (forest_apbg, NULL, 0);
85
t8_forest_set_balance (forest_apbg, NULL, 0);
86
t8_forest_set_ghost (forest_apbg, 1, T8_GHOST_FACES);
87
t8_forest_commit (forest_apbg);
88
89
return forest_apbg;
90
}
91
92
static struct t8_step5_data_per_element *
93
t8_step5_create_element_data (t8_forest_t forest)
94
{
95
t8_locidx_t num_local_elements;
96
t8_locidx_t num_ghost_elements;
97
struct t8_step5_data_per_element *element_data;
98
99
/* Check that forest is a committed, that is valid and usable, forest. */
100
T8_ASSERT (t8_forest_is_committed (forest));
101
102
/* Get the number of local elements of forest. */
103
num_local_elements = t8_forest_get_local_num_leaf_elements (forest);
104
/* Get the number of ghost elements of forest. */
105
num_ghost_elements = t8_forest_get_num_ghosts (forest);
106
107
/* Now we need to build an array of our data that is as long as the number
108
* of elements plus the number of ghosts. You can use any allocator such as
109
* new, malloc or the t8code provide allocation macro T8_ALLOC.
110
* Note that in the latter case you need
111
* to use T8_FREE in order to free the memory.
112
*/
113
element_data = T8_ALLOC (struct t8_step5_data_per_element, num_local_elements + num_ghost_elements);
114
/* Note: We will later need to associate this data with an sc_array in order to exchange the values for
115
* the ghost elements, which we can do with sc_array_new_data (see t8_step5_exchange_ghost_data).
116
* We could also have directly allocated the data here in an sc_array with
117
* sc_array_new_count (sizeof (struct data_per_element), num_local_elements + num_ghost_elements);
118
*/
119
120
/* Let us now fill the data with something.
121
* For this, we iterate through all trees and for each tree through all its elements, calling
122
* t8_forest_get_leaf_element_in_tree to get a pointer to the current element.
123
* This is the recommended and most performant way.
124
* An alternative is to iterate over the number of local elements and use
125
* t8_forest_get_leaf_element. However, this function needs to perform a binary search
126
* for the element and the tree it is in, while t8_forest_get_leaf_element_in_tree has a
127
* constant look up time. You should only use t8_forest_get_leaf_element if you do not know
128
* in which tree an element is.
129
*/
130
{
131
t8_locidx_t itree, num_local_trees;
132
t8_locidx_t current_index;
133
t8_locidx_t ielement, num_elements_in_tree;
134
const t8_element_t *element;
135
136
/* Get the scheme of the forest for later usage. */
137
const t8_scheme_c *scheme = t8_forest_get_scheme (forest);
138
/* Get the number of trees that have elements of this process. */
139
num_local_trees = t8_forest_get_num_local_trees (forest);
140
for (itree = 0, current_index = 0; itree < num_local_trees; ++itree) {
141
/* This loop iterates through all local trees in the forest. */
142
/* Each tree may have a different tree class (quad/tri/hex/tet etc.) and therefore
143
* also a different way to interpret its elements. In order to be able to handle elements
144
* of a tree, we need the scheme we obtained earlier and in order to use it we get the eclass of the tree. */
145
const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree);
146
/* Get the number of elements of this tree. */
147
num_elements_in_tree = t8_forest_get_tree_num_leaf_elements (forest, itree);
148
for (ielement = 0; ielement < num_elements_in_tree; ++ielement, ++current_index) {
149
/* This loop iterates through all the local elements of the forest in the current tree. */
150
151
/* We can now write to the position current_index into our array in order to store
152
* data for this element. */
153
/* Since in this example we want to compute the data based on the element in question,
154
* we need to get a pointer to this element. */
155
element = t8_forest_get_leaf_element_in_tree (forest, itree, ielement);
156
/* We want to store the elements level and its volume as data. We compute these
157
* via the eclass_scheme and the forest_element interface. */
158
159
element_data[current_index].level = t8_element_get_level (forest, tree_class, element);
160
element_data[current_index].volume = t8_forest_element_volume (forest, itree, element);
161
}
162
}
163
}
164
return element_data;
165
}
166
167
/* Each process has computed the data entries for its local elements.
168
* In order to get the values for the ghost elements, we use t8_forest_ghost_exchange_data.
169
* Calling this function will fill all the ghost entries of our element data array with the
170
* value on the process that owns the corresponding element. */
171
static void
172
t8_step5_exchange_ghost_data (t8_forest_t forest, struct t8_step5_data_per_element *data)
173
{
174
sc_array_t *sc_array_wrapper;
175
t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest);
176
t8_locidx_t num_ghosts = t8_forest_get_num_ghosts (forest);
177
178
/* t8_forest_ghost_exchange_data expects an sc_array (of length num_local_elements + num_ghosts).
179
* We wrap our data array to an sc_array. */
180
sc_array_wrapper = sc_array_new_data (data, sizeof (struct t8_step5_data_per_element), num_elements + num_ghosts);
181
182
/* Carry out the data exchange. The entries with indices > num_local_elements will get overwritten.
183
*/
184
t8_forest_ghost_exchange_data (forest, sc_array_wrapper);
185
186
/* Destroy the wrapper array. This will not free the data memory since we used sc_array_new_data. */
187
sc_array_destroy (sc_array_wrapper);
188
}
189
190
/* Write the forest as vtu and also write the element's volumes in the file.
191
*
192
* t8code supports writing element based data to vtu as long as its stored
193
* as doubles. Each of the data fields to write has to be provided in its own
194
* array of length num_local_elements.
195
* We support two types: T8_VTK_SCALAR - One double per element
196
* and T8_VTK_VECTOR - 3 doubles per element
197
*/
198
static void
199
t8_step5_output_data_to_vtu (t8_forest_t forest, struct t8_step5_data_per_element *data, const char *prefix)
200
{
201
t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest);
202
t8_locidx_t ielem;
203
/* We need to allocate a new array to store the volumes on their own.
204
* This array has one entry per local element. */
205
double *element_volumes = T8_ALLOC (double, num_elements);
206
/* The number of user defined data fields to write. */
207
int num_data = 1;
208
/* For each user defined data field we need one t8_vtk_data_field_t variable */
209
t8_vtk_data_field_t vtk_data;
210
/* Set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR */
211
vtk_data.type = T8_VTK_SCALAR;
212
/* The name of the field as should be written to the file. */
213
strcpy (vtk_data.description, "Element volume");
214
vtk_data.data = element_volumes;
215
/* Copy the element's volumes from our data array to the output array. */
216
for (ielem = 0; ielem < num_elements; ++ielem) {
217
element_volumes[ielem] = data[ielem].volume;
218
}
219
{
220
/* To write user defined data, we need to extended output function t8_forest_vtk_write_file
221
* from t8_forest_vtk.h. Despite writing user data, it also offers more control over which
222
* properties of the forest to write. */
223
int write_treeid = 1;
224
int write_mpirank = 1;
225
int write_level = 1;
226
int write_element_id = 1;
227
int write_ghosts = 0;
228
t8_forest_write_vtk_ext (forest, prefix, write_treeid, write_mpirank, write_level, write_element_id, write_ghosts,
229
0, 0, num_data, &vtk_data);
230
}
231
T8_FREE (element_volumes);
232
}
233
234
int
235
t8_step5_main (int argc, char **argv)
236
{
237
int mpiret;
238
sc_MPI_Comm comm;
239
t8_forest_t forest;
240
/* The prefix for our output files. */
241
const char *prefix_forest = "t8_step5_forest";
242
const char *prefix_forest_with_data = "t8_step5_forest_with_volume_data";
243
/* The uniform refinement level of the forest. */
244
const int level = 3;
245
/* The array that will hold our per element data. */
246
struct t8_step5_data_per_element *data;
247
248
/* Initialize MPI. This has to happen before we initialize sc or t8code. */
249
mpiret = sc_MPI_Init (&argc, &argv);
250
/* Error check the MPI return value. */
251
SC_CHECK_MPI (mpiret);
252
253
/* Initialize the sc library, has to happen before we initialize t8code. */
254
sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL);
255
/* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */
256
t8_init (SC_LP_PRODUCTION);
257
258
/* Print a message on the root process. */
259
t8_global_productionf (" [step5] \n");
260
t8_global_productionf (" [step5] Hello, this is the step5 example of t8code.\n");
261
t8_global_productionf (
262
" [step5] In this example we will store data on our elements and exchange the data of ghost elements.\n");
263
t8_global_productionf (" [step5] \n");
264
265
/* We will use MPI_COMM_WORLD as a communicator. */
266
comm = sc_MPI_COMM_WORLD;
267
268
/*
269
* Setup.
270
* Build cmesh and uniform forest.
271
* Adapt forest similar to step 3 & 4.
272
*/
273
274
t8_global_productionf (" [step5] \n");
275
t8_global_productionf (" [step5] Creating an adapted forest as in step3.\n");
276
t8_global_productionf (" [step5] \n");
277
278
forest = t8_step5_build_forest (comm, level);
279
t8_forest_write_vtk (forest, prefix_forest);
280
t8_global_productionf (" [step5] Wrote forest to vtu files: %s*\n", prefix_forest);
281
282
/*
283
* Build data array and gather data for the local elements.
284
*/
285
data = t8_step5_create_element_data (forest);
286
287
t8_global_productionf (" [step5] Computed level and volume data for local elements.\n");
288
if (t8_forest_get_local_num_leaf_elements (forest) > 0) {
289
/* Output the stored data of the first local element (if it exists). */
290
t8_global_productionf (" [step5] Element 0 has level %i and volume %e.\n", data[0].level, data[0].volume);
291
}
292
293
/*
294
* Exchange the data values of the ghost elements
295
*/
296
t8_step5_exchange_ghost_data (forest, data);
297
t8_global_productionf (" [step5] Exchanged ghost data.\n");
298
299
if (t8_forest_get_num_ghosts (forest) > 0) {
300
/* output the data of the first ghost element (if it exists) */
301
t8_locidx_t first_ghost_index = t8_forest_get_local_num_leaf_elements (forest);
302
t8_global_productionf (" [step5] Ghost 0 has level %i and volume %e.\n", data[first_ghost_index].level,
303
data[first_ghost_index].volume);
304
}
305
306
/*
307
* Output the volume data to vtu.
308
*/
309
t8_step5_output_data_to_vtu (forest, data, prefix_forest_with_data);
310
t8_global_productionf (" [step5] Wrote forest and volume data to %s*.\n", prefix_forest_with_data);
311
312
/*
313
* clean-up
314
*/
315
316
/* Free the data array. */
317
T8_FREE (data);
318
319
/* Destroy the forest. */
320
t8_forest_unref (&forest);
321
t8_global_productionf (" [step5] Destroyed forest.\n");
322
323
sc_finalize ();
324
325
mpiret = sc_MPI_Finalize ();
326
SC_CHECK_MPI (mpiret);
327
328
return 0;
329
}
330
331
T8_EXTERN_C_END ();
332
333