From 05c741be09d1575b637ca88f580986871b291071 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Fri, 19 Jun 2026 09:37:28 +0200 Subject: [PATCH 01/11] First Changes and File Creation --- tutorials/CMakeLists.txt | 5 + .../t8_mesh_step3_adapt_forest.cxx | 95 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 4c21e232db..32dd1976c9 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -84,3 +84,8 @@ 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 ) endif() + +if( T8CODE_BUILD_MESH_HANDLE ) + add_mesh_handle_tutorial( NAME t8_mesh_step3_adapt_forest SOURCES mesh_handle/t8_mesh_step3_adapt_forest.cxx ) +endif() + diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx new file mode 100644 index 0000000000..0fba3470c6 --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx @@ -0,0 +1,95 @@ +/* + 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) 2015 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_forest.cxx + * This is the same as general/t8_step3_adapt_forest.cxx but using the mesh handle interface instead of the forest + * interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct adapt_data { + std::array midpoint; + double refine_radius; + double coarsen_radius; +}; + +template +int adapt_callback(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); + if (dist < adapt_data.refine_radius) { + return 1; // refine + } else if ((elements.size() > 1) && (dist > adapt_data.coarsen_radius)) { + return -1; // coarsen + } + return 0; // do nothing +} + +template +std::unique_ptr build_mesh(sc_MPI_Comm comm, int level) { + auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default(level, comm); + struct adapt_data adapt_params = { + {0.5, 0.5, 1.0}, + 0.2, + 0.4 + }; + mesh->set_balance(); + mesh->set_partition(); + mesh->set_adapt( + TMeshClass::template mesh_adapt_callback_wrapper( + adapt_callback, + adapt_params + ) + ); + mesh->set_ghost(); + return mesh; +} + +int main(int argc, char** argv) { + int mpiret = sc_MPI_Init(&argc, &argv); + SC_CHECK_MPI(mpiret); + + sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); + + t8_init (SC_LP_PRODUCTION); + + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + + using mesh_type = t8_mesh_handle::mesh<>; + + int uniform_level = 2; + auto mesh = build_mesh(comm, uniform_level); + + t8_mesh_handle::write_mesh_to_vtk(mesh, "adapted_mesh.vtu"); + + sc_finalize(); + return 0; +} From 53199cbc23ea577eb2db723f38d6eca40c1305a6 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Fri, 19 Jun 2026 09:44:26 +0200 Subject: [PATCH 02/11] Spellchecking correct --- tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx index 0fba3470c6..fd8552980a 100644 --- a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx +++ b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx @@ -66,7 +66,7 @@ std::unique_ptr build_mesh(sc_MPI_Comm comm, int level) { mesh->set_adapt( TMeshClass::template mesh_adapt_callback_wrapper( adapt_callback, - adapt_params +adapt_params ) ); mesh->set_ghost(); From 665d24cd0936267c2879ef9c306f078f816e44fd Mon Sep 17 00:00:00 2001 From: Schmitt Date: Mon, 22 Jun 2026 10:18:12 +0200 Subject: [PATCH 03/11] Mesh_handle_tutorial Step3 --- tutorials/CMakeLists.txt | 4 + .../t8_mesh_step3_adapt_forest.cxx | 141 +++++++++++------- 2 files changed, 95 insertions(+), 50 deletions(-) diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 32dd1976c9..daa0bc8146 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -89,3 +89,7 @@ if( T8CODE_BUILD_MESH_HANDLE ) add_mesh_handle_tutorial( NAME t8_mesh_step3_adapt_forest SOURCES mesh_handle/t8_mesh_step3_adapt_forest.cxx ) endif() +if( T8CODE_BUILD_MESH_HANDLE ) + add_mesh_handle_tutorial( NAME t8_mesh_step4_partition_balance_ghost SOURCES mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx ) +endif() + diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx index fd8552980a..6dcf176481 100644 --- a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx +++ b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx @@ -31,65 +31,106 @@ #include #include #include -#include +#include #include #include -struct adapt_data { - std::array midpoint; - double refine_radius; - double coarsen_radius; +/* 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 +{ + std::array 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. */ }; +/** The adaption 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] adapt_data The user data to be used during the adaptation process. + * \returns 1 if the first entry in \a elements should be refined, + * -1 if the family of elements should be coarsened, + * 0 else. +*/ template -int adapt_callback(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); - if (dist < adapt_data.refine_radius) { - return 1; // refine - } else if ((elements.size() > 1) && (dist > adapt_data.coarsen_radius)) { - return -1; // coarsen - } - return 0; // do nothing +int +adapt_callback ([[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); + if (dist < adapt_data.refine_radius) { + return 1; // refine + } //first check if there is a family, and only if yes check if we should coarsen. + else if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { + return -1; // coarsen + } + return 0; // do nothing } +/** Build our adapted mesh by transfering the adaption parameters and adapting once with our 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) { - auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default(level, comm); - struct adapt_data adapt_params = { - {0.5, 0.5, 1.0}, - 0.2, - 0.4 - }; - mesh->set_balance(); - mesh->set_partition(); - mesh->set_adapt( - TMeshClass::template mesh_adapt_callback_wrapper( - adapt_callback, -adapt_params - ) - ); - mesh->set_ghost(); - return mesh; +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. */ + struct adapt_data adapt_params = { { 0.5, 0.5, 1.0 }, 0.2, 0.4 }; + mesh->set_balance (); + mesh->set_partition (); + /*Adapting once with our adapt_callback function. */ + mesh->set_adapt ( + TMeshClass::template mesh_adapt_callback_wrapper (adapt_callback, adapt_params)); + mesh->set_ghost (); + mesh->commit (); + return mesh; } -int main(int argc, char** argv) { - int mpiret = sc_MPI_Init(&argc, &argv); - SC_CHECK_MPI(mpiret); - - sc_init (sc_MPI_COMM_WORLD, 1, 1, NULL, SC_LP_ESSENTIAL); - - t8_init (SC_LP_PRODUCTION); - - sc_MPI_Comm comm = sc_MPI_COMM_WORLD; - - using mesh_type = t8_mesh_handle::mesh<>; - - int uniform_level = 2; - auto mesh = build_mesh(comm, uniform_level); - - t8_mesh_handle::write_mesh_to_vtk(mesh, "adapted_mesh.vtu"); - - sc_finalize(); - return 0; +/** 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 (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); + t8_global_productionf ( + " [tutorial] In this example 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 (" [tutorial] \n"); + + using mesh_type = t8_mesh_handle::mesh<>; + + t8_global productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating an adapted mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + /* The initial uniform refinement level. */ + int uniform_level = 3; + /* Building the Mesh*/ + auto mesh = build_mesh (comm, uniform_level); + /* Write the mesh to a vtu file. */ + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Writing adapted mesh to vtu file: adapted_mesh.vtu\n"); + t8_global_productionf (" [tutorial] \n"); + t8_mesh_handle::write_mesh_to_vtk (*mesh, "adapted_mesh.vtu"); + + sc_finalize (); + return 0; } From 9523fc53313dcd23257a017c0c6bba59f0032be3 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Mon, 22 Jun 2026 10:18:51 +0200 Subject: [PATCH 04/11] Mesh_handle_tutorial Step3 --- tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx index 6dcf176481..9876a8c06e 100644 --- a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx +++ b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx @@ -56,21 +56,21 @@ struct adapt_data */ template int -adapt_callback ([[maybe_unused]]const TMeshClass &mesh, std::span elements, +adapt_callback ([[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); if (dist < adapt_data.refine_radius) { return 1; // refine - } //first check if there is a family, and only if yes check if we should coarsen. + } //first check if there is a family, and only if yes check if we should coarsen. else if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { return -1; // coarsen } return 0; // do nothing } -/** Build our adapted mesh by transfering the adaption parameters and adapting once with our adapt_callback function. +/** Build our adapted mesh by transferring the adaption parameters and adapting once with our adapt_callback function. * \tparam TMeshClass The mesh handle class. * \param sc_MPI_Comm The MPI Communicator. * \param level The initial uniform refinement level. @@ -83,7 +83,7 @@ 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. */ - struct adapt_data adapt_params = { { 0.5, 0.5, 1.0 }, 0.2, 0.4 }; + struct adapt_data adapt_params = { { 0.5, 0.5, 1.0 }, 0.2, 0.4 }; mesh->set_balance (); mesh->set_partition (); /*Adapting once with our adapt_callback function. */ @@ -112,8 +112,8 @@ main (int argc, char **argv) /* Print a starting message. */ t8_global_productionf (" [tutorial] \n"); t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); - t8_global_productionf ( - " [tutorial] In this example 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 (" [tutorial] In this example 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 (" [tutorial] \n"); using mesh_type = t8_mesh_handle::mesh<>; From 604e003fcc6134389b08c3c0c6081c84b489798a Mon Sep 17 00:00:00 2001 From: Schmitt Date: Mon, 22 Jun 2026 11:32:35 +0200 Subject: [PATCH 05/11] Mesh_handle_tutorial Step3 fixed and Step4 added. --- .../t8_mesh_step3_adapt_forest.cxx | 4 +- .../t8_mesh_step4_partition_balance_ghost.cxx | 228 ++++++++++++++++++ 2 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx index 9876a8c06e..8e1bf1bffe 100644 --- a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx +++ b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx @@ -118,7 +118,7 @@ main (int argc, char **argv) using mesh_type = t8_mesh_handle::mesh<>; - t8_global productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] \n"); t8_global_productionf (" [tutorial] Creating an adapted mesh.\n"); t8_global_productionf (" [tutorial] \n"); /* The initial uniform refinement level. */ @@ -132,5 +132,7 @@ main (int argc, char **argv) t8_mesh_handle::write_mesh_to_vtk (*mesh, "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..db5582c418 --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx @@ -0,0 +1,228 @@ +/* + 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 the same as general/t8_step4_partition_balance_ghost.cxx but using the mesh handle interface instead of the forest + * interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** (This is the same as in tutorial step 3) + * 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 +{ + std::array 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. */ +}; + +/** (This is the same as in tutorial step 3) + * The adaption 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] adapt_data The user data to be used during the adaptation process. + * \returns 1 if the first entry in \a elements should be refined, + * -1 if the family of elements should be coarsened, + * 0 else. +*/ +template +int +adapt_callback ([[maybe_unused]]const mesh_type &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); + if (dist < adapt_data.refine_radius) { + return 1; // refine + } //first check if there is a family, and only if yes check if we should coarsen. + else if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { + return -1; // coarsen + } + return 0; // do nothing +} + +/** 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); + + int rank = 0; + MPI_Comm_rank(comm, &rank); + if (rank == 0) { + std::cout << "=== " << stage << " ===" << std::endl; + std::cout << "Total elements: " << global_elements << std::endl; + } +} + +/** 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 (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); + t8_global_productionf ( + " [tutorial] In this example we will Us\n"); + t8_global_productionf (" [tutorial] \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 }; + + using mesh_type = t8_mesh_handle::mesh<>; + + /** + * INITIAL MESH + */ + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating initial mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating the initial mesh with uniform refinement. */ + auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (uniform_level, comm); + + /* Commiting the initial mesh. */ + mesh->commit(); + + /* 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 (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating adapted mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating the adapted mesh as a copy of the initial mesh (Initial mesh can't be refined because it is already commited.) */ + auto mesh_adapt = std::make_unique(*mesh); + + /* Adapting the mesh once with our adapt_callback function from step 3 and the parameters defined above. */ + mesh_adapt->set_adapt( + mesh_type::template mesh_adapt_callback_wrapper ( + &adapt_callback, + adapt_params) + ); + /* Commiting the adapted mesh. */ + mesh_adapt->commit(); + + /* Printing the mesh information. */ + print_mesh_stats(mesh_adapt, "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_adapt, "adapted_mesh.vtu", 0, nullptr, true, true, true, true, true, false, false); + + /** + * PARTITIONED, BALANCED MESH + */ + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating partitioned and balanced mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating the partition and balance mesh as a copy of the adapted mesh. */ + auto mesh_partition_balance = std::make_unique(*mesh_adapt); + + /* Partitioning the mesh.*/ + mesh_partition_balance->set_partition(); + + /* Balancing the mesh. */ + mesh_partition_balance->set_balance(); + + /* Commiting the partitioned and balanced mesh. */ + mesh_partition_balance->commit(); + + /* Printing the mesh information. */ + print_mesh_stats(mesh_partition_balance, "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, "partition_balance_mesh.vtu", 0, nullptr, true, true, true, true, true, false, false); + + /** + * GHOST MESH + */ + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating ghost mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating the ghost mesh as a copy of the partitioned and balanced mesh. */ + auto mesh_ghost = std::make_unique(*mesh_partition_balance); + + /* Creating the ghost layers. */ + mesh_ghost->set_ghost(); + + /* Commiting the ghost mesh. */ + mesh_ghost->commit(); + + /* Printing the mesh information*/ + print_mesh_stats(mesh_ghost, "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, "ghost_mesh.vtu", 0, nullptr, true, true, true, true, true, false, false); + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Finished all steps successfully.\n"); + t8_global_productionf (" [tutorial] \n"); + + + sc_finalize(); + mpiret = sc_MPI_Finalize(); + SC_CHECK_MPI(mpiret); + return 0; +} \ No newline at end of file From 282f592a7b0c8a577ac7d497996ae0fd13748c50 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Wed, 1 Jul 2026 10:44:48 +0200 Subject: [PATCH 06/11] added bsd license --- doc/author_schmitt.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/author_schmitt.txt diff --git a/doc/author_schmitt.txt b/doc/author_schmitt.txt new file mode 100644 index 0000000000..42045f2bc9 --- /dev/null +++ b/doc/author_schmitt.txt @@ -0,0 +1 @@ +I place my contributions to t8code under the FreeBSD license. Vincent Schmitt (vinz@vincent-schmitt.de) \ No newline at end of file From bd844302fba902311cecb5369045ba0efcaa364b Mon Sep 17 00:00:00 2001 From: Schmitt Date: Fri, 3 Jul 2026 10:27:02 +0200 Subject: [PATCH 07/11] spell checking corrected --- .../t8_mesh_step4_partition_balance_ghost.cxx | 239 +++++++++--------- 1 file changed, 121 insertions(+), 118 deletions(-) diff --git a/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx b/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx index db5582c418..586b66bff3 100644 --- a/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx +++ b/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx @@ -60,14 +60,14 @@ struct adapt_data */ template int -adapt_callback ([[maybe_unused]]const mesh_type &mesh, std::span elements, +adapt_callback ([[maybe_unused]] const mesh_type &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); if (dist < adapt_data.refine_radius) { return 1; // refine - } //first check if there is a family, and only if yes check if we should coarsen. + } //first check if there is a family, and only if yes check if we should coarsen. else if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { return -1; // coarsen } @@ -79,150 +79,153 @@ adapt_callback ([[maybe_unused]]const mesh_type &mesh, std::span> &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); - - int rank = 0; - MPI_Comm_rank(comm, &rank); - if (rank == 0) { - std::cout << "=== " << stage << " ===" << std::endl; - std::cout << "Total elements: " << global_elements << std::endl; - } +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); + + int rank = 0; + MPI_Comm_rank (comm, &rank); + if (rank == 0) { + std::cout << "=== " << stage << " ===" << std::endl; + std::cout << "Total elements: " << global_elements << std::endl; + } } /** 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 (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); - t8_global_productionf ( - " [tutorial] In this example we will Us\n"); - t8_global_productionf (" [tutorial] \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 }; - - using mesh_type = t8_mesh_handle::mesh<>; - - /** +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 (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); + t8_global_productionf (" [tutorial] In this example we will Us\n"); + t8_global_productionf (" [tutorial] \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 }; + + using mesh_type = t8_mesh_handle::mesh<>; + + /** * INITIAL MESH */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating initial mesh.\n"); - t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating initial mesh.\n"); + t8_global_productionf (" [tutorial] \n"); - /* Creating the initial mesh with uniform refinement. */ - auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (uniform_level, comm); + /* Creating the initial mesh with uniform refinement. */ + auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (uniform_level, comm); - /* Commiting the initial mesh. */ - mesh->commit(); + /* Committing the initial mesh. */ + mesh->commit (); - /* 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); + /* 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 (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating adapted mesh.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating the adapted mesh as a copy of the initial mesh (Initial mesh can't be refined because it is already commited.) */ - auto mesh_adapt = std::make_unique(*mesh); - - /* Adapting the mesh once with our adapt_callback function from step 3 and the parameters defined above. */ - mesh_adapt->set_adapt( - mesh_type::template mesh_adapt_callback_wrapper ( - &adapt_callback, - adapt_params) - ); - /* Commiting the adapted mesh. */ - mesh_adapt->commit(); - - /* Printing the mesh information. */ - print_mesh_stats(mesh_adapt, "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_adapt, "adapted_mesh.vtu", 0, nullptr, true, true, true, true, true, false, false); - - /** + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating adapted mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating the adapted mesh as a copy of the initial mesh (Initial mesh can't be refined because it is already committed.) */ + auto mesh_adapt = std::make_unique (*mesh); + + /* Adapting the mesh once with our adapt_callback function from step 3 and the parameters defined above. */ + mesh_adapt->set_adapt ( + mesh_type::template mesh_adapt_callback_wrapper (&adapt_callback, adapt_params)); + /* Committing the adapted mesh. */ + mesh_adapt->commit (); + + /* Printing the mesh information. */ + print_mesh_stats (mesh_adapt, "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_adapt, "adapted_mesh.vtu", 0, nullptr, true, true, true, true, true, + false, false); + + /** * PARTITIONED, BALANCED MESH */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating partitioned and balanced mesh.\n"); - t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating partitioned and balanced mesh.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating the partition and balance mesh as a copy of the adapted mesh. */ + auto mesh_partition_balance = std::make_unique (*mesh_adapt); - /* Creating the partition and balance mesh as a copy of the adapted mesh. */ - auto mesh_partition_balance = std::make_unique(*mesh_adapt); + /* Partitioning the mesh.*/ + mesh_partition_balance->set_partition (); - /* Partitioning the mesh.*/ - mesh_partition_balance->set_partition(); + /* Balancing the mesh. */ + mesh_partition_balance->set_balance (); - /* Balancing the mesh. */ - mesh_partition_balance->set_balance(); + /* Committing the partitioned and balanced mesh. */ + mesh_partition_balance->commit (); - /* Commiting the partitioned and balanced mesh. */ - mesh_partition_balance->commit(); + /* Printing the mesh information. */ + print_mesh_stats (mesh_partition_balance, "Partitioned and Balanced mesh", comm); - /* Printing the mesh information. */ - print_mesh_stats(mesh_partition_balance, "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, "partition_balance_mesh.vtu", 0, nullptr, true, true, true, true, true, false, false); + /* 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, "partition_balance_mesh.vtu", 0, nullptr, true, true, + true, true, true, false, false); - /** + /** * GHOST MESH */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating ghost mesh.\n"); - t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating ghost mesh.\n"); + t8_global_productionf (" [tutorial] \n"); - /* Creating the ghost mesh as a copy of the partitioned and balanced mesh. */ - auto mesh_ghost = std::make_unique(*mesh_partition_balance); + /* Creating the ghost mesh as a copy of the partitioned and balanced mesh. */ + auto mesh_ghost = std::make_unique (*mesh_partition_balance); - /* Creating the ghost layers. */ - mesh_ghost->set_ghost(); - - /* Commiting the ghost mesh. */ - mesh_ghost->commit(); + /* Creating the ghost layers. */ + mesh_ghost->set_ghost (); - /* Printing the mesh information*/ - print_mesh_stats(mesh_ghost, "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, "ghost_mesh.vtu", 0, nullptr, true, true, true, true, true, false, false); + /* Committing the ghost mesh. */ + mesh_ghost->commit (); - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Finished all steps successfully.\n"); - t8_global_productionf (" [tutorial] \n"); + /* Printing the mesh information*/ + print_mesh_stats (mesh_ghost, "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, "ghost_mesh.vtu", 0, nullptr, true, true, true, true, true, false, + false); - sc_finalize(); - mpiret = sc_MPI_Finalize(); - SC_CHECK_MPI(mpiret); - return 0; -} \ No newline at end of file + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Finished all steps successfully.\n"); + t8_global_productionf (" [tutorial] \n"); + + sc_finalize (); + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + return 0; +} From 4042c007ebaccee71e267344fca8d40d3e78af42 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Fri, 3 Jul 2026 14:53:40 +0200 Subject: [PATCH 08/11] Added get_reference_coordinates, element_is_equal, forest_leaf_face_orientation --- mesh_handle/element.hxx | 39 +++++++++++++++++++ src/t8_forest/t8_forest.cxx | 8 ++++ .../t8_gtest_compare_handle_to_forest.cxx | 10 +++++ 3 files changed, 57 insertions(+) diff --git a/mesh_handle/element.hxx b/mesh_handle/element.hxx index 3b1854b452..9560909d89 100644 --- a/mesh_handle/element.hxx +++ b/mesh_handle/element.hxx @@ -583,6 +583,45 @@ class element: public TCompetences>... { return m_is_ghost_element; } + // get_reference_coordinates + /** Function to convert points in reference space of an element to points of the + * reference space of the tree. + * \param [in] ref_coords Pointer to the reference coordinates of the element. + * \param [in] num_coords Number of reference coordinates to convert. + * \param [out] tree_ref_coords Pointer to the reference coordinates of the tree. + */ + void + get_reference_coordinates (const double* ref_coords, std::size_t num_coords, double* tree_ref_coords) const + { + t8_forest_get_scheme (m_mesh->m_forest) + ->element_get_reference_coords (get_tree_class (), m_element, ref_coords, num_coords, tree_ref_coords); + } + + // element_is_equal + /** Check if two elements are equal. + * \param [in] elem1 The first element. + * \param [in] elem2 The second element. + * \return true if the elements are equal, false if they are not equal. + */ + bool + is_equal (const SelfType& elem1, const SelfType& elem2) const + { + return t8_forest_get_scheme (m_mesh->m_forest) + ->element_is_equal (get_tree_class (), elem1.get_forest_element (), elem2.get_forest_element ()); + } + + //t8_forest_leaf_face_orientation + /** Compute the orientation of a face of an element with respect to its neighbor. + * \param [in] face The index of the face for which the orientation should be computed. + * \return The orientation of the face with respect to its neighbor. Returns 0 if the face has no neighbor. + */ + int + get_face_orientation (int face) const + { + return t8_forest_leaf_face_orientation (m_mesh->m_forest, m_tree_id, t8_forest_get_scheme (m_mesh->m_forest), + m_element, face); + } + private: // --- Private member variables. --- TMeshClass* m_mesh; /**< Pointer to the mesh the element is defined for. */ diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 7d604c555d..2da2d7acda 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1578,6 +1578,14 @@ t8_forest_element_half_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, return neighbor_tree; } +/** Compute the orientation of a leaf face with respect to its neighbor tree. + * \param forest The forest to which the leaf belongs. + * \param ltreeid The local tree id of the leaf. + * \param scheme The scheme of the forest. + * \param leaf The leaf element. + * \param face The face of the leaf element. + * \return The orientation of the leaf face with respect to its neighbor tree. Returns 0 if the leaf face is not a boundary face. +*/ int t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, const t8_scheme *scheme, const t8_element_t *leaf, int face) diff --git a/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx b/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx index d39351ac21..f0ea0956fb 100644 --- a/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx +++ b/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx @@ -61,6 +61,14 @@ TEST (t8_gtest_compare_handle_to_forest, compare_handle_to_forest) // --- Compare elements. --- EXPECT_EQ (mesh_iterator->get_local_tree_id (), itree); EXPECT_EQ (mesh_iterator->get_local_element_id (), ielem); + EXPECT_EQ (mesh_iterator->is_equal (*mesh_iterator, *mesh_iterator), + scheme->element_is_equal (tree_class, elem, elem)); + + t8_3D_vec ref = { 0.2, 0.3, 1 }; + t8_3D_vec a, b; + mesh_iterator->get_reference_coordinates (ref.data (), 1, a.data ()); + scheme->element_get_reference_coords (tree_class, elem, ref.data (), 1, b.data ()); + EXPECT_EQ (a, b); // --- Compare functionality. --- EXPECT_EQ (mesh_iterator->get_level (), scheme->element_get_level (tree_class, elem)); EXPECT_EQ (mesh_iterator->get_num_faces (), scheme->element_get_num_faces (tree_class, elem)); @@ -94,6 +102,8 @@ TEST (t8_gtest_compare_handle_to_forest, compare_handle_to_forest) EXPECT_EQ (mesh_iterator->face_vertex_to_element_vertex (iface, ivertex), scheme->element_get_face_corner (tree_class, elem, iface, ivertex)); } + EXPECT_EQ (mesh_iterator->get_face_orientation (iface), + t8_forest_leaf_face_orientation (forest, itree, scheme, elem, iface)); } // --- Evolve mesh iterator. --- mesh_iterator++; From e796ce36977a5a0355b54d03f70f751f6ca2259e Mon Sep 17 00:00:00 2001 From: Schmitt Date: Mon, 20 Jul 2026 12:38:08 +0200 Subject: [PATCH 09/11] Added a tutorial to demonstrate the core competences of the mesh handle. --- tutorials/CMakeLists.txt | 4 + tutorials/mesh_handle/t8_mesh_competences.cxx | 227 ++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 tutorials/mesh_handle/t8_mesh_competences.cxx diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index daa0bc8146..63531fb794 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -93,3 +93,7 @@ if( T8CODE_BUILD_MESH_HANDLE ) add_mesh_handle_tutorial( NAME t8_mesh_step4_partition_balance_ghost SOURCES mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx ) endif() +if( T8CODE_BUILD_MESH_HANDLE ) + add_mesh_handle_tutorial( NAME t8_mesh_competences SOURCES mesh_handle/t8_mesh_competences.cxx ) +endif() + diff --git a/tutorials/mesh_handle/t8_mesh_competences.cxx b/tutorials/mesh_handle/t8_mesh_competences.cxx new file mode 100644 index 0000000000..2640814189 --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_competences.cxx @@ -0,0 +1,227 @@ +/* + 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) 2015 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_forest.cxx + * This tutorial will explain the the use of competences native to the mesh_handle while explaining the already given competences but also going deeper in how to + * create custom competences. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace t8_mesh_handle; + +/** + * Creating a simple custom competence that computes the squared volume of an element. +*/ +template +struct volume_squared: public t8_crtp_operator +{ + public: + /** + * Getter function for the squared volume of an element. + */ + double + get_squared_volume () const + { + double volume = this->underlying ().get_volume (); + return volume * volume; + } +}; + +/** + * Demonstrates the use of the standard element data competences by computing the total volume of a mesh. +*/ +template +void +demonstrate_element_data (MeshType& mesh) +{ + /** The most used geometric standard competences. */ + for (auto& elem : mesh) { + auto centroid = elem.get_centroid (); /**< Get the Centroid of the element. */ + double volume = elem.get_volume (); /**< Get the Volume of the element. */ + + (void) centroid; + + elem.set_element_data (volume); /**< Save the Volume in the data of the element. */ + } + + double total_volume = 0.0; + + /** Read the element data of each element in the mesh. */ + for (const auto& elem : mesh) { + total_volume += elem.get_element_data (); /**< Sum up all volumes.*/ + } + + std::cout << "Total volume of the mesh: " << total_volume << '\n'; +} + +/** + * Demonstrates the use of the cache competences by comparing the freshly computed values to the one saved in the cache. +*/ +template +void +demonstrate_cache_competences (const ElementType& elem) +{ + + std::cout << "Vertex cache initially filled: " << elem.vertex_cache_filled () << '\n'; + + auto vertices1 = elem.get_vertex_coordinates (); /**< Compute the Vertex Coordinates for the first time. */ + + std::cout << "Vertex cache after first call: " << elem.vertex_cache_filled () << '\n'; + + auto vertices2 = elem.get_vertex_coordinates (); /**< Compute the Vertex Coordinates for the second time. */ + + std::cout << "Vertex coordinates (first call):\n"; + for (const auto& v : vertices1) { + std::cout << "(" << v[0] << ", " << v[1] << ", " << v[2] << ")\n"; + } + + std::cout << "\nVertex coordinates (second call):\n"; + for (const auto& v : vertices2) { + std::cout << "(" << v[0] << ", " << v[1] << ", " << v[2] << ")\n"; + } +} + +/** + * Demonstrates the use of the custom competence 'volume_squared' that was defined at the top so that we can compute the squared volume of each element in the mesh. +*/ +template +void +demonstrate_custom_competence (const MeshType& mesh) +{ + std::size_t count = 0; + + for (const auto& elem : mesh) { + if (count++ >= 30) { /**< Counter to only show the first 30 entries so the terminal doesn't get cluttered. */ + break; + } + + std::cout << "Volume: " << elem.get_volume () /**< Compute default Volume of the element*/ + << " Squared volume: " + << elem.get_squared_volume () /**< Computing the squared Volume using the custom competence. */ + << '\n'; + } +} + +int +main (int argc, char** argv) +{ + /* The initial refinement level of our meshes. */ + const int level = 2; + /* 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 on the root process. */ + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Hello, this is the competence example of t8code using the mesh handle.\n"); + t8_global_productionf (" [tutorial] In this example we will go through the most important competences and caching," + "as well as create custom competences.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Initializing all the competence packs with the functions/competences we want to use. */ + using cache_competences = element_competence_pack; /**< Vertex Coordinates Cache. */ + + using data_competences = element_competence_pack< + element_data_element_competence>; /**< Element data Competence to store element data on an element. */ + + /** Combine both competence packs into one with union_competence_packs_type. */ + using element_competences = union_competence_packs_type; + + using mesh_competences = mesh_competence_pack::template type>; /**< Element data Competence to store element data on an element. */ + + /* Defining our mesh type with the competence packs defined above. */ + using mesh_type = mesh; + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Creating a default mesh with refinement level 2.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating a simple mesh of Hexahedrons and an initial refinement level of 2. */ + auto default_mesh = handle_hypercube_hybrid_uniform_default (level, comm, false, false); + + default_mesh->commit (); /**< Committing the mesh so we can work on it. */ + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf ( + " [tutorial] Demonstrating standard element data competences by computing the total volume.\n"); + t8_global_productionf (" [tutorial] \n"); + + demonstrate_element_data (*default_mesh); /**< Calling the element data competence function defined above. */ + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Demonstrating the cache competences by comparing the freshly computed values to " + "the one saved in the cache.\n"); + t8_global_productionf (" [tutorial] \n"); + + demonstrate_cache_competences (*default_mesh->cbegin ()); /**< Calling the cache competence function defined above. */ + + /* Defining a competence pack with the volume cache competence and our custom defined competence. */ + using custom_element_competences = element_competence_pack; /**< Our custom competence. */ + + /* Defining a custom mesh_type with our competence pack. */ + using custom_mesh = mesh; + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf ( + " [tutorial] Creating a custom mesh for the custom competence with initial refinement level of 2.\n"); + t8_global_productionf (" [tutorial] \n"); + + /* Creating a custom mesh with the mesh_type including our custom competence pack and the initial refinement level 2. */ + auto custom = handle_hypercube_hybrid_uniform_default (level, comm, false, false); + + custom->commit (); /**< Committing the custom mesh. */ + + t8_global_productionf (" [tutorial] \n"); + t8_global_productionf (" [tutorial] Demonstrating the custom competence 'Squared Value'.\n"); + t8_global_productionf (" [tutorial] \n"); + + demonstrate_custom_competence (*custom); /**< Calling the custom competence function defined above. */ + + /* Finalizing. */ + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +} From 6ac480e6c5e1dc59db4a179d37fdc2bbf9cca5c2 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Mon, 20 Jul 2026 13:02:09 +0200 Subject: [PATCH 10/11] deleted unnecessary files. --- mesh_handle/element.hxx | 39 --- src/t8_forest/t8_forest.cxx | 20 +- .../t8_gtest_compare_handle_to_forest.cxx | 14 +- .../t8_mesh_step3_adapt_forest.cxx | 138 ----------- .../t8_mesh_step4_partition_balance_ghost.cxx | 231 ------------------ 5 files changed, 9 insertions(+), 433 deletions(-) delete mode 100644 tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx delete mode 100644 tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx diff --git a/mesh_handle/element.hxx b/mesh_handle/element.hxx index 9560909d89..3b1854b452 100644 --- a/mesh_handle/element.hxx +++ b/mesh_handle/element.hxx @@ -583,45 +583,6 @@ class element: public TCompetences>... { return m_is_ghost_element; } - // get_reference_coordinates - /** Function to convert points in reference space of an element to points of the - * reference space of the tree. - * \param [in] ref_coords Pointer to the reference coordinates of the element. - * \param [in] num_coords Number of reference coordinates to convert. - * \param [out] tree_ref_coords Pointer to the reference coordinates of the tree. - */ - void - get_reference_coordinates (const double* ref_coords, std::size_t num_coords, double* tree_ref_coords) const - { - t8_forest_get_scheme (m_mesh->m_forest) - ->element_get_reference_coords (get_tree_class (), m_element, ref_coords, num_coords, tree_ref_coords); - } - - // element_is_equal - /** Check if two elements are equal. - * \param [in] elem1 The first element. - * \param [in] elem2 The second element. - * \return true if the elements are equal, false if they are not equal. - */ - bool - is_equal (const SelfType& elem1, const SelfType& elem2) const - { - return t8_forest_get_scheme (m_mesh->m_forest) - ->element_is_equal (get_tree_class (), elem1.get_forest_element (), elem2.get_forest_element ()); - } - - //t8_forest_leaf_face_orientation - /** Compute the orientation of a face of an element with respect to its neighbor. - * \param [in] face The index of the face for which the orientation should be computed. - * \return The orientation of the face with respect to its neighbor. Returns 0 if the face has no neighbor. - */ - int - get_face_orientation (int face) const - { - return t8_forest_leaf_face_orientation (m_mesh->m_forest, m_tree_id, t8_forest_get_scheme (m_mesh->m_forest), - m_element, face); - } - private: // --- Private member variables. --- TMeshClass* m_mesh; /**< Pointer to the mesh the element is defined for. */ diff --git a/src/t8_forest/t8_forest.cxx b/src/t8_forest/t8_forest.cxx index 2da2d7acda..04ee2da63e 100644 --- a/src/t8_forest/t8_forest.cxx +++ b/src/t8_forest/t8_forest.cxx @@ -1017,13 +1017,13 @@ t8_forest_element_face_normal (t8_forest_t forest, t8_locidx_t ltreeid, const t8 #if T8_ENABLE_DEBUG /* Issue a warning if the points of the quad do not lie in the same plane */ { - t8_3D_vec p_0, p_1, p_2, p_3; + t8_3D_vec points[4]; /* Compute the vertex coordinates of the quad */ - t8_forest_element_coordinate (forest, ltreeid, element, 0, p_0.data ()); - t8_forest_element_coordinate (forest, ltreeid, element, 1, p_1.data ()); - t8_forest_element_coordinate (forest, ltreeid, element, 2, p_2.data ()); - t8_forest_element_coordinate (forest, ltreeid, element, 3, p_3.data ()); - if (!t8_four_points_coplanar (p_0, p_1, p_2, p_3, T8_PRECISION_SQRT_EPS)) { + for (int ipoint = 0; ipoint < 4; ipoint++) { + const int corner = scheme->element_get_face_corner (tree_class, element, face, ipoint); + t8_forest_element_coordinate (forest, ltreeid, element, corner, points[ipoint].data ()); + } + if (!t8_four_points_coplanar (points[0], points[1], points[2], points[3], T8_PRECISION_SQRT_EPS)) { t8_debugf ("WARNING: Computing normal to a quad that is not coplanar. This computation will be inaccurate.\n"); } } @@ -1578,14 +1578,6 @@ t8_forest_element_half_face_neighbors (t8_forest_t forest, t8_locidx_t ltreeid, return neighbor_tree; } -/** Compute the orientation of a leaf face with respect to its neighbor tree. - * \param forest The forest to which the leaf belongs. - * \param ltreeid The local tree id of the leaf. - * \param scheme The scheme of the forest. - * \param leaf The leaf element. - * \param face The face of the leaf element. - * \return The orientation of the leaf face with respect to its neighbor tree. Returns 0 if the leaf face is not a boundary face. -*/ int t8_forest_leaf_face_orientation (t8_forest_t forest, const t8_locidx_t ltreeid, const t8_scheme *scheme, const t8_element_t *leaf, int face) diff --git a/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx b/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx index f0ea0956fb..1243f0bc3d 100644 --- a/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx +++ b/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx @@ -42,7 +42,9 @@ TEST (t8_gtest_compare_handle_to_forest, compare_handle_to_forest) { // Define forest and mesh handle. const int level = 2; - t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); + t8_cmesh_t cmesh; + t8_cmesh_init (&cmesh); + t8_cmesh_new_hypercube_hybrid (cmesh, sc_MPI_COMM_WORLD, 0); const t8_scheme *scheme = t8_scheme_new_default (); t8_forest_t forest = t8_forest_new_uniform (cmesh, scheme, level, 0, sc_MPI_COMM_WORLD); @@ -61,14 +63,6 @@ TEST (t8_gtest_compare_handle_to_forest, compare_handle_to_forest) // --- Compare elements. --- EXPECT_EQ (mesh_iterator->get_local_tree_id (), itree); EXPECT_EQ (mesh_iterator->get_local_element_id (), ielem); - EXPECT_EQ (mesh_iterator->is_equal (*mesh_iterator, *mesh_iterator), - scheme->element_is_equal (tree_class, elem, elem)); - - t8_3D_vec ref = { 0.2, 0.3, 1 }; - t8_3D_vec a, b; - mesh_iterator->get_reference_coordinates (ref.data (), 1, a.data ()); - scheme->element_get_reference_coords (tree_class, elem, ref.data (), 1, b.data ()); - EXPECT_EQ (a, b); // --- Compare functionality. --- EXPECT_EQ (mesh_iterator->get_level (), scheme->element_get_level (tree_class, elem)); EXPECT_EQ (mesh_iterator->get_num_faces (), scheme->element_get_num_faces (tree_class, elem)); @@ -102,8 +96,6 @@ TEST (t8_gtest_compare_handle_to_forest, compare_handle_to_forest) EXPECT_EQ (mesh_iterator->face_vertex_to_element_vertex (iface, ivertex), scheme->element_get_face_corner (tree_class, elem, iface, ivertex)); } - EXPECT_EQ (mesh_iterator->get_face_orientation (iface), - t8_forest_leaf_face_orientation (forest, itree, scheme, elem, iface)); } // --- Evolve mesh iterator. --- mesh_iterator++; diff --git a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx b/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx deleted file mode 100644 index 8e1bf1bffe..0000000000 --- a/tutorials/mesh_handle/t8_mesh_step3_adapt_forest.cxx +++ /dev/null @@ -1,138 +0,0 @@ -/* - 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) 2015 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_forest.cxx - * This is the same as general/t8_step3_adapt_forest.cxx but using the mesh handle interface instead of the forest - * interface. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#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 -{ - std::array 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. */ -}; - -/** The adaption 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] adapt_data The user data to be used during the adaptation process. - * \returns 1 if the first entry in \a elements should be refined, - * -1 if the family of elements should be coarsened, - * 0 else. -*/ -template -int -adapt_callback ([[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); - if (dist < adapt_data.refine_radius) { - return 1; // refine - } //first check if there is a family, and only if yes check if we should coarsen. - else if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { - return -1; // coarsen - } - return 0; // do nothing -} - -/** Build our adapted mesh by transferring the adaption parameters and adapting once with our 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. */ - struct adapt_data adapt_params = { { 0.5, 0.5, 1.0 }, 0.2, 0.4 }; - mesh->set_balance (); - mesh->set_partition (); - /*Adapting once with our adapt_callback function. */ - mesh->set_adapt ( - TMeshClass::template mesh_adapt_callback_wrapper (adapt_callback, adapt_params)); - mesh->set_ghost (); - 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 (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); - t8_global_productionf (" [tutorial] In this example 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 (" [tutorial] \n"); - - using mesh_type = t8_mesh_handle::mesh<>; - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating an adapted mesh.\n"); - t8_global_productionf (" [tutorial] \n"); - /* The initial uniform refinement level. */ - int uniform_level = 3; - /* Building the Mesh*/ - auto mesh = build_mesh (comm, uniform_level); - /* Write the mesh to a vtu file. */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Writing adapted mesh to vtu file: adapted_mesh.vtu\n"); - t8_global_productionf (" [tutorial] \n"); - t8_mesh_handle::write_mesh_to_vtk (*mesh, "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 deleted file mode 100644 index 586b66bff3..0000000000 --- a/tutorials/mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx +++ /dev/null @@ -1,231 +0,0 @@ -/* - 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 the same as general/t8_step4_partition_balance_ghost.cxx but using the mesh handle interface instead of the forest - * interface. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** (This is the same as in tutorial step 3) - * 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 -{ - std::array 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. */ -}; - -/** (This is the same as in tutorial step 3) - * The adaption 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] adapt_data The user data to be used during the adaptation process. - * \returns 1 if the first entry in \a elements should be refined, - * -1 if the family of elements should be coarsened, - * 0 else. -*/ -template -int -adapt_callback ([[maybe_unused]] const mesh_type &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); - if (dist < adapt_data.refine_radius) { - return 1; // refine - } //first check if there is a family, and only if yes check if we should coarsen. - else if ((elements.size () > 1) && (dist > adapt_data.coarsen_radius)) { - return -1; // coarsen - } - return 0; // do nothing -} - -/** 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); - - int rank = 0; - MPI_Comm_rank (comm, &rank); - if (rank == 0) { - std::cout << "=== " << stage << " ===" << std::endl; - std::cout << "Total elements: " << global_elements << std::endl; - } -} - -/** 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 (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Hello, this is the mesh adaptation example of t8code using the mesh handle.\n"); - t8_global_productionf (" [tutorial] In this example we will Us\n"); - t8_global_productionf (" [tutorial] \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 }; - - using mesh_type = t8_mesh_handle::mesh<>; - - /** - * INITIAL MESH - */ - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating initial mesh.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating the initial mesh with uniform refinement. */ - auto mesh = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (uniform_level, comm); - - /* Committing the initial mesh. */ - mesh->commit (); - - /* 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 (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating adapted mesh.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating the adapted mesh as a copy of the initial mesh (Initial mesh can't be refined because it is already committed.) */ - auto mesh_adapt = std::make_unique (*mesh); - - /* Adapting the mesh once with our adapt_callback function from step 3 and the parameters defined above. */ - mesh_adapt->set_adapt ( - mesh_type::template mesh_adapt_callback_wrapper (&adapt_callback, adapt_params)); - /* Committing the adapted mesh. */ - mesh_adapt->commit (); - - /* Printing the mesh information. */ - print_mesh_stats (mesh_adapt, "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_adapt, "adapted_mesh.vtu", 0, nullptr, true, true, true, true, true, - false, false); - - /** - * PARTITIONED, BALANCED MESH - */ - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating partitioned and balanced mesh.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating the partition and balance mesh as a copy of the adapted mesh. */ - auto mesh_partition_balance = std::make_unique (*mesh_adapt); - - /* Partitioning the mesh.*/ - mesh_partition_balance->set_partition (); - - /* Balancing the mesh. */ - mesh_partition_balance->set_balance (); - - /* Committing the partitioned and balanced mesh. */ - mesh_partition_balance->commit (); - - /* Printing the mesh information. */ - print_mesh_stats (mesh_partition_balance, "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, "partition_balance_mesh.vtu", 0, nullptr, true, true, - true, true, true, false, false); - - /** - * GHOST MESH - */ - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating ghost mesh.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating the ghost mesh as a copy of the partitioned and balanced mesh. */ - auto mesh_ghost = std::make_unique (*mesh_partition_balance); - - /* Creating the ghost layers. */ - mesh_ghost->set_ghost (); - - /* Committing the ghost mesh. */ - mesh_ghost->commit (); - - /* Printing the mesh information*/ - print_mesh_stats (mesh_ghost, "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, "ghost_mesh.vtu", 0, nullptr, true, true, true, true, true, false, - false); - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Finished all steps successfully.\n"); - t8_global_productionf (" [tutorial] \n"); - - sc_finalize (); - mpiret = sc_MPI_Finalize (); - SC_CHECK_MPI (mpiret); - return 0; -} From 761d9dfa19023e58a15560a163a80d9426d938b5 Mon Sep 17 00:00:00 2001 From: Schmitt Date: Tue, 4 Aug 2026 11:09:40 +0200 Subject: [PATCH 11/11] Fixed all errors mentioned in Pull Request comments. --- tutorials/CMakeLists.txt | 13 +- tutorials/mesh_handle/t8_mesh_competences.cxx | 227 ---------------- .../mesh_handle/t8_mesh_stepA_competences.cxx | 256 ++++++++++++++++++ 3 files changed, 257 insertions(+), 239 deletions(-) delete mode 100644 tutorials/mesh_handle/t8_mesh_competences.cxx create mode 100644 tutorials/mesh_handle/t8_mesh_stepA_competences.cxx diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 63531fb794..ceed6e8f04 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -83,17 +83,6 @@ 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 ) -endif() - -if( T8CODE_BUILD_MESH_HANDLE ) - add_mesh_handle_tutorial( NAME t8_mesh_step3_adapt_forest SOURCES mesh_handle/t8_mesh_step3_adapt_forest.cxx ) -endif() - -if( T8CODE_BUILD_MESH_HANDLE ) - add_mesh_handle_tutorial( NAME t8_mesh_step4_partition_balance_ghost SOURCES mesh_handle/t8_mesh_step4_partition_balance_ghost.cxx ) -endif() - -if( T8CODE_BUILD_MESH_HANDLE ) - add_mesh_handle_tutorial( NAME t8_mesh_competences SOURCES mesh_handle/t8_mesh_competences.cxx ) + add_mesh_handle_tutorial( NAME t8_mesh_stepA_competences SOURCES mesh_handle/t8_mesh_stepA_competences.cxx ) endif() diff --git a/tutorials/mesh_handle/t8_mesh_competences.cxx b/tutorials/mesh_handle/t8_mesh_competences.cxx deleted file mode 100644 index 2640814189..0000000000 --- a/tutorials/mesh_handle/t8_mesh_competences.cxx +++ /dev/null @@ -1,227 +0,0 @@ -/* - 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) 2015 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_forest.cxx - * This tutorial will explain the the use of competences native to the mesh_handle while explaining the already given competences but also going deeper in how to - * create custom competences. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace t8_mesh_handle; - -/** - * Creating a simple custom competence that computes the squared volume of an element. -*/ -template -struct volume_squared: public t8_crtp_operator -{ - public: - /** - * Getter function for the squared volume of an element. - */ - double - get_squared_volume () const - { - double volume = this->underlying ().get_volume (); - return volume * volume; - } -}; - -/** - * Demonstrates the use of the standard element data competences by computing the total volume of a mesh. -*/ -template -void -demonstrate_element_data (MeshType& mesh) -{ - /** The most used geometric standard competences. */ - for (auto& elem : mesh) { - auto centroid = elem.get_centroid (); /**< Get the Centroid of the element. */ - double volume = elem.get_volume (); /**< Get the Volume of the element. */ - - (void) centroid; - - elem.set_element_data (volume); /**< Save the Volume in the data of the element. */ - } - - double total_volume = 0.0; - - /** Read the element data of each element in the mesh. */ - for (const auto& elem : mesh) { - total_volume += elem.get_element_data (); /**< Sum up all volumes.*/ - } - - std::cout << "Total volume of the mesh: " << total_volume << '\n'; -} - -/** - * Demonstrates the use of the cache competences by comparing the freshly computed values to the one saved in the cache. -*/ -template -void -demonstrate_cache_competences (const ElementType& elem) -{ - - std::cout << "Vertex cache initially filled: " << elem.vertex_cache_filled () << '\n'; - - auto vertices1 = elem.get_vertex_coordinates (); /**< Compute the Vertex Coordinates for the first time. */ - - std::cout << "Vertex cache after first call: " << elem.vertex_cache_filled () << '\n'; - - auto vertices2 = elem.get_vertex_coordinates (); /**< Compute the Vertex Coordinates for the second time. */ - - std::cout << "Vertex coordinates (first call):\n"; - for (const auto& v : vertices1) { - std::cout << "(" << v[0] << ", " << v[1] << ", " << v[2] << ")\n"; - } - - std::cout << "\nVertex coordinates (second call):\n"; - for (const auto& v : vertices2) { - std::cout << "(" << v[0] << ", " << v[1] << ", " << v[2] << ")\n"; - } -} - -/** - * Demonstrates the use of the custom competence 'volume_squared' that was defined at the top so that we can compute the squared volume of each element in the mesh. -*/ -template -void -demonstrate_custom_competence (const MeshType& mesh) -{ - std::size_t count = 0; - - for (const auto& elem : mesh) { - if (count++ >= 30) { /**< Counter to only show the first 30 entries so the terminal doesn't get cluttered. */ - break; - } - - std::cout << "Volume: " << elem.get_volume () /**< Compute default Volume of the element*/ - << " Squared volume: " - << elem.get_squared_volume () /**< Computing the squared Volume using the custom competence. */ - << '\n'; - } -} - -int -main (int argc, char** argv) -{ - /* The initial refinement level of our meshes. */ - const int level = 2; - /* 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 on the root process. */ - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Hello, this is the competence example of t8code using the mesh handle.\n"); - t8_global_productionf (" [tutorial] In this example we will go through the most important competences and caching," - "as well as create custom competences.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Initializing all the competence packs with the functions/competences we want to use. */ - using cache_competences = element_competence_pack; /**< Vertex Coordinates Cache. */ - - using data_competences = element_competence_pack< - element_data_element_competence>; /**< Element data Competence to store element data on an element. */ - - /** Combine both competence packs into one with union_competence_packs_type. */ - using element_competences = union_competence_packs_type; - - using mesh_competences = mesh_competence_pack::template type>; /**< Element data Competence to store element data on an element. */ - - /* Defining our mesh type with the competence packs defined above. */ - using mesh_type = mesh; - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Creating a default mesh with refinement level 2.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating a simple mesh of Hexahedrons and an initial refinement level of 2. */ - auto default_mesh = handle_hypercube_hybrid_uniform_default (level, comm, false, false); - - default_mesh->commit (); /**< Committing the mesh so we can work on it. */ - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf ( - " [tutorial] Demonstrating standard element data competences by computing the total volume.\n"); - t8_global_productionf (" [tutorial] \n"); - - demonstrate_element_data (*default_mesh); /**< Calling the element data competence function defined above. */ - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Demonstrating the cache competences by comparing the freshly computed values to " - "the one saved in the cache.\n"); - t8_global_productionf (" [tutorial] \n"); - - demonstrate_cache_competences (*default_mesh->cbegin ()); /**< Calling the cache competence function defined above. */ - - /* Defining a competence pack with the volume cache competence and our custom defined competence. */ - using custom_element_competences = element_competence_pack; /**< Our custom competence. */ - - /* Defining a custom mesh_type with our competence pack. */ - using custom_mesh = mesh; - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf ( - " [tutorial] Creating a custom mesh for the custom competence with initial refinement level of 2.\n"); - t8_global_productionf (" [tutorial] \n"); - - /* Creating a custom mesh with the mesh_type including our custom competence pack and the initial refinement level 2. */ - auto custom = handle_hypercube_hybrid_uniform_default (level, comm, false, false); - - custom->commit (); /**< Committing the custom mesh. */ - - t8_global_productionf (" [tutorial] \n"); - t8_global_productionf (" [tutorial] Demonstrating the custom competence 'Squared Value'.\n"); - t8_global_productionf (" [tutorial] \n"); - - demonstrate_custom_competence (*custom); /**< Calling the custom competence function defined above. */ - - /* Finalizing. */ - sc_finalize (); - - mpiret = sc_MPI_Finalize (); - SC_CHECK_MPI (mpiret); - - return 0; -} diff --git a/tutorials/mesh_handle/t8_mesh_stepA_competences.cxx b/tutorials/mesh_handle/t8_mesh_stepA_competences.cxx new file mode 100644 index 0000000000..7e5f277272 --- /dev/null +++ b/tutorials/mesh_handle/t8_mesh_stepA_competences.cxx @@ -0,0 +1,256 @@ +/* + 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_stepA_competences.cxx + * This is step A of the t8code mesh handle tutorials. + * After finishing the core t8code features, we will now go into an important feature which is native to the mesh handle. + * These 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 the functionality. + * Element data competences are used to store data in the mesh elements and work with it in different ways + * Cache competences are used to store data in the mesh elements to work with it more efficiently, e.g. to avoid recomputing the same data multiple times. + * The keypoint about competences though is, that you can create your own competence packs with all the competences 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 creating your own competences and adding them to your competence pack, making the mesh handle really flexible and individual for each use case. + * + * In this tutorial, we will go through the most important competences and caching, as well as create custom competences. +*/ + +#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. */ +#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 /** t8code vector dataclass. */ + +using namespace t8_mesh_handle; /** Using the namespace to avoid the t8_mesh_handle:: prefix everywhere and shorten the code. */ + +/** + * Creating a simple custom competence that computes the squared volume of an element. + * + * All custom competences follow the same CRTP inheritance pattern: + * They are templated on the underlying element type and inherit from + * t8_crtp_operator. This gives the competence access to the functionality + * of the underlying element with using this->underlying(), allowing it to extend the element with additional methods. + * The use of t8_crtp_operator also avoids diamond-shaped inheritance when multiple competences are combined into one pack. + * + * \tparam TUnderlying The underlying element type that we want to extend with this competence. +*/ +template +struct volume_squared_custom_competence: public t8_crtp_operator +{ + public: + /** + * Returns the squared volume of the underlying element. + */ + double + get_squared_volume () const + { + double volume = this->underlying ().get_volume (); + return volume * volume; + } +}; + +/** + * Example element data type that stores the volume of an element. +*/ +struct element_data_volume +{ + double volume; /**< Volume of the element. */ +}; + +/** + * Demonstrates the use of the standard element data competences by computing the total volume of a mesh. + * + * \param [in] mesh The mesh to compute the total volume of. + * \param [in] comm The MPI communicator to use for the reduction of the total volume. +*/ +template +void +demonstrate_element_data (MeshType& mesh, sc_MPI_Comm comm) +{ + /** The most used geometric standard competences. */ + for (auto& elem : mesh) { + element_data_volume data { elem.get_volume () }; /**< Get the volume of the element. */ + elem.set_element_data (data); /**< Save the volume in the data of the element. */ + } + + double local_volume = 0.0; + + /** Read the element data of each element in the mesh. */ + for (const auto& elem : mesh) { + local_volume += elem.get_element_data ().volume; /**< Sum up all volumes.*/ + } + + double global_volume = 0.0; + + sc_MPI_Reduce (&local_volume, &global_volume, 1, sc_MPI_DOUBLE, sc_MPI_SUM, 0, + comm); /**< Reduce the local volumes to the root process. */ + + t8_global_productionf (" [t8 Step A Mesh handle] Total volume of the mesh: %f\n", global_volume); +} + +/** + * Demonstrates the use of the cache competences by comparing the freshly computed values to the one saved in the cache. + * + * \param [in] elem The element to demonstrate the cache competences on. +*/ +template +void +demonstrate_cache_competences (const ElementType& elem) +{ + + t8_global_productionf ("Vertex cache initially filled: %d\n", elem.vertex_cache_filled ()); + + auto vertices1 = elem.get_vertex_coordinates (); /**< Compute the Vertex Coordinates for the first time. */ + + t8_global_productionf ("Vertex coordinates (first call):\n"); + for (const auto& v : vertices1) { + t8_global_productionf ("(%f, %f, %f)\n", v[0], v[1], v[2]); + } + + t8_global_productionf ("Vertex cache filled after first call: %d\n", elem.vertex_cache_filled ()); + + auto vertices2 = elem.get_vertex_coordinates (); /**< Compute the Vertex Coordinates for the second time. */ + + if (vertices1 == vertices2) { + t8_global_productionf ("Vertex coordinates are the same for both calls.\n"); + } +} + +/** + * Demonstrates the use of the custom competence 'volume_squared' that was defined at the top so that we can compute the squared volume of each element in the mesh. + * Only the first and last local elements are printed to avoid excessive output when running with multiple MPI processes. + * + * \param [in] mesh The mesh to demonstrate the custom competence on. +*/ +template +void +demonstrate_custom_competence (const MeshType& mesh) +{ + auto first_elem = mesh.cbegin (); /**< Get the first element of this MPI process. */ + auto last_elem = mesh.cend () - 1; /**< Get the last element of this MPI process. */ + + t8_global_productionf ( + "First element: Volume: %f Squared volume: %f\n", + first_elem->get_volume (), /**< Compute default Volume of the element*/ + first_elem->get_squared_volume ()); /**< Computing the squared Volume using the custom competence. */ + + t8_global_productionf ( + "Last element: Volume: %f Squared volume: %f\n", + last_elem->get_volume (), /**< Compute default Volume of the element*/ + last_elem->get_squared_volume ()); /**< Computing the squared Volume using the custom competence. */ +} + +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 on the root process. */ + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + t8_global_productionf ( + " [t8 Step A Mesh handle] Hello, this is the competence tutorial of t8code using the mesh handle.\n"); + t8_global_productionf ( + " [t8 Step A Mesh handle] In this tutorial we will go through the most important competences and caching," + "as well as create custom competences.\n"); + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + { /* Start of mesh scope. */ + /* Initializing all the competence packs with the functions/competences we want to use. */ + + using data_competences + = data_element_competences; /**< Element data Competence to store element data on an element. */ + + /** Combine the data competence pack with the predefined 'all_cache_element_competences' (see competence_pack.hxx) pack into one with union_competence_packs_type. */ + using element_competences = union_competence_packs_type; + + using mesh_competences + = data_mesh_competences; /**< Element data Competence to store element data on an element. */ + + /* Defining our mesh type with the competence packs defined above. */ + using mesh_type = mesh; + + const int level = 2; + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + t8_global_productionf (" [t8 Step A Mesh handle] Creating a default mesh with refinement level %d.\n", level); + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + /* Creating a simple mesh of Hexahedrons and an initial refinement level of 2. Our competences get transferred onto the mesh by the mesh type we defined above. */ + auto default_mesh = handle_hypercube_hybrid_uniform_default (level, comm); + + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + t8_global_productionf ( + " [t8 Step A Mesh handle] Demonstrating standard element data competences by computing the total volume.\n"); + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + + demonstrate_element_data (*default_mesh, comm); /**< Calling the element data competence function defined above. */ + + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + t8_global_productionf ( + " [t8 Step A Mesh handle] Demonstrating the cache competences by comparing the freshly computed values to " + "the one saved in the cache.\n"); + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + + demonstrate_cache_competences ( + (*default_mesh)[0]); /** Only demonstrating the cache competences for the first element of the mesh*/ + + /** + * We will now create a second mesh with our custom competence pack that includes the volume competence and our custom defined competence 'volume_squared'. + */ + /* Defining a competence pack with the volume cache competence and our custom defined competence. */ + using custom_element_competences = element_competence_pack; + + /* Defining a custom mesh_type with our competence pack. */ + using custom_mesh = mesh; + + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + t8_global_productionf (" [t8 Step A Mesh handle] Creating a custom mesh for the custom competence with initial " + "refinement level of %d.\n", + level); + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + + /* Creating a custom mesh with the mesh_type including our custom competence pack and the initial refinement level 2. */ + auto custom = handle_hypercube_hybrid_uniform_default (level, comm); + + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + t8_global_productionf (" [t8 Step A Mesh handle] Demonstrating the custom competence 'Squared Value'.\n"); + t8_global_productionf (" [t8 Step A Mesh handle] \n"); + + demonstrate_custom_competence (*custom); + } /* End of mesh scope. */ + /* Finalizing. */ + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +}