Path: blob/main/tutorials/general/t8_step5_element_data_c_interface.c
903 views
/*1This file is part of t8code.2t8code is a C library to manage a collection (a forest) of multiple3connected adaptive space-trees of general element types in parallel.45Copyright (C) 2015 the developers67t8code is free software; you can redistribute it and/or modify8it under the terms of the GNU General Public License as published by9the Free Software Foundation; either version 2 of the License, or10(at your option) any later version.1112t8code is distributed in the hope that it will be useful,13but WITHOUT ANY WARRANTY; without even the implied warranty of14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15GNU General Public License for more details.1617You should have received a copy of the GNU General Public License18along with t8code; if not, write to the Free Software Foundation, Inc.,1951 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.20*/2122/* See also: https://github.com/DLR-AMR/t8code/wiki/Step-5---Store-element-data23*24* This is step5 of the t8code tutorials using the C interface of t8code.25* In the following we will store data in the individual elements of our forest.26* To do this, we will again create a uniform forest, which will get adapted as in step4,27* with the difference that we partition, balance and create ghost elements all in the same step.28* After adapting the forest we will learn how to build a data array and gather data for29* the local elements. Furthermore, we exchange the data values of the ghost elements and30* output the volume data to vtu.31*32* How you can experiment here:33* - Look at the paraview output files of the adapted forest.34* You can apply a clip filter to look into the cube. Also you can apply (in addition)35* the threshold filter to display only elements with certain properties.36* But at first you may just want to enter the tooltip selection mode 'Hover Cells On'37* to display cell information when hover over them.38* - Change the adaptation criterion as you wish to adapt elements or families as desired.39* - Store even more data per element, for instance the coordinates of its midpoint.40* You can again apply the threshold filter to your new data. Don't forget to write the41* data into the output file.42* */4344#include <sc_containers.h> /* sc library. */45#include <t8.h> /* General t8code header, always include this. */46#include <t8_cmesh/t8_cmesh.h> /* cmesh definition and basic interface. */47#include <t8_cmesh/t8_cmesh_examples.h> /* A collection of exemplary cmeshes. */48#include <t8_forest/t8_forest_general.h> /* forest definition and basic interface. */49#include <t8_forest/t8_forest_geometrical.h> /* geometrical information of a forest. */50#include <t8_forest/t8_forest_io.h> /* save forest. */51#include <t8_schemes/t8_default/t8_default_c_interface.h> /* default refinement scheme. */52#include <t8_schemes/t8_scheme.h> /* C interface of the forest scheme */53#include <tutorials/general/t8_step3.h>5455T8_EXTERN_C_BEGIN ();5657/* The data that we want to store for each element.58* In this example we want to store the element's level and volume. */59struct t8_step5_data_per_element60{61int level;62double volume;63};6465static t8_forest_t66t8_step5_build_forest (sc_MPI_Comm comm, int level)67{68t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (comm, 0, 0);69const t8_scheme_c *scheme = t8_scheme_new_default ();70struct t8_step3_adapt_data adapt_data = {71{ 0.5, 0.5, 1 }, /* Midpoints of the sphere. */720.2, /* Refine if inside this radius. */730.4 /* Coarsen if outside this radius. */74};75/* Start with a uniform forest. */76t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, comm);77t8_forest_t forest_apbg;7879/* Adapt, partition, balance and create ghost elements all in the same step. */80t8_forest_init (&forest_apbg);81t8_forest_set_user_data (forest_apbg, &adapt_data);82t8_forest_set_adapt (forest_apbg, forest, t8_step3_adapt_callback, 0);83t8_forest_set_partition (forest_apbg, NULL, 0);84t8_forest_set_balance (forest_apbg, NULL, 0);85t8_forest_set_ghost (forest_apbg, 1, T8_GHOST_FACES);86t8_forest_commit (forest_apbg);8788return forest_apbg;89}9091static struct t8_step5_data_per_element *92t8_step5_create_element_data (t8_forest_t forest)93{94t8_locidx_t num_local_elements;95t8_locidx_t num_ghost_elements;96struct t8_step5_data_per_element *element_data;9798/* Check that forest is a committed, that is valid and usable, forest. */99T8_ASSERT (t8_forest_is_committed (forest));100101/* Get the number of local elements of forest. */102num_local_elements = t8_forest_get_local_num_leaf_elements (forest);103/* Get the number of ghost elements of forest. */104num_ghost_elements = t8_forest_get_num_ghosts (forest);105106/* Now we need to build an array of our data that is as long as the number107* of elements plus the number of ghosts. You can use any allocator such as108* new, malloc or the t8code provide allocation macro T8_ALLOC.109* Note that in the latter case you need110* to use T8_FREE in order to free the memory.111*/112element_data = T8_ALLOC (struct t8_step5_data_per_element, num_local_elements + num_ghost_elements);113/* Note: We will later need to associate this data with an sc_array in order to exchange the values for114* the ghost elements, which we can do with sc_array_new_data (see t8_step5_exchange_ghost_data).115* We could also have directly allocated the data here in an sc_array with116* sc_array_new_count (sizeof (struct data_per_element), num_local_elements + num_ghost_elements);117*/118119/* Let us now fill the data with something.120* For this, we iterate through all trees and for each tree through all its elements, calling121* t8_forest_get_leaf_element_in_tree to get a pointer to the current element.122* This is the recommended and most performant way.123* An alternative is to iterate over the number of local elements and use124* t8_forest_get_leaf_element. However, this function needs to perform a binary search125* for the element and the tree it is in, while t8_forest_get_leaf_element_in_tree has a126* constant look up time. You should only use t8_forest_get_leaf_element if you do not know127* in which tree an element is.128*/129{130t8_locidx_t itree, num_local_trees;131t8_locidx_t current_index;132t8_locidx_t ielement, num_elements_in_tree;133const t8_element_t *element;134135/* Get the scheme of the forest for later usage. */136const t8_scheme_c *scheme = t8_forest_get_scheme (forest);137/* Get the number of trees that have elements of this process. */138num_local_trees = t8_forest_get_num_local_trees (forest);139for (itree = 0, current_index = 0; itree < num_local_trees; ++itree) {140/* This loop iterates through all local trees in the forest. */141/* Each tree may have a different tree class (quad/tri/hex/tet etc.) and therefore142* also a different way to interpret its elements. In order to be able to handle elements143* of a tree, we need the scheme we obtained earlier and in order to use it we get the eclass of the tree. */144const t8_eclass_t tree_class = t8_forest_get_tree_class (forest, itree);145/* Get the number of elements of this tree. */146num_elements_in_tree = t8_forest_get_tree_num_leaf_elements (forest, itree);147for (ielement = 0; ielement < num_elements_in_tree; ++ielement, ++current_index) {148/* This loop iterates through all the local elements of the forest in the current tree. */149150/* We can now write to the position current_index into our array in order to store151* data for this element. */152/* Since in this example we want to compute the data based on the element in question,153* we need to get a pointer to this element. */154element = t8_forest_get_leaf_element_in_tree (forest, itree, ielement);155/* We want to store the elements level and its volume as data. We compute these156* via the eclass_scheme and the forest_element interface. */157158element_data[current_index].level = t8_element_get_level (forest, tree_class, element);159element_data[current_index].volume = t8_forest_element_volume (forest, itree, element);160}161}162}163return element_data;164}165166/* Each process has computed the data entries for its local elements.167* In order to get the values for the ghost elements, we use t8_forest_ghost_exchange_data.168* Calling this function will fill all the ghost entries of our element data array with the169* value on the process that owns the corresponding element. */170static void171t8_step5_exchange_ghost_data (t8_forest_t forest, struct t8_step5_data_per_element *data)172{173sc_array_t *sc_array_wrapper;174t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest);175t8_locidx_t num_ghosts = t8_forest_get_num_ghosts (forest);176177/* t8_forest_ghost_exchange_data expects an sc_array (of length num_local_elements + num_ghosts).178* We wrap our data array to an sc_array. */179sc_array_wrapper = sc_array_new_data (data, sizeof (struct t8_step5_data_per_element), num_elements + num_ghosts);180181/* Carry out the data exchange. The entries with indices > num_local_elements will get overwritten.182*/183t8_forest_ghost_exchange_data (forest, sc_array_wrapper);184185/* Destroy the wrapper array. This will not free the data memory since we used sc_array_new_data. */186sc_array_destroy (sc_array_wrapper);187}188189/* Write the forest as vtu and also write the element's volumes in the file.190*191* t8code supports writing element based data to vtu as long as its stored192* as doubles. Each of the data fields to write has to be provided in its own193* array of length num_local_elements.194* We support two types: T8_VTK_SCALAR - One double per element195* and T8_VTK_VECTOR - 3 doubles per element196*/197static void198t8_step5_output_data_to_vtu (t8_forest_t forest, struct t8_step5_data_per_element *data, const char *prefix)199{200t8_locidx_t num_elements = t8_forest_get_local_num_leaf_elements (forest);201t8_locidx_t ielem;202/* We need to allocate a new array to store the volumes on their own.203* This array has one entry per local element. */204double *element_volumes = T8_ALLOC (double, num_elements);205/* The number of user defined data fields to write. */206int num_data = 1;207/* For each user defined data field we need one t8_vtk_data_field_t variable */208t8_vtk_data_field_t vtk_data;209/* Set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR */210vtk_data.type = T8_VTK_SCALAR;211/* The name of the field as should be written to the file. */212strcpy (vtk_data.description, "Element volume");213vtk_data.data = element_volumes;214/* Copy the element's volumes from our data array to the output array. */215for (ielem = 0; ielem < num_elements; ++ielem) {216element_volumes[ielem] = data[ielem].volume;217}218{219/* To write user defined data, we need to extended output function t8_forest_vtk_write_file220* from t8_forest_vtk.h. Despite writing user data, it also offers more control over which221* properties of the forest to write. */222int write_treeid = 1;223int write_mpirank = 1;224int write_level = 1;225int write_element_id = 1;226int write_ghosts = 0;227t8_forest_write_vtk_ext (forest, prefix, write_treeid, write_mpirank, write_level, write_element_id, write_ghosts,2280, 0, num_data, &vtk_data);229}230T8_FREE (element_volumes);231}232233int234t8_step5_main (int argc, char **argv)235{236int mpiret;237sc_MPI_Comm comm;238t8_forest_t forest;239/* The prefix for our output files. */240const char *prefix_forest = "t8_step5_forest";241const char *prefix_forest_with_data = "t8_step5_forest_with_volume_data";242/* The uniform refinement level of the forest. */243const int level = 3;244/* The array that will hold our per element data. */245struct t8_step5_data_per_element *data;246247/* Initialize MPI. This has to happen before we initialize sc or t8code. */248mpiret = sc_MPI_Init (&argc, &argv);249/* Error check the MPI return value. */250SC_CHECK_MPI (mpiret);251252/* Initialize the sc library, has to happen before we initialize t8code. */253sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL);254/* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */255t8_init (SC_LP_PRODUCTION);256257/* Print a message on the root process. */258t8_global_productionf (" [step5] \n");259t8_global_productionf (" [step5] Hello, this is the step5 example of t8code.\n");260t8_global_productionf (261" [step5] In this example we will store data on our elements and exchange the data of ghost elements.\n");262t8_global_productionf (" [step5] \n");263264/* We will use MPI_COMM_WORLD as a communicator. */265comm = sc_MPI_COMM_WORLD;266267/*268* Setup.269* Build cmesh and uniform forest.270* Adapt forest similar to step 3 & 4.271*/272273t8_global_productionf (" [step5] \n");274t8_global_productionf (" [step5] Creating an adapted forest as in step3.\n");275t8_global_productionf (" [step5] \n");276277forest = t8_step5_build_forest (comm, level);278t8_forest_write_vtk (forest, prefix_forest);279t8_global_productionf (" [step5] Wrote forest to vtu files: %s*\n", prefix_forest);280281/*282* Build data array and gather data for the local elements.283*/284data = t8_step5_create_element_data (forest);285286t8_global_productionf (" [step5] Computed level and volume data for local elements.\n");287if (t8_forest_get_local_num_leaf_elements (forest) > 0) {288/* Output the stored data of the first local element (if it exists). */289t8_global_productionf (" [step5] Element 0 has level %i and volume %e.\n", data[0].level, data[0].volume);290}291292/*293* Exchange the data values of the ghost elements294*/295t8_step5_exchange_ghost_data (forest, data);296t8_global_productionf (" [step5] Exchanged ghost data.\n");297298if (t8_forest_get_num_ghosts (forest) > 0) {299/* output the data of the first ghost element (if it exists) */300t8_locidx_t first_ghost_index = t8_forest_get_local_num_leaf_elements (forest);301t8_global_productionf (" [step5] Ghost 0 has level %i and volume %e.\n", data[first_ghost_index].level,302data[first_ghost_index].volume);303}304305/*306* Output the volume data to vtu.307*/308t8_step5_output_data_to_vtu (forest, data, prefix_forest_with_data);309t8_global_productionf (" [step5] Wrote forest and volume data to %s*.\n", prefix_forest_with_data);310311/*312* clean-up313*/314315/* Free the data array. */316T8_FREE (data);317318/* Destroy the forest. */319t8_forest_unref (&forest);320t8_global_productionf (" [step5] Destroyed forest.\n");321322sc_finalize ();323324mpiret = sc_MPI_Finalize ();325SC_CHECK_MPI (mpiret);326327return 0;328}329330T8_EXTERN_C_END ();331332333