From 07a692ac61324398c3c0b859307e855db8205a4b Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 21 Jan 2026 16:06:30 +0100 Subject: [PATCH 01/12] add partition and balance functionality --- mesh_handle/mesh.hxx | 51 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 4f4cc879fc..ff1a1d65b5 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -277,12 +277,53 @@ class mesh { t8_forest_set_adapt (m_uncommitted_forest.value (), m_forest, detail::mesh_adapt_callback_wrapper, recursive); } + /** If this function is called, the mesh will be partitioned on committing. + * The partitioning is done according to the SFC and each rank is assigned + * the same (maybe +1) number of elements. + * \note The partition is carried out only when \ref commit is called. + * \note This setting can be combined with \ref set_adapt and \ref set_balance. The order in which + * these operations are executed is always 1) Adapt 2) Partition 3) Balance. + * \param [in] set_for_coarsening If true, the partitions are choose such that coarsening + * an element once is a process local operation. Default is false. + */ + void + set_partition (bool set_for_coarsening = false) + { + if (!m_uncommitted_forest.has_value ()) { + t8_forest_t new_forest; + t8_forest_init (&new_forest); + m_uncommitted_forest = new_forest; + } + t8_forest_set_partition (m_uncommitted_forest.value (), m_forest, set_for_coarsening); + } + + /** If this function is called, the mesh will be balanced on committing. + * The mesh is said to be balanced if each element has face neighbors of level + * at most +1 or -1 of the element's level. + * \note The balance is carried out only when \ref commit is called. + * \note This setting can be combined with \ref set_adapt and \ref set_partition. The order in which + * these operations are executed is always 1) Adapt 2) Partition 3) Balance. + * \note The balanced mesh is not repartitioned per default to maintain a balanced load. + * Call \ref set_partition if you want to repartition the balanced mesh. + */ + void + set_balance () + { + if (!m_uncommitted_forest.has_value ()) { + t8_forest_t new_forest; + t8_forest_init (&new_forest); + m_uncommitted_forest = new_forest; + } + // Disable repartitioning and let the user call set_partition if desired. + t8_forest_set_balance (tm_uncommitted_forest.value (), m_forest, true); + } + /** Enable or disable the creation of a layer of ghost elements. - * \param [in] do_ghost If true a ghost layer will be created. - * \param [in] ghost_type Controls which neighbors count as ghost elements, - * currently only T8_GHOST_FACES is supported. This value - * is ignored if \a do_ghost = false. - */ + * \param [in] do_ghost If true a ghost layer will be created. + * \param [in] ghost_type Controls which neighbors count as ghost elements, + * currently only T8_GHOST_FACES is supported. This value + * is ignored if \a do_ghost = false. + */ void set_ghost (bool do_ghost = true, t8_ghost_type_t ghost_type = T8_GHOST_FACES) { From cb6a8e0323a0d274736ed11001a9c10f97bb063b Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 21 Jan 2026 16:55:48 +0100 Subject: [PATCH 02/12] begin test --- mesh_handle/mesh.hxx | 15 ++++++++++++++- test/mesh_handle/t8_gtest_adapt.cxx | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index ff1a1d65b5..f94087f5d9 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -30,6 +30,8 @@ #include "element.hxx" #include "competence_pack.hxx" #include "adapt.hxx" +#include "t8_forest/t8_forest_balance.h" +#include "t8_forest/t8_forest_types.h" #include #include #include @@ -162,6 +164,17 @@ class mesh { return m_forest; } + /** Check if the local elements of the mesh are balanced. + * The mesh is said to be balanced if each element has face neighbors of level + * at most +1 or -1 of the element's level. + * \return true if the local elements are balanced, false otherwise. + */ + bool + is_balanced () + { + return t8_forest_is_balanced (m_forest); + } + // --- Methods to access elements. --- /** * Returns a constant iterator to the first (local) mesh element. @@ -315,7 +328,7 @@ class mesh { m_uncommitted_forest = new_forest; } // Disable repartitioning and let the user call set_partition if desired. - t8_forest_set_balance (tm_uncommitted_forest.value (), m_forest, true); + t8_forest_set_balance (m_uncommitted_forest.value (), m_forest, true); } /** Enable or disable the creation of a layer of ghost elements. diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index 5739c790a1..3b909782e1 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -129,6 +129,16 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) // Compare results. EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest)); + // Adapt the mesh handle again and check that it is unbalanced afterwards. + mesh_handle.set_adapt ( + mesh_class::mesh_adapt_callback_wrapper (adapt_callback_test, user_data), false); + mesh_handle.commit (); + EXPECT_FALSE (mesh_handle.is_balanced ()); + mesh_handle.set_balance (); + mesh_handle.set_partition (); + mesh_handle.commit (); + // TODO have a look at t8_gtest_forest_commit for a better structure + EXPECT_TRUE (mesh_handle.is_balanced ()); // Clean up. t8_forest_unref (&forest); } From d0990b29d20d27c84d3734928f4b280fdea7e2ec Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Thu, 22 Jan 2026 16:04:39 +0100 Subject: [PATCH 03/12] adapt test to check also partition and balance --- test/mesh_handle/t8_gtest_adapt.cxx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index 3b909782e1..218ff40589 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -21,7 +21,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., */ /** - * \file t8_gtest_adapt.cxx + * \file t8_gtest_adapt_balance_partition.cxx * Tests for the adapt routines of mesh handles. * This tests uses the callback and user data of tutorial step 3 as example. * The adaptation criterion is to look at the midpoint coordinates of the current element and if @@ -100,6 +100,8 @@ forest_adapt_callback_example (t8_forest_t forest, t8_forest_t forest_from, t8_l /** Test the adapt routine of a mesh handle. * We compare the result to a classically adapted forest with similar callback. + * Therefore we ensure that the functionality for the mesh handle works based on the assumption that the forest functionality is correct. + * Forest functionality is tested elsewhere. */ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) { @@ -137,8 +139,18 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) mesh_handle.set_balance (); mesh_handle.set_partition (); mesh_handle.commit (); - // TODO have a look at t8_gtest_forest_commit for a better structure EXPECT_TRUE (mesh_handle.is_balanced ()); + + // Compare the results again to an appropriate forest. + t8_forest_t forest_compare; + t8_forest_init (&forest_compare); + t8_forest_set_user_data (forest_compare, &user_data); + t8_forest_set_adapt (forest_compare, forest, forest_adapt_callback_example, false); + t8_forest_set_partition (forest_compare, NULL, 0); + t8_forest_set_balance (forest_compare, NULL, 0); + t8_forest_commit (forest_compare); + // Compare results. + EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest_compare)); // Clean up. - t8_forest_unref (&forest); + t8_forest_unref (&forest_compare); } From 72287c871e4c02b65f416421e42049f1aecf5e46 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Thu, 22 Jan 2026 16:05:39 +0100 Subject: [PATCH 04/12] rename adapt test --- .../{t8_gtest_adapt.cxx => t8_gtest_adapt_partition_balance.cxx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/mesh_handle/{t8_gtest_adapt.cxx => t8_gtest_adapt_partition_balance.cxx} (100%) diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx similarity index 100% rename from test/mesh_handle/t8_gtest_adapt.cxx rename to test/mesh_handle/t8_gtest_adapt_partition_balance.cxx From e782a56bc97052d0e366724d129c2b3595460f96 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 26 Jan 2026 10:33:16 +0100 Subject: [PATCH 05/12] WIP: add balance,partition --- test/CMakeLists.txt | 14 +++++++------- .../t8_gtest_adapt_partition_balance.cxx | 17 +++++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 77b3c56d99..44bf59d4bd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -211,13 +211,13 @@ endif() add_t8_cpp_test( NAME t8_gtest_vector_split_serial SOURCES t8_helper_functions/t8_gtest_vector_split.cxx ) if( T8CODE_BUILD_MESH_HANDLE ) - add_t8_cpp_test( NAME t8_gtest_mesh_handle_parallel SOURCES mesh_handle/t8_gtest_mesh_handle.cxx ) - add_t8_cpp_test( NAME t8_gtest_compare_handle_to_forest_serial SOURCES mesh_handle/t8_gtest_compare_handle_to_forest.cxx ) - add_t8_cpp_test( NAME t8_gtest_handle_ghost_parallel SOURCES mesh_handle/t8_gtest_ghost.cxx ) - add_t8_cpp_test( NAME t8_gtest_custom_competence_serial SOURCES mesh_handle/t8_gtest_custom_competence.cxx ) - add_t8_cpp_test( NAME t8_gtest_cache_competence_serial SOURCES mesh_handle/t8_gtest_cache_competence.cxx ) - add_t8_cpp_test( NAME t8_gtest_handle_data_parallel SOURCES mesh_handle/t8_gtest_handle_data.cxx ) - add_t8_cpp_test( NAME t8_gtest_handle_adapt_serial SOURCES mesh_handle/t8_gtest_adapt.cxx ) + add_t8_cpp_test( NAME t8_gtest_mesh_handle_parallel SOURCES mesh_handle/t8_gtest_mesh_handle.cxx ) + add_t8_cpp_test( NAME t8_gtest_compare_handle_to_forest_serial SOURCES mesh_handle/t8_gtest_compare_handle_to_forest.cxx ) + add_t8_cpp_test( NAME t8_gtest_handle_ghost_parallel SOURCES mesh_handle/t8_gtest_ghost.cxx ) + add_t8_cpp_test( NAME t8_gtest_custom_competence_serial SOURCES mesh_handle/t8_gtest_custom_competence.cxx ) + add_t8_cpp_test( NAME t8_gtest_cache_competence_serial SOURCES mesh_handle/t8_gtest_cache_competence.cxx ) + add_t8_cpp_test( NAME t8_gtest_handle_data_parallel SOURCES mesh_handle/t8_gtest_handle_data.cxx ) + add_t8_cpp_test( NAME t8_gtest_adapt_partition_balance_parallel SOURCES mesh_handle/t8_gtest_adapt_partition_balance.cxx ) endif() copy_test_file( test_cube_unstructured_1.inp ) diff --git a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx index 218ff40589..abf50cee51 100644 --- a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx +++ b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx @@ -21,11 +21,13 @@ along with t8code; if not, write to the Free Software Foundation, Inc., */ /** - * \file t8_gtest_adapt_balance_partition.cxx - * Tests for the adapt routines of mesh handles. - * This tests uses the callback and user data of tutorial step 3 as example. + * \file t8_gtest_adapt_partition_balance.cxx + * Tests for the adapt, partition and balance routines of mesh handles. + * For the adapt routine, we use the callback and user data of tutorial step 3 as example. * The adaptation criterion is to look at the midpoint coordinates of the current element and if * they are inside a sphere around a given midpoint we refine, if they are outside, we coarsen. + * The test compares the results of the mesh handle to a forest adapted with the same criterion and balanced and partitioned similarly. + * Therefore, the check is based on the assumption that the forest functionality works as intended and is tested elsewhere. */ #include #include @@ -98,10 +100,9 @@ forest_adapt_callback_example (t8_forest_t forest, t8_forest_t forest_from, t8_l return 0; } -/** Test the adapt routine of a mesh handle. - * We compare the result to a classically adapted forest with similar callback. - * Therefore we ensure that the functionality for the mesh handle works based on the assumption that the forest functionality is correct. - * Forest functionality is tested elsewhere. +/** Test the adapt partition and balance routine of a mesh handle. + * The test compares the results of the mesh handle to a forest adapted with the same criterion and balanced and partitioned similarly. + * Therefore, the check is based on the assumption that the forest functionality works as intended and is tested elsewhere. */ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) { @@ -136,6 +137,7 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) mesh_class::mesh_adapt_callback_wrapper (adapt_callback_test, user_data), false); mesh_handle.commit (); EXPECT_FALSE (mesh_handle.is_balanced ()); + // Now apply partition and balance. mesh_handle.set_balance (); mesh_handle.set_partition (); mesh_handle.commit (); @@ -149,7 +151,6 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) t8_forest_set_partition (forest_compare, NULL, 0); t8_forest_set_balance (forest_compare, NULL, 0); t8_forest_commit (forest_compare); - // Compare results. EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest_compare)); // Clean up. t8_forest_unref (&forest_compare); From a869748e75623b38202285b10d9cb973c2ed1f1b Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Tue, 27 Jan 2026 11:24:17 +0100 Subject: [PATCH 06/12] add flag and correct test --- mesh_handle/adapt.hxx | 10 +++++----- mesh_handle/mesh.hxx | 18 +++++++++++------- .../t8_gtest_adapt_partition_balance.cxx | 12 +++++------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index 9bf787c917..cf962b0b62 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -120,8 +120,8 @@ class AdaptRegistry { public: /** Static function to register \a context using \a forest as identifier. * This makes the context publicly available using the Registry. - * \param [in] forest The forest identifier. In our case, this is the forest to be adapted - * and not the forest from which we adapt. + * \param [in] forest The forest identifier. In our case, this is the forest from which we adapt the mesh because + * we do not change this forest during adaptation such that it is valid unique identifier. * \param [in] context The context to register. Use unique pointer to ensure proper memory management and ownership. */ static void @@ -135,7 +135,7 @@ class AdaptRegistry { } /** Static function to unregister a context using \a forest as identifier. - * \param [in] forest The forest identifier. In our case, this is the forest to be adapted. + * \param [in] forest The forest identifier. In our case, this is the forest from which we adapt. */ static void unregister_context (t8_forest_t forest) @@ -146,7 +146,7 @@ class AdaptRegistry { } /** Getter for a context using \a forest as identifier. - * \param [in] forest The forest identifier. In our case, this is the forest to be adapted. + * \param [in] forest The forest identifier. In our case, this is the forest from which we adapt. * \return Pointer to the context registered with the id \a forest if found, nullptr otherwise. */ static MeshAdaptContextBase* @@ -193,7 +193,7 @@ mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t fo { // Get static adapt context from the registry. // Via this, we can access the mesh handle and the user defined adapt callback that uses mesh handle functionality. - auto* context = AdaptRegistry::get (forest); + auto* context = AdaptRegistry::get (forest_from); if (!context) { t8_global_infof ( "Something went wrong while registering the adaptation callbacks. Please check your implementation."); diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 6a52c477e2..c9a459367d 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -283,8 +283,7 @@ class mesh { } // Create and register adaptation context holding the mesh handle and the user defined callback. detail::AdaptRegistry::register_context ( - m_uncommitted_forest.value (), - std::make_unique> (*this, std::move (adapt_callback))); + m_forest, std::make_unique> (*this, std::move (adapt_callback))); // Set up the forest for adaptation using the wrapper callback. t8_forest_set_adapt (m_uncommitted_forest.value (), m_forest, detail::mesh_adapt_callback_wrapper, recursive); @@ -314,13 +313,16 @@ class mesh { * The mesh is said to be balanced if each element has face neighbors of level * at most +1 or -1 of the element's level. * \note The balance is carried out only when \ref commit is called. + * \param [in] no_repartition Balance constructs several intermediate steps that + * are refined from each other. In order to maintain a balanced load, a repartitioning is performed in each + * round and the resulting mesh is load-balanced per default. + * Set \a no_repartition to true if this behaviour is not desired. + * If \a no_repartition is false (default), an additional call of \ref set_partition is not necessary. * \note This setting can be combined with \ref set_adapt and \ref set_partition. The order in which * these operations are executed is always 1) Adapt 2) Partition 3) Balance. - * \note The balanced mesh is not repartitioned per default to maintain a balanced load. - * Call \ref set_partition if you want to repartition the balanced mesh. */ void - set_balance () + set_balance (bool no_repartition = false) { if (!m_uncommitted_forest.has_value ()) { t8_forest_t new_forest; @@ -328,7 +330,7 @@ class mesh { m_uncommitted_forest = new_forest; } // Disable repartitioning and let the user call set_partition if desired. - t8_forest_set_balance (m_uncommitted_forest.value (), m_forest, true); + t8_forest_set_balance (m_uncommitted_forest.value (), m_forest, no_repartition); } /** Enable or disable the creation of a layer of ghost elements. @@ -367,16 +369,18 @@ class mesh { if (m_uncommitted_forest.value ()->set_from == NULL) { t8_forest_set_copy (m_uncommitted_forest.value (), m_forest); } + t8_forest_ref (m_forest); t8_forest_commit (m_uncommitted_forest.value ()); // Check if we adapted and unregister the adapt context if so. if (detail::AdaptRegistry::get (m_uncommitted_forest.value ()) != nullptr) { - detail::AdaptRegistry::unregister_context (m_uncommitted_forest.value ()); + detail::AdaptRegistry::unregister_context (m_forest); if (!std::is_void::value) { t8_global_infof ( "Please note that the element data is not interpolated automatically during adaptation. Use the " "function set_element_data() to provide new adapted element data.\n"); } } + t8_forest_unref (&m_forest); // Update underlying forest of the mesh. m_forest = m_uncommitted_forest.value (); m_uncommitted_forest = std::nullopt; diff --git a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx index abf50cee51..1afa512dbb 100644 --- a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx +++ b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx @@ -127,17 +127,14 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) mesh_class::mesh_adapt_callback_wrapper (adapt_callback_test, user_data), false); mesh_handle.commit (); // Adapt forest classically. - forest = t8_forest_new_adapt (forest, forest_adapt_callback_example, 0, 1, &user_data); + forest = t8_forest_new_adapt (forest, forest_adapt_callback_example, 0, 0, &user_data); // Compare results. EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest)); - // Adapt the mesh handle again and check that it is unbalanced afterwards. + // Adapt the mesh handle again and apply partition and balance. mesh_handle.set_adapt ( mesh_class::mesh_adapt_callback_wrapper (adapt_callback_test, user_data), false); - mesh_handle.commit (); - EXPECT_FALSE (mesh_handle.is_balanced ()); - // Now apply partition and balance. mesh_handle.set_balance (); mesh_handle.set_partition (); mesh_handle.commit (); @@ -148,10 +145,11 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) t8_forest_init (&forest_compare); t8_forest_set_user_data (forest_compare, &user_data); t8_forest_set_adapt (forest_compare, forest, forest_adapt_callback_example, false); - t8_forest_set_partition (forest_compare, NULL, 0); - t8_forest_set_balance (forest_compare, NULL, 0); + t8_forest_set_partition (forest_compare, NULL, false); + t8_forest_set_balance (forest_compare, NULL, false); t8_forest_commit (forest_compare); EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest_compare)); + // Clean up. t8_forest_unref (&forest_compare); } From 75657fac69afe3ea9232438e8ef9457d39339a0f Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Tue, 27 Jan 2026 13:55:50 +0100 Subject: [PATCH 07/12] change order for codecov --- test/mesh_handle/t8_gtest_adapt_partition_balance.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx index 1afa512dbb..3dde1d26cf 100644 --- a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx +++ b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx @@ -133,10 +133,10 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest)); // Adapt the mesh handle again and apply partition and balance. - mesh_handle.set_adapt ( - mesh_class::mesh_adapt_callback_wrapper (adapt_callback_test, user_data), false); mesh_handle.set_balance (); mesh_handle.set_partition (); + mesh_handle.set_adapt ( + mesh_class::mesh_adapt_callback_wrapper (adapt_callback_test, user_data), false); mesh_handle.commit (); EXPECT_TRUE (mesh_handle.is_balanced ()); From dde0ef9abf8228243bb5d38ef6a14fa0ce11722c Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Thu, 19 Mar 2026 16:42:03 +0100 Subject: [PATCH 08/12] add tutorial 5 with mesh handle# --- mesh_handle/competence_pack.hxx | 2 + mesh_handle/mesh.hxx | 7 + .../t8_gtest_adapt_partition_balance.cxx | 1 - tutorials/CMakeLists.txt | 10 +- tutorials/general/t8_step5_mesh_handle.cxx | 230 ++++++++++++++++++ 5 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 tutorials/general/t8_step5_mesh_handle.cxx diff --git a/mesh_handle/competence_pack.hxx b/mesh_handle/competence_pack.hxx index d7406b5112..285c767208 100644 --- a/mesh_handle/competence_pack.hxx +++ b/mesh_handle/competence_pack.hxx @@ -50,6 +50,8 @@ using all_cache_competences = competence_pack; +using no_competences = competence_pack<>; + /** Predefined competence pack combining all competences related to faces. */ using cache_face_competences = competence_pack; diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 06d5477a6a..9f9e0aaa15 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -34,6 +34,7 @@ #include "t8_forest/t8_forest_types.h" #include #include +#include #include #include #include @@ -135,6 +136,12 @@ class mesh { return t8_forest_get_local_num_leaf_elements (m_forest); } + int + write_vtk (const char* fileprefix) + { + return t8_forest_write_vtk (m_forest, fileprefix); + } + /** * Getter for the number of ghost elements. * \return Number of ghost elements in the mesh. diff --git a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx index fa09ba5367..e0dfee8c30 100644 --- a/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx +++ b/test/mesh_handle/t8_gtest_adapt_partition_balance.cxx @@ -34,7 +34,6 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include #include -#include #include #include #include diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 5d3a1d1d87..c0f1d654c4 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -32,7 +32,14 @@ function( add_t8_tutorial ) set(TUTORIAL_BUILD_DIR "${CMAKE_BINARY_DIR}/${TUTORIAL_RELATIVE_DIR}") add_executable( ${ADD_T8_TUTORIAL_NAME} ${ADD_T8_TUTORIAL_SOURCES} ) - target_link_libraries( ${ADD_T8_TUTORIAL_NAME} PRIVATE T8 SC::SC ) + target_link_libraries( ${ADD_T8_TUTORIAL_NAME} PRIVATE T8 SC::SC T8_MESH_HANDLE) + + string( FIND ${ADD_T8_TUTORIAL_NAME} "mesh_handle" is_mesh_handle_file ) + + if ( (${is_mesh_handle_file} GREATER_EQUAL 0) ) + target_link_libraries(${ADD_T8_TUTORIAL_NAME} PRIVATE T8_MESH_HANDLE ) + endif () + target_include_directories( ${ADD_T8_TUTORIAL_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/.. ) set_target_properties(${ADD_T8_TUTORIAL_NAME} PROPERTIES @@ -59,6 +66,7 @@ add_t8_tutorial( NAME t8_step2_uniform_forest SOURCES general/t8_step2 add_t8_tutorial( NAME t8_step3_adapt_forest SOURCES general/t8_step3_main.cxx general/t8_step3_adapt_forest.cxx ) add_t8_tutorial( NAME t8_step4_partition_balance_ghost SOURCES general/t8_step4_main.cxx general/t8_step3_adapt_forest.cxx general/t8_step4_partition_balance_ghost.cxx ) add_t8_tutorial( NAME t8_step5_element_data SOURCES general/t8_step5_main.cxx general/t8_step3_adapt_forest.cxx general/t8_step5_element_data.cxx ) +add_t8_tutorial( NAME t8_step5_mesh_handle SOURCES general/t8_step5_mesh_handle.cxx ) add_t8_tutorial( NAME t8_step6_stencil SOURCES general/t8_step6_main.cxx general/t8_step3_adapt_forest.cxx general/t8_step6_stencil.cxx ) add_t8_tutorial( NAME t8_step7_interpolation SOURCES general/t8_step7_main.cxx general/t8_step7_interpolation.cxx ) add_t8_tutorial( NAME t8_tutorial_build_cmesh SOURCES general/t8_tutorial_build_cmesh.cxx general/t8_tutorial_build_cmesh_main.cxx) diff --git a/tutorials/general/t8_step5_mesh_handle.cxx b/tutorials/general/t8_step5_mesh_handle.cxx new file mode 100644 index 0000000000..fb270e32e9 --- /dev/null +++ b/tutorials/general/t8_step5_mesh_handle.cxx @@ -0,0 +1,230 @@ +/* + 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. +*/ + +#include + +#include +#include +#include +#include +#include +#include + +/* The data that we want to store for each element. + * In this example we want to store the element's level and volume. */ +struct t8_step5_data_per_element +{ + int level; + double volume; +}; + +struct user_data +{ + t8_3D_vec midpoint; /**< The midpoint of our sphere. */ + double refine_if_inside_radius; /**< If an element's center is smaller than this value, we refine the element. */ + double coarsen_if_outside_radius; /**< If an element's center is larger this value, we coarsen its family. */ +}; + +template +int +adapt_callback ([[maybe_unused]] const TMeshClass &mesh, + const std::vector &elements, const user_data &user_data) +{ + auto element_centroid = elements[0].get_centroid (); + double dist = t8_dist (element_centroid, user_data.midpoint); + if (dist < user_data.refine_if_inside_radius) { + return 1; + } + // Check if we got a family and if yes, if we should coarsen. + if ((elements.size () > 1) && (dist > user_data.coarsen_if_outside_radius)) { + return -1; + } + return 0; +} + +template +std::unique_ptr +t8_step5_build_mesh (sc_MPI_Comm comm, int level) +{ + auto mesh_handle = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, comm); + + struct user_data adapt_data = { + { 0.5, 0.5, 1 }, /* Midpoints of the sphere. */ + 0.2, /* Refine if inside this radius. */ + 0.4 /* Coarsen if outside this radius. */ + }; + /* Start with a uniform forest. */ + /* Adapt, partition, balance and create ghost elements all in the same step. */ + mesh_handle->set_balance (); + mesh_handle->set_partition (); + mesh_handle->set_adapt (TMesh::template mesh_adapt_callback_wrapper (adapt_callback, adapt_data), + false); + mesh_handle->set_ghost (); + mesh_handle->commit (); + return mesh_handle; +} + +template +void +t8_step5_set_element_data (TMeshClass &mesh) +{ + for (auto &elem : *mesh) { + elem.set_element_data ({ elem.get_level (), elem.get_volume () }); + } +} + +/* Write the forest as vtu and also write the element's volumes in the file. + * + * t8code supports writing element based data to vtu as long as its stored + * as doubles. Each of the data fields to write has to be provided in its own + * array of length num_local_elements. + * We support two types: T8_VTK_SCALAR - One double per element + * and T8_VTK_VECTOR - 3 doubles per element + */ +template +static void +t8_step5_output_data_to_vtu (TMeshClass &mesh, const char *prefix) +{ + t8_locidx_t num_elements = mesh->get_num_local_elements (); + + /* We need to allocate a new array to store the volumes on their own. + * This array has one entry per local element. */ + double *element_volumes = T8_ALLOC (double, num_elements); + /* The number of user defined data fields to write. */ + int num_data = 1; + /* For each user defined data field we need one t8_vtk_data_field_t variable */ + t8_vtk_data_field_t vtk_data; + /* Set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR */ + vtk_data.type = T8_VTK_SCALAR; + /* The name of the field as should be written to the file. */ + strcpy (vtk_data.description, "Element volume"); + vtk_data.data = element_volumes; + /* Copy the element's volumes from our data array to the output array. */ + for (t8_locidx_t ielem = 0; ielem < num_elements; ++ielem) { + element_volumes[ielem] = ((*mesh)[ielem]).get_element_data ().volume; + } + { + /* To write user defined data, we need the extended output function t8_forest_vtk_write_file + * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which + * properties of the forest to write. */ + bool write_treeid = false; + int write_mpirank = 1; + int write_level = 1; + int write_element_id = 1; + int write_ghosts = 0; + t8_forest_write_vtk_ext (mesh->get_forest (), prefix, write_treeid, write_mpirank, write_level, write_element_id, + write_ghosts, 0, 0, num_data, &vtk_data); + } + T8_FREE (element_volumes); +} + +int +t8_step5_main (int argc, char **argv) +{ + /* The prefix for our output files. */ + const char *prefix_mesh = "t8_step5_handle"; + const char *prefix_mesh_with_data = "t8_step5_handle_with_volume_data"; + /* The uniform refinement level. */ + const int level = 3; + + /* 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); + + /* Print a message on the root process. */ + t8_global_productionf (" [step5] \n"); + t8_global_productionf (" [step5] Hello, this is the step5 example of t8code.\n"); + t8_global_productionf ( + " [step5] In this example we will store data on our elements and exchange the data of ghost elements.\n"); + t8_global_productionf (" [step5] \n"); + + /* We will use MPI_COMM_WORLD as a communicator. */ + sc_MPI_Comm comm = sc_MPI_COMM_WORLD; + + /* + * Setup. + * Build cmesh and adapt uniformly. + * Adapt similar to step 3 & 4. + */ + + t8_global_productionf (" [step5] \n"); + t8_global_productionf (" [step5] Creating an adapted mesh as in step3.\n"); + t8_global_productionf (" [step5] \n"); + { // begin scope of mesh + using mesh_class = t8_mesh_handle::mesh; + auto mesh = t8_step5_build_mesh (comm, level); + + mesh->write_vtk (prefix_mesh); + t8_global_productionf (" [step5] Wrote mesh to vtu files: %s*\n", prefix_mesh); + + /* + * Set element data. + */ + t8_step5_set_element_data (mesh); + + t8_global_productionf (" [step5] Computed level and volume data for local elements.\n"); + if (mesh->get_num_local_elements () > 0) { + /* Output the stored data of the first local element (if it exists). */ + t8_global_productionf (" [step5] Element 0 has level %i and volume %e.\n", ((*mesh)[0]).get_element_data ().level, + ((*mesh)[0]).get_element_data ().volume); + } + + /* + * Exchange the data values of the ghost elements + */ + mesh->exchange_ghost_data (); + t8_global_productionf (" [step5] Exchanged ghost data.\n"); + + if (mesh->get_num_ghosts () > 0) { + /* output the data of the first ghost element (if it exists) */ + t8_locidx_t first_ghost_index = mesh->get_num_local_elements (); + t8_global_productionf (" [step5] Ghost 0 has level %i and volume %e.\n", + ((*mesh)[first_ghost_index]).get_element_data ().level, + ((*mesh)[first_ghost_index]).get_element_data ().volume); + } + + /* + * Output the volume data to vtu. + */ + t8_step5_output_data_to_vtu (mesh, prefix_mesh_with_data); + t8_global_productionf (" [step5] Wrote mesh and volume data to %s*.\n", prefix_mesh_with_data); + + } // end scope of mesh + /** Cleanup. */ + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return 0; +} + +int +main (int argc, char **argv) +{ + return t8_step5_main (argc, argv); +} From 6eb819a5f48ea0ff41ccb0ef769f2f995a535f10 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 20 Mar 2026 09:08:49 +0100 Subject: [PATCH 09/12] update tutorial --- tutorials/general/t8_step5_mesh_handle.cxx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tutorials/general/t8_step5_mesh_handle.cxx b/tutorials/general/t8_step5_mesh_handle.cxx index fb270e32e9..1a06152145 100644 --- a/tutorials/general/t8_step5_mesh_handle.cxx +++ b/tutorials/general/t8_step5_mesh_handle.cxx @@ -92,6 +92,13 @@ t8_step5_set_element_data (TMeshClass &mesh) } } +template +void +t8_step5_exchange_ghost_data (TMeshClass &mesh) +{ + mesh->exchange_ghost_data (); +} + /* Write the forest as vtu and also write the element's volumes in the file. * * t8code supports writing element based data to vtu as long as its stored @@ -181,9 +188,6 @@ t8_step5_main (int argc, char **argv) mesh->write_vtk (prefix_mesh); t8_global_productionf (" [step5] Wrote mesh to vtu files: %s*\n", prefix_mesh); - /* - * Set element data. - */ t8_step5_set_element_data (mesh); t8_global_productionf (" [step5] Computed level and volume data for local elements.\n"); @@ -196,7 +200,7 @@ t8_step5_main (int argc, char **argv) /* * Exchange the data values of the ghost elements */ - mesh->exchange_ghost_data (); + t8_step5_exchange_ghost_data (mesh); t8_global_productionf (" [step5] Exchanged ghost data.\n"); if (mesh->get_num_ghosts () > 0) { From 209b5134c5a219efa42734b04f4ec55ab42088fa Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 23 Mar 2026 11:07:34 +0100 Subject: [PATCH 10/12] new file for mesh io --- mesh_handle/mesh.hxx | 7 -- mesh_handle/mesh_io.hxx | 92 ++++++++++++++++++++++ tutorials/general/t8_step5_mesh_handle.cxx | 89 +++++++++++---------- 3 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 mesh_handle/mesh_io.hxx diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 9f9e0aaa15..06d5477a6a 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -34,7 +34,6 @@ #include "t8_forest/t8_forest_types.h" #include #include -#include #include #include #include @@ -136,12 +135,6 @@ class mesh { return t8_forest_get_local_num_leaf_elements (m_forest); } - int - write_vtk (const char* fileprefix) - { - return t8_forest_write_vtk (m_forest, fileprefix); - } - /** * Getter for the number of ghost elements. * \return Number of ghost elements in the mesh. diff --git a/mesh_handle/mesh_io.hxx b/mesh_handle/mesh_io.hxx new file mode 100644 index 0000000000..b5e0333b38 --- /dev/null +++ b/mesh_handle/mesh_io.hxx @@ -0,0 +1,92 @@ +/* + 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 classes in parallel. + + Copyright (C) 2025 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 mesh_io.hxx + * In- and output of meshes. For example, writing a mesh to vtk format. + */ + +#pragma once +#include +namespace t8_mesh_handle +{ + +/** + * Write the mesh in a parallel vtu format. Extended version. + * See \see write_mesh_to_vtk for the standard version of this function. + * Writes one master .pvtu file and each process writes in its own .vtu file. + * If linked and not otherwise specified, the VTK API is used. + * If the VTK library is not linked, an ASCII file is written. + * This may change in accordance with \a write_ghosts, \a write_curved and + * \a do_not_use_API, because the export of ghosts is not yet available with + * the VTK API and the export of curved elements is not available with the + * inbuilt function to write ASCII files. The function will for example + * still use the VTK API to satisfy \a write_curved, even if \a do_not_use_API + * is set to true. + * This function is collective and must be called on each process. + * \param [in] mesh The mesh to write. + * \param [in] fileprefix The prefix of the files where the vtk will be stored. + * The master file is then fileprefix.pvtu and the process with rank r writes in the file fileprefix_r.vtu. + * \param [in] num_data Number of user defined double valued data fields to write. + * \param [in] data Array of t8_vtk_data_field_t of length \a num_data providing the user defined + * per element data. If scalar and vector fields are used, all scalar fields must come first in the array. + * \param [in] write_treeid If true, the global tree id of the underlying forest is written for each element. + * \param [in] write_mpirank If true, the mpirank is written for each element. + * \param [in] write_level If true, the refinement level is written for each element. + * \param [in] write_element_id If true, the global element id is written for each element. + * \param [in] write_ghosts If true, each process additionally writes its ghost elements. + * For ghost element the treeid of the underlying forest is -1. + * \param [in] write_curved If true, write the elements as curved element types from vtk. + * \param [in] do_not_use_API Do not use the VTK API, even if linked and available. + * \return True if successful, false if not (process local). + */ +template +int +write_mesh_to_vtk_ext (TMeshClass &mesh, const char *fileprefix, const int num_data, t8_vtk_data_field_t *data, + bool write_treeid = false, bool write_mpirank = true, bool write_level = true, + bool write_element_id = true, bool write_ghosts = false, bool write_curved = false, + bool do_not_use_API = false) +{ + return t8_forest_write_vtk_ext (mesh->get_forest (), fileprefix, write_treeid, write_mpirank, write_level, + write_element_id, write_ghosts, write_curved, do_not_use_API, num_data, &data); +} + +/** + * Write the mesh in a parallel vtu format. Writes one master + * .pvtu file and each process writes in its own .vtu file. + * If linked, the VTK API is used. + * If the VTK library is not linked, an ASCII file is written. + * This function writes the elements, level, mpirank, element id and the tree id of the underlying forest as data. + * This function is collective and must be called on each process. + * For more options use \see write_mesh_to_vtk_ext. + * \param [in] mesh The mesh to write. + * \param [in] fileprefix The prefix of the files where the vtk will be stored. + * The master file is then fileprefix.pvtu and the process with rank r writes in the file fileprefix_r.vtu. + * \return True if successful, false if not (process local). + */ +template +int +write_mesh_to_vtk (TMeshClass &mesh, const char *fileprefix) +{ + return t8_forest_write_vtk (mesh->get_forest (), fileprefix); +} + +} // namespace t8_mesh_handle diff --git a/tutorials/general/t8_step5_mesh_handle.cxx b/tutorials/general/t8_step5_mesh_handle.cxx index 1a06152145..4b66e29796 100644 --- a/tutorials/general/t8_step5_mesh_handle.cxx +++ b/tutorials/general/t8_step5_mesh_handle.cxx @@ -20,12 +20,18 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** \file t8_step5_mesh_handle.cxx + * + * This is the same as t8_step5_element_data.cxx but using the mesh handle interface instead of the forest interface. + */ + #include #include #include #include #include +#include #include #include @@ -37,6 +43,7 @@ struct t8_step5_data_per_element double volume; }; +/** User data type we will pass to the adapt callback. */ struct user_data { t8_3D_vec midpoint; /**< The midpoint of our sphere. */ @@ -44,6 +51,16 @@ struct user_data double coarsen_if_outside_radius; /**< If an element's center is larger this value, we coarsen its family. */ }; +/** The adaptation callback function. This will refine elements inside of a given sphere and coarsen the elements + * outside of a given sphere. + * \tparam TMeshClass The mesh handle class. + * \param [in] mesh The mesh that should be adapted. + * \param [in] elements One element or a family of elements to consider for adaptation. + * \param [in] user_data The user data to be used during the adaptation process. + * \return 1 if the first entry in \a elements should be refined, + * -1 if the family \a elements shall be coarsened, + * 0 else. + */ template int adapt_callback ([[maybe_unused]] const TMeshClass &mesh, @@ -61,19 +78,24 @@ adapt_callback ([[maybe_unused]] const TMeshClass &mesh, return 0; } +/** Build a mesh with initial uniform refinement level \a level which is adapted according to \ref adapt_callback, + * partitioned and balanced afterwards and ghost elements are set. + * \tparam TMeshClass The mesh handle class. + * \param [in] comm MPI communicator to use. + * \param [in] level Initial refinement level. + * \return Unique pointer to the mesh created. + */ template std::unique_ptr t8_step5_build_mesh (sc_MPI_Comm comm, int level) { auto mesh_handle = t8_mesh_handle::handle_hypercube_hybrid_uniform_default (level, comm); - struct user_data adapt_data = { - { 0.5, 0.5, 1 }, /* Midpoints of the sphere. */ + { 0.5, 0.5, 1 }, /* Midpoint of the sphere. */ 0.2, /* Refine if inside this radius. */ 0.4 /* Coarsen if outside this radius. */ }; - /* Start with a uniform forest. */ - /* Adapt, partition, balance and create ghost elements all in the same step. */ + /* Adapt, partition, balance and create ghost elements. */ mesh_handle->set_balance (); mesh_handle->set_partition (); mesh_handle->set_adapt (TMesh::template mesh_adapt_callback_wrapper (adapt_callback, adapt_data), @@ -83,6 +105,7 @@ t8_step5_build_mesh (sc_MPI_Comm comm, int level) return mesh_handle; } +/** Set element data to the mesh handle. */ template void t8_step5_set_element_data (TMeshClass &mesh) @@ -92,6 +115,7 @@ t8_step5_set_element_data (TMeshClass &mesh) } } +/** Exchange element data set in \ref t8_step5_set_element_data for ghost elements. */ template void t8_step5_exchange_ghost_data (TMeshClass &mesh) @@ -99,8 +123,7 @@ t8_step5_exchange_ghost_data (TMeshClass &mesh) mesh->exchange_ghost_data (); } -/* Write the forest as vtu and also write the element's volumes in the file. - * +/** Write the mesh as vtu and also write the element's volumes in the file. * t8code supports writing element based data to vtu as long as its stored * as doubles. Each of the data fields to write has to be provided in its own * array of length num_local_elements. @@ -112,7 +135,6 @@ static void t8_step5_output_data_to_vtu (TMeshClass &mesh, const char *prefix) { t8_locidx_t num_elements = mesh->get_num_local_elements (); - /* We need to allocate a new array to store the volumes on their own. * This array has one entry per local element. */ double *element_volumes = T8_ALLOC (double, num_elements); @@ -120,7 +142,7 @@ t8_step5_output_data_to_vtu (TMeshClass &mesh, const char *prefix) int num_data = 1; /* For each user defined data field we need one t8_vtk_data_field_t variable */ t8_vtk_data_field_t vtk_data; - /* Set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR */ + /* Set the type of this variable. Since we have one value per element, we pick T8_VTK_SCALAR. */ vtk_data.type = T8_VTK_SCALAR; /* The name of the field as should be written to the file. */ strcpy (vtk_data.description, "Element volume"); @@ -129,28 +151,20 @@ t8_step5_output_data_to_vtu (TMeshClass &mesh, const char *prefix) for (t8_locidx_t ielem = 0; ielem < num_elements; ++ielem) { element_volumes[ielem] = ((*mesh)[ielem]).get_element_data ().volume; } - { - /* To write user defined data, we need the extended output function t8_forest_vtk_write_file - * from t8_forest_vtk.h. Despite writing user data, it also offers more control over which - * properties of the forest to write. */ - bool write_treeid = false; - int write_mpirank = 1; - int write_level = 1; - int write_element_id = 1; - int write_ghosts = 0; - t8_forest_write_vtk_ext (mesh->get_forest (), prefix, write_treeid, write_mpirank, write_level, write_element_id, - write_ghosts, 0, 0, num_data, &vtk_data); - } + /* To write user defined data, we need the extended output function write_mesh_to_vtk_ext. + * Despite writing user data, it also offers more control over which properties to write. */ + write_mesh_to_vtk_ext (mesh, prefix, num_data, &vtk_data); T8_FREE (element_volumes); } +/** Main function. */ int t8_step5_main (int argc, char **argv) { /* The prefix for our output files. */ const char *prefix_mesh = "t8_step5_handle"; const char *prefix_mesh_with_data = "t8_step5_handle_with_volume_data"; - /* The uniform refinement level. */ + /* The initial uniform refinement level. */ const int level = 3; /* Initialize MPI. This has to happen before we initialize sc or t8code. */ @@ -161,6 +175,8 @@ t8_step5_main (int argc, char **argv) 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 message on the root process. */ t8_global_productionf (" [step5] \n"); @@ -169,19 +185,13 @@ t8_step5_main (int argc, char **argv) " [step5] In this example we will store data on our elements and exchange the data of ghost elements.\n"); t8_global_productionf (" [step5] \n"); - /* We will use MPI_COMM_WORLD as a communicator. */ - sc_MPI_Comm comm = sc_MPI_COMM_WORLD; - - /* - * Setup. - * Build cmesh and adapt uniformly. - * Adapt similar to step 3 & 4. - */ - + /* Setup: Build cmesh and adapt uniformly. Adapt similar to step 3 & 4. */ t8_global_productionf (" [step5] \n"); t8_global_productionf (" [step5] Creating an adapted mesh as in step3.\n"); t8_global_productionf (" [step5] \n"); - { // begin scope of mesh + { /* We put the mesh in its own scope so that it is automatically destroyed at the end of the scope. + * This is only necessary because sc_finalize checks if there are leftover references. + * This unique pointer would have been destroyed automatically at the end of the programme. */ using mesh_class = t8_mesh_handle::mesh; auto mesh = t8_step5_build_mesh (comm, level); @@ -189,7 +199,6 @@ t8_step5_main (int argc, char **argv) t8_global_productionf (" [step5] Wrote mesh to vtu files: %s*\n", prefix_mesh); t8_step5_set_element_data (mesh); - t8_global_productionf (" [step5] Computed level and volume data for local elements.\n"); if (mesh->get_num_local_elements () > 0) { /* Output the stored data of the first local element (if it exists). */ @@ -197,28 +206,23 @@ t8_step5_main (int argc, char **argv) ((*mesh)[0]).get_element_data ().volume); } - /* - * Exchange the data values of the ghost elements - */ + /* Exchange the data values of the ghost elements. */ t8_step5_exchange_ghost_data (mesh); t8_global_productionf (" [step5] Exchanged ghost data.\n"); - if (mesh->get_num_ghosts () > 0) { - /* output the data of the first ghost element (if it exists) */ + /* Output the data of the first ghost element (if it exists). */ t8_locidx_t first_ghost_index = mesh->get_num_local_elements (); t8_global_productionf (" [step5] Ghost 0 has level %i and volume %e.\n", ((*mesh)[first_ghost_index]).get_element_data ().level, ((*mesh)[first_ghost_index]).get_element_data ().volume); } - /* - * Output the volume data to vtu. - */ + /* Output the volume data to vtu. */ t8_step5_output_data_to_vtu (mesh, prefix_mesh_with_data); t8_global_productionf (" [step5] Wrote mesh and volume data to %s*.\n", prefix_mesh_with_data); - } // end scope of mesh - /** Cleanup. */ + /* Cleanup. */ + } // End scope of mesh sc_finalize (); mpiret = sc_MPI_Finalize (); @@ -227,6 +231,7 @@ t8_step5_main (int argc, char **argv) return 0; } +/** Entry point of the program. */ int main (int argc, char **argv) { From 48c46fc7a2f7c090a79aeec6d0935d59f9724959 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 23 Mar 2026 11:10:43 +0100 Subject: [PATCH 11/12] coppyright year --- mesh_handle/mesh_io.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesh_handle/mesh_io.hxx b/mesh_handle/mesh_io.hxx index b5e0333b38..f93f0d6670 100644 --- a/mesh_handle/mesh_io.hxx +++ b/mesh_handle/mesh_io.hxx @@ -3,7 +3,7 @@ t8code is a C library to manage a collection (a forest) of multiple connected adaptive space-trees of general element classes in parallel. - Copyright (C) 2025 the developers + 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 From 04aacc8884adc79d95534e9e143cb9f7c360b826 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 23 Mar 2026 15:48:12 +0100 Subject: [PATCH 12/12] resolve errors --- mesh_handle/competence_pack.hxx | 1 + mesh_handle/mesh_io.hxx | 2 +- tutorials/CMakeLists.txt | 8 +++++--- tutorials/general/t8_step5_mesh_handle.cxx | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mesh_handle/competence_pack.hxx b/mesh_handle/competence_pack.hxx index 285c767208..61034c23dd 100644 --- a/mesh_handle/competence_pack.hxx +++ b/mesh_handle/competence_pack.hxx @@ -50,6 +50,7 @@ using all_cache_competences = competence_pack; +/** Empty competence pack. */ using no_competences = competence_pack<>; /** Predefined competence pack combining all competences related to faces. */ diff --git a/mesh_handle/mesh_io.hxx b/mesh_handle/mesh_io.hxx index f93f0d6670..b32d1330ce 100644 --- a/mesh_handle/mesh_io.hxx +++ b/mesh_handle/mesh_io.hxx @@ -66,7 +66,7 @@ write_mesh_to_vtk_ext (TMeshClass &mesh, const char *fileprefix, const int num_d bool do_not_use_API = false) { return t8_forest_write_vtk_ext (mesh->get_forest (), fileprefix, write_treeid, write_mpirank, write_level, - write_element_id, write_ghosts, write_curved, do_not_use_API, num_data, &data); + write_element_id, write_ghosts, write_curved, do_not_use_API, num_data, data); } /** diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index c0f1d654c4..176f5b1b8d 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -32,10 +32,9 @@ function( add_t8_tutorial ) set(TUTORIAL_BUILD_DIR "${CMAKE_BINARY_DIR}/${TUTORIAL_RELATIVE_DIR}") add_executable( ${ADD_T8_TUTORIAL_NAME} ${ADD_T8_TUTORIAL_SOURCES} ) - target_link_libraries( ${ADD_T8_TUTORIAL_NAME} PRIVATE T8 SC::SC T8_MESH_HANDLE) + target_link_libraries( ${ADD_T8_TUTORIAL_NAME} PRIVATE T8 SC::SC) string( FIND ${ADD_T8_TUTORIAL_NAME} "mesh_handle" is_mesh_handle_file ) - if ( (${is_mesh_handle_file} GREATER_EQUAL 0) ) target_link_libraries(${ADD_T8_TUTORIAL_NAME} PRIVATE T8_MESH_HANDLE ) endif () @@ -66,7 +65,6 @@ add_t8_tutorial( NAME t8_step2_uniform_forest SOURCES general/t8_step2 add_t8_tutorial( NAME t8_step3_adapt_forest SOURCES general/t8_step3_main.cxx general/t8_step3_adapt_forest.cxx ) add_t8_tutorial( NAME t8_step4_partition_balance_ghost SOURCES general/t8_step4_main.cxx general/t8_step3_adapt_forest.cxx general/t8_step4_partition_balance_ghost.cxx ) add_t8_tutorial( NAME t8_step5_element_data SOURCES general/t8_step5_main.cxx general/t8_step3_adapt_forest.cxx general/t8_step5_element_data.cxx ) -add_t8_tutorial( NAME t8_step5_mesh_handle SOURCES general/t8_step5_mesh_handle.cxx ) add_t8_tutorial( NAME t8_step6_stencil SOURCES general/t8_step6_main.cxx general/t8_step3_adapt_forest.cxx general/t8_step6_stencil.cxx ) add_t8_tutorial( NAME t8_step7_interpolation SOURCES general/t8_step7_main.cxx general/t8_step7_interpolation.cxx ) add_t8_tutorial( NAME t8_tutorial_build_cmesh SOURCES general/t8_tutorial_build_cmesh.cxx general/t8_tutorial_build_cmesh_main.cxx) @@ -77,3 +75,7 @@ copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_hex.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_quad.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_tet.geo) copy_tutorial_file (features/t8_features_curved_meshes_generate_cmesh_tri.geo) + +if( T8CODE_BUILD_MESH_HANDLE ) + add_t8_tutorial( NAME t8_step5_mesh_handle SOURCES general/t8_step5_mesh_handle.cxx ) +endif() \ No newline at end of file diff --git a/tutorials/general/t8_step5_mesh_handle.cxx b/tutorials/general/t8_step5_mesh_handle.cxx index 4b66e29796..3ce0246296 100644 --- a/tutorials/general/t8_step5_mesh_handle.cxx +++ b/tutorials/general/t8_step5_mesh_handle.cxx @@ -153,7 +153,7 @@ t8_step5_output_data_to_vtu (TMeshClass &mesh, const char *prefix) } /* To write user defined data, we need the extended output function write_mesh_to_vtk_ext. * Despite writing user data, it also offers more control over which properties to write. */ - write_mesh_to_vtk_ext (mesh, prefix, num_data, &vtk_data); + t8_mesh_handle::write_mesh_to_vtk_ext (mesh, prefix, num_data, &vtk_data); T8_FREE (element_volumes); } @@ -195,7 +195,7 @@ t8_step5_main (int argc, char **argv) using mesh_class = t8_mesh_handle::mesh; auto mesh = t8_step5_build_mesh (comm, level); - mesh->write_vtk (prefix_mesh); + t8_mesh_handle::write_mesh_to_vtk (mesh, prefix_mesh); t8_global_productionf (" [step5] Wrote mesh to vtu files: %s*\n", prefix_mesh); t8_step5_set_element_data (mesh);