diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 4c21e232db..27d84996de 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -82,5 +82,8 @@ copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_tet.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_tri.geo) if( T8CODE_BUILD_MESH_HANDLE ) - add_mesh_handle_tutorial( NAME t8_mesh_element_data SOURCES mesh_handle/t8_mesh_element_data.cxx ) + add_mesh_handle_tutorial( NAME t8_mesh_step3_adapt_mesh SOURCES mesh_handle/t8_mesh_step3_adapt_mesh.cxx ) + add_mesh_handle_tutorial( NAME t8_mesh_step4_partition_balance_ghost SOURCES mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx ) + add_mesh_handle_tutorial( NAME t8_mesh_step5_element_data SOURCES mesh_handle/t8_mesh_step5_element_data.cxx ) endif() + diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_mesh.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_mesh.cxx new file mode 100644 index 0000000000..d0942e43ae --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_step3_adapt_mesh.cxx @@ -0,0 +1,117 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element types in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_mesh_step3_adapt_mesh.cxx + * This is step3 of the t8code mesh handle tutorials. + * Therefore, this is the same as general/t8_step3_adapt_forest.cxx but using the mesh handle interface instead of the forest + * interface. + * After generating a coarse mesh (step1) and building a uniform mesh + * on it (step2), we will now adapt (= refine and coarsen) the mesh + * according to our own criterion. + * + * The geometry (coarse mesh) is again a cube, this time modelled with + * 6 tetrahedra, 6 prisms and 4 cubes. + * We refine an element if its midpoint is within a sphere of given radius + * around the point (0.5, 0.5, 1) and we coarsen outside of a given radius. + * We will use non-recursive refinement, that means that the refinement level + * of any element will change by at most +-1. +*/ + +#include /** General t8code header. Always include this. */ +#include /** General mesh header. Always needed for mesh_handle code. */ +#include /** Competence pack for basic mesh_handle features. Look into tutorials/mesh_handle/t8_mesh_competences for more information. */ +#include /** Wrapper for basic cmesh to mesh_handle conversions. */ +#include /** Used to export mesh to vtk files. */ +#include /** Include this to use c++ concepts related to the mesh handle. This can be used to constraint the template parameters to only allow mesh handle classes. */ +#include "t8_mesh_tutorials_common.hxx" /** Adaption function definition used for this tutorial. */ +#include + +/** Build our adapted mesh by transferring the adaption parameters and adapting once with our \ref adapt_callback function. + * \tparam TMeshClass The mesh handle class. + * \param sc_MPI_Comm The MPI communicator. + * \param level The initial uniform refinement level. + * \returns Unique pointer to the adapted mesh. + */ +template +std::unique_ptr +build_mesh (sc_MPI_Comm comm, int level) +{ + /* Generate a hybrid hypercube, made out of cubes, prisms etc. */ + auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, comm); + /* Defining the adaption parameters. */ + adapt_data adapt_params = { { 0.5, 0.5, 1.0 }, 0.2, 0.4 }; + /** Adapting once using our adapt callback. + * set_adapt() only records how the mesh should be changed, it does not modify anything yet. + * commit() is the function that actually builds the new, adapted mesh from these settings. + * This "configure, then commit" split let's t8code carry out several mesh operations together in one efficient pass, rather than one at a time. + */ + mesh->set_adapt ( + TMeshClass::template mesh_adapt_callback_wrapper (adapt_callback_sphere, adapt_params)); + mesh->commit (); + return mesh; +} + +/** Entry point of the program. */ +int +main (int argc, char **argv) +{ + /* Initialize MPI. This has to happen before we initialize sc or t8code. */ + int mpiret = sc_MPI_Init (&argc, &argv); + /* Error check the MPI return value. */ + SC_CHECK_MPI (mpiret); + /* Initialize the sc library, has to happen before we initialize t8code. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + /* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */ + t8_init (SC_LP_PRODUCTION); + /* We will use MPI_COMM_WORLD as a communicator. */ + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + + /* Print a starting message. */ + t8_global_productionf (" [mesh_step3] \n"); + t8_global_productionf ( + " [mesh_step3] Hello, this is the mesh adaptation tutorial of t8code using the mesh handle.\n"); + t8_global_productionf ( + " [mesh_step3] In this tutorial we will adapt a mesh in a spherical shape around a given point " + "and write the adapted mesh to a vtu file.\n"); + t8_global_productionf (" [mesh_step3] \n"); + + using mesh_type = t8_mesh_handle::mesh<>; + + t8_global_productionf (" [mesh_step3] \n"); + t8_global_productionf (" [mesh_step3] Creating an adapted mesh.\n"); + t8_global_productionf (" [mesh_step3] \n"); + /* The initial uniform refinement level. */ + int uniform_level = 3; + /* Building the mesh. */ + { /** Scope to ensure mesh is deleted properly. */ + auto mesh = build_mesh (comm, uniform_level); + /* Write the mesh to a vtu file. */ + t8_global_productionf (" [mesh_step3] \n"); + t8_global_productionf (" [mesh_step3] Writing adapted mesh to vtu file: step3_adapted_mesh.vtu\n"); + t8_global_productionf (" [mesh_step3] \n"); + t8_mesh_handle::write_mesh_to_vtk (*mesh, "step3_adapted_mesh.vtu"); + } + sc_finalize (); + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + return 0; +} diff --git a/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx b/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx new file mode 100644 index 0000000000..7c48b38e47 --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx @@ -0,0 +1,217 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element types in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_mesh_element_data.cxx + * This is step4 of the t8code mesh handle tutorials. + * Therefore, this is the same as general/t8_step4_partition_balance_ghost.cxx but using the mesh handle interface instead of the forest + * interface. + * After generating a coarse mesh (step1), building a uniform mesh + * on it (step2) and adapting this mesh (step3) + * we will now learn how to control the mesh creation in more detail, + * how to partition and balance a mesh and how to generate a layer of ghost elements. + */ + +#include /** General t8code header. Always include this. */ +#include /** General mesh header. Always needed for mesh_handle code. */ +#include /** Used to export mesh to vtk files. */ +#include /** Wrapper for basic cmesh to mesh_handle conversions. */ +#include /** Include this to use c++ concepts related to the mesh handle. This can be used to constraint the template parameters to only allow mesh handle classes. */ +#include /** t8 vector dataclass. */ +#include "t8_mesh_tutorials_common.hxx" /** Default adaption function. */ +#include +#include + +using mesh_type = t8_mesh_handle:: + mesh<>; /**< Mesh class used in this tutorial. We define it globally to get rid of the function templates to simplify the code. */ + +/** Helper function to print the total number of elements in the mesh after each step. + * \param mesh The mesh handle to get the number of elements from. + * \param stage The stage of the mesh (e.g. "Initial mesh", "Adapted mesh", etc.) to print in the output. + * \param comm The MPI communicator to use for the reduction and printing. +*/ +void +print_mesh_stats (const std::unique_ptr& mesh, const char* stage, sc_MPI_Comm comm) +{ + int local_elements = mesh->get_num_local_elements (); + int global_elements = 0; + MPI_Allreduce (&local_elements, &global_elements, 1, MPI_INT, MPI_SUM, comm); + + t8_global_productionf (" [mesh_step4] === %s === \n", stage); + t8_global_productionf (" [mesh_step4] Total elements: %i \n", global_elements); +} + +/** Helper function to create an adapted mesh from an initial mesh. + * \param mesh The initial mesh to adapt. + * \param adapt_params The adaptation parameters to use for the adaptation. +*/ +void +create_adapted_mesh (std::unique_ptr& mesh, const adapt_data& adapt_params) +{ + /* Adapting the mesh once with our adapt_callback_sphere function from step 3 and the adapt_params. Both can be found in the file \ref t8_mesh_tutorials_common.hxx. */ + mesh->set_adapt ( + mesh_type::template mesh_adapt_callback_wrapper (&adapt_callback_sphere, adapt_params)); + /* Committing the adapted mesh. */ + mesh->commit (); +} + +/** Helper function to create a partitioned and balanced mesh from an initial mesh. + * \param mesh The initial mesh to adapt. +*/ +void +create_partitioned_balanced_mesh (const std::unique_ptr& mesh) +{ + /* Calculate partition information.*/ + mesh->set_partition (); + + /* Calculate balancing information. */ + mesh->set_balance (); + + /* Committing the mesh --> modifying the mesh according to the precalculated information. */ + mesh->commit (); +} + +/** Helper function to create a mesh with ghosts from an initial mesh. + * \param mesh The initial mesh to adapt. +*/ +void +create_ghost_mesh (const std::unique_ptr& mesh) +{ + /* Creating the ghost layers. */ + mesh->set_ghost (); + + /* Committing the ghost mesh. */ + mesh->commit (); +} + +/** Entry point of the program. */ +int +main (int argc, char** argv) +{ + + /* Initialize MPI. This has to happen before we initialize sc or t8code. */ + int mpiret = sc_MPI_Init (&argc, &argv); + /* Error check the MPI return value. */ + SC_CHECK_MPI (mpiret); + /* Initialize the sc library, has to happen before we initialize t8code. */ + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + /* Initialize t8code with log level SC_LP_PRODUCTION. See sc.h for more info on the log levels. */ + t8_init (SC_LP_PRODUCTION); + /* We will use MPI_COMM_WORLD as a communicator. */ + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + + /* Print a starting message. */ + t8_global_productionf (" [mesh_step4] \n"); + t8_global_productionf (" [mesh_step4] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); + t8_global_productionf (" [mesh_step4] In this example we will create a mesh, adapt, partition, balance " + "and create a ghost layer on it. \n"); + t8_global_productionf (" [mesh_step4] \n"); + + /* The initial uniform refinement level. */ + int uniform_level = 3; + + /* Parameters for the adaption step. */ + struct adapt_data adapt_params = { { 0.5, 0.5, 1.0 }, 0.2, 0.4 }; + + /** + * INITIAL MESH + */ + + t8_global_productionf (" [mesh_step4] \n"); + t8_global_productionf (" [mesh_step4] Creating initial mesh.\n"); + t8_global_productionf (" [mesh_step4] \n"); + { /** Mesh scope begin. */ + /* Creating the initial mesh with uniform refinement. */ + auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (uniform_level, comm); + + /* Printing the mesh information. */ + print_mesh_stats (mesh, "Initial mesh", comm); + + /* Writing the mesh to vtu and pvtu files, using the extended version of the function to ensure additional data like ghost elements, treeid etc. to be written into the files. */ + t8_mesh_handle::write_mesh_to_vtk_ext (*mesh, "initial_mesh.vtu", 0, nullptr, true, true, true, true, true, false, + false); + + /** + * ADAPTED MESH + */ + + t8_global_productionf (" [mesh_step4] \n"); + t8_global_productionf (" [mesh_step4] Creating adapted mesh.\n"); + t8_global_productionf (" [mesh_step4] \n"); + + /** Call adaption helper function. */ + create_adapted_mesh (mesh, adapt_params); + + /* Printing the mesh information. */ + print_mesh_stats (mesh, "Adapted mesh", comm); + + /* Writing the mesh to vtu and pvtu files using the extended version of the function. */ + t8_mesh_handle::write_mesh_to_vtk_ext (*mesh, "adapted_mesh.vtu", 0, nullptr, true, true, true, true, true, false, + false); + + /** + * PARTITIONED, BALANCED MESH + */ + + t8_global_productionf (" [mesh_step4] \n"); + t8_global_productionf (" [mesh_step4] Creating partitioned and balanced mesh.\n"); + t8_global_productionf (" [mesh_step4] \n"); + + /** Adapting the mesh from above a second time to see a difference when balancing. */ + create_adapted_mesh (mesh, adapt_params); + + /** Call partitioning and balancing helper function. */ + create_partitioned_balanced_mesh (mesh); + + /* Printing the mesh information. */ + print_mesh_stats (mesh, "Partitioned and Balanced mesh", comm); + + /* Writing the mesh to vtu and pvtu files using the extended version of the function. */ + t8_mesh_handle::write_mesh_to_vtk_ext (*mesh, "partition_balance_mesh.vtu", 0, nullptr, true, true, true, true, + true, false, false); + + /** + * GHOST MESH + */ + + t8_global_productionf (" [mesh_step4] \n"); + t8_global_productionf (" [mesh_step4] Creating ghost layer for mesh.\n"); + t8_global_productionf (" [mesh_step4] \n"); + + /** Call ghost helper function. */ + create_ghost_mesh (mesh); + + /* Printing the mesh information. */ + print_mesh_stats (mesh, "Ghost mesh", comm); + + /* Writing the mesh to vtu and pvtu files using the extended version of the function. */ + t8_mesh_handle::write_mesh_to_vtk_ext (*mesh, "ghost_mesh.vtu", 0, nullptr, true, true, true, true, true, false, + false); + + t8_global_productionf (" [mesh_step4] \n"); + t8_global_productionf (" [mesh_step4] Finished all steps successfully.\n"); + t8_global_productionf (" [mesh_step4] \n"); + } /** Mesh scope end. */ + sc_finalize (); + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + return 0; +} diff --git a/tutorials/mesh_handle/t8_mesh_element_data.cxx b/tutorials/mesh_handle/t8_mesh_step5_element_data.cxx similarity index 70% rename from tutorials/mesh_handle/t8_mesh_element_data.cxx rename to tutorials/mesh_handle/t8_mesh_step5_element_data.cxx index 03249f0002..bda91a69c8 100644 --- a/tutorials/mesh_handle/t8_mesh_element_data.cxx +++ b/tutorials/mesh_handle/t8_mesh_step5_element_data.cxx @@ -20,19 +20,27 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file t8_mesh_element_data.cxx - * This is the same as general/t8_step5_element_data.cxx but using the mesh handle interface instead of the forest +/** \file t8_mesh_step5_element_data.cxx + * This is step5 of the mesh handle tutorials. + * Therefore, this is the same as general/t8_step5_element_data.cxx but using the mesh handle interface instead of the forest * interface. + * In the following we will store data in the individual elements of our mesh. + * To do this, we will again create a uniform mesh, which will get adapted as in step4, + * with the difference that we partition, balance and create ghost elements all in the same step. + * After adapting the mesh we will learn how to build a data array and gather data for + * the local elements. Furthermore, we exchange the data values of the ghost elements and + * output the volume data to vtu. */ -#include +#include /** General t8code header. Always include this. */ -#include -#include -#include -#include -#include -#include +#include /** General mesh header. Always needed for mesh_handle code. */ +#include /** Competence pack for basic mesh_handle features. Look into tutorials/mesh_handle/t8_mesh_competences for more information. */ +#include /** Wrapper for basic cmesh to mesh_handle conversions. */ +#include /** Used to export mesh to vtk files. */ +#include /** Include this to use c++ concepts related to the mesh handle. This can be used to constraint the template parameters to only allow mesh handle classes. */ +#include /** t8 vector dataclass. */ +#include "t8_mesh_tutorials_common.hxx" /** Default adaption function. */ #include #include @@ -44,41 +52,6 @@ struct data_per_element_type double volume; /**< Volume of the element. */ }; -/** User data type we will pass to the adapt callback. */ -struct user_data -{ - t8_3D_vec midpoint; /**< The midpoint of our sphere. */ - double refine_if_inside_radius; /**< If an element's center is smaller than this value, we refine the element. */ - double coarsen_if_outside_radius; /**< If an element's center is larger this value, we coarsen its family. */ -}; - -/** The adaptation callback function. This will refine elements inside of a given sphere and coarsen the elements - * outside of a given sphere. - * \tparam TMeshClass The mesh handle class. - * \param [in] mesh The mesh that should be adapted. - * \param [in] elements One element or a family of elements to consider for adaptation. - * \param [in] user_data The user data to be used during the adaptation process. - * \return 1 if the first entry in \a elements should be refined, - * -1 if the family \a elements shall be coarsened, - * 0 else. - */ -template -int -adapt_callback ([[maybe_unused]] const TMeshClass &mesh, std::span elements, - const user_data &user_data) -{ - auto element_centroid = elements[0].get_centroid (); - double dist = t8_dist (element_centroid, user_data.midpoint); - if (dist < user_data.refine_if_inside_radius) { - return 1; - } - // Check if we got a family and if yes, if we should coarsen. - if ((elements.size () > 1) && (dist > user_data.coarsen_if_outside_radius)) { - return -1; - } - return 0; -} - /** Build a mesh with initial uniform refinement level \a level which is adapted according to \ref adapt_callback, * partitioned and balanced afterwards, and ghost elements are set. * \tparam TMeshClass The mesh handle class. @@ -91,7 +64,7 @@ std::unique_ptr build_mesh (sc_MPI_Comm comm, int level) { auto mesh_handle = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, comm); - struct user_data adapt_data = { + struct adapt_data adapt_params = { { 0.5, 0.5, 1 }, /* Midpoint of the sphere. */ 0.2, /* Refine if inside this radius. */ 0.4 /* Coarsen if outside this radius. */ @@ -100,7 +73,7 @@ build_mesh (sc_MPI_Comm comm, int level) mesh_handle->set_balance (); mesh_handle->set_partition (); mesh_handle->set_adapt ( - TMeshClass::template mesh_adapt_callback_wrapper (adapt_callback, adapt_data)); + TMeshClass::template mesh_adapt_callback_wrapper (&adapt_callback_sphere, adapt_params)); mesh_handle->set_ghost (); mesh_handle->commit (); return mesh_handle; @@ -191,16 +164,16 @@ main (int argc, char **argv) sc_MPI_Comm comm = sc_MPI_COMM_WORLD; /* Print a message on the root process. */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Hello, this is the element data example of t8code using the mesh handle.\n"); - t8_global_productionf ( - " [tutorial] In this example we will store data on our elements and exchange the data of ghost elements.\n"); - t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [mesh_step5] \n"); + t8_global_productionf (" [mesh_step5] Hello, this is the element data example of t8code using the mesh handle.\n"); + t8_global_productionf (" [mesh_step5] In this example we will store data on our elements and exchange the " + "data of ghost elements.\n"); + t8_global_productionf (" [mesh_step5] \n"); /* Setup: Build cmesh and adapt uniformly. */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating an adapted mesh.\n"); - t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [mesh_step5] \n"); + t8_global_productionf (" [mesh_step5] Creating an adapted mesh.\n"); + t8_global_productionf (" [mesh_step5] \n"); { /* We put the mesh in its own scope so that it is automatically destroyed at the end of the scope. * This is only necessary because sc_finalize checks if there are leftover references. * This unique pointer would have been destroyed automatically at the end of the programme. */ @@ -209,30 +182,30 @@ main (int argc, char **argv) auto mesh = build_mesh (comm, level); t8_mesh_handle::write_mesh_to_vtk (*mesh, prefix_mesh); - t8_global_productionf (" [tutorial] Wrote mesh to vtu files: %s*\n", prefix_mesh); + t8_global_productionf (" [mesh_step5] Wrote mesh to vtu files: %s*\n", prefix_mesh); set_element_data_mesh (*mesh); - t8_global_productionf (" [tutorial] Computed level and volume data for local elements.\n"); + t8_global_productionf (" [mesh_step5] Computed level and volume data for local elements.\n"); if (mesh->get_num_local_elements () > 0) { /* Output the stored data of the first local element (if it exists). */ - t8_global_productionf (" [tutorial] Element 0 has level %i and volume %e.\n", + t8_global_productionf (" [mesh_step5] Element 0 has level %i and volume %e.\n", ((*mesh)[0]).get_element_data ().level, ((*mesh)[0]).get_element_data ().volume); } /* Exchange the data values of the ghost elements. */ exchange_ghost_data_mesh (*mesh); - t8_global_productionf (" [tutorial] Exchanged ghost data.\n"); + t8_global_productionf (" [mesh_step5] Exchanged ghost data.\n"); if (mesh->get_num_ghosts () > 0) { /* Output the data of the first ghost element (if it exists). */ t8_locidx_t first_ghost_index = mesh->get_num_local_elements (); - t8_global_productionf (" [tutorial] Ghost 0 has level %i and volume %e.\n", + t8_global_productionf (" [mesh_step5] Ghost 0 has level %i and volume %e.\n", ((*mesh)[first_ghost_index]).get_element_data ().level, ((*mesh)[first_ghost_index]).get_element_data ().volume); } /* Output the volume data to vtu. */ output_data_to_vtu (*mesh, prefix_mesh_with_data); - t8_global_productionf (" [tutorial] Wrote mesh and volume data to %s*.\n", prefix_mesh_with_data); + t8_global_productionf (" [mesh_step5] Wrote mesh and volume data to %s*.\n", prefix_mesh_with_data); /* Cleanup. */ } // End scope of mesh diff --git a/tutorials/mesh_handle/t8_mesh_tutorials_common.hxx b/tutorials/mesh_handle/t8_mesh_tutorials_common.hxx new file mode 100644 index 0000000000..19c910e2c3 --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_tutorials_common.hxx @@ -0,0 +1,80 @@ +/* + This file is part of t8code. + t8code is a C library to manage a collection (a forest) of multiple + connected adaptive space-trees of general element types in parallel. + + Copyright (C) 2026 the developers + + t8code is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + t8code is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with t8code; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/** \file t8_mesh_tutorials_common.hxx + * This is the default adaptation callback function that can be used to refine and coarsen a mesh in a spherical shape around a given point. + * It is used in step 3, 4 and 5 in the mesh_handle tutorials. + */ + +#pragma once + +#include /** General t8code header. Always include this. */ + +#include /** Mesh concepts header. */ +#include /** t8 vector dataclass. */ +#include + +/** The data that determines the adaptation characteristics of our algorithm. + * In this example we want to adapt in a spherical shape around a given point. */ +struct adapt_data +{ + t8_3D_vec midpoint; /**< Midpoint of our sphere. */ + double refine_radius; /**< We refine inside this radius of our sphere.*/ + double coarsen_radius; /**< We coarsen outside this radius of our sphere. */ +}; + +/** + * Exemplary adaption callback function for the mesh handle. + * + * This will refine elements inside of a given sphere and coarsen elements + * outside of a given sphere. + * + * \tparam TMeshClass The mesh handle class. + * \param[in] mesh The mesh that should be adapted. + * \param[in] elements One element or a family of elements to consider. + * \param[in] adapt_data The user data used during adaptation. + * + * \return + * 1 if the first element should be refined, + * -1 if the family of elements should be coarsened, + * 0 otherwise. + */ +template +int +adapt_callback_sphere ([[maybe_unused]] const TMeshClass& mesh, + std::span elements, const adapt_data& adapt_data) +{ + auto element_centroid = elements[0].get_centroid (); + + double dist = t8_dist (element_centroid, adapt_data.midpoint); + /** When this if statement returns true, we are inside the set radius of our "refinement sphere" of our point and therefore need to refine. */ + if (dist < adapt_data.refine_radius) { + return 1; /**< Refine. */ + } + + /** Only coarsen if we actually have a complete family. */ + if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { + return -1; /**< Coarsen. */ + } + + return 0; /**< Do nothing. */ +}