From 2b75de15b67f2cd6cb7d3981150cb0a7a9b87489 Mon Sep 17 00:00:00 2001 From: lenaploetzke <70579874+lenaploetzke@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:46:09 +0100 Subject: [PATCH 01/16] WIP: file for adaption --- mesh_handle/adapt.hxx | 88 +++++++++++++++++++++++++++++++++++++++++++ mesh_handle/mesh.hxx | 5 +++ 2 files changed, 93 insertions(+) create mode 100644 mesh_handle/adapt.hxx diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx new file mode 100644 index 0000000000..19d420a34e --- /dev/null +++ b/mesh_handle/adapt.hxx @@ -0,0 +1,88 @@ +/* + 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 TODO + */ + +#ifndef T8_MESH_HXX +#define T8_MESH_HXX + +#include +#include +#include "abstract_element.hxx" +#include "mesh_element.hxx" +#include "ghost_element.hxx" +#include "competence_pack.hxx" +#include +#include +#include + +namespace t8_mesh_handle +{ +template +using coarsen_mesh_element_family = bool (*) (mesh_class mesh, std::vector& family); + +template +using refine_mesh_element = bool (*) (mesh_class mesh, mesh_element& element); + +template +int +mesh_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + [[maybe_unused]] t8_eclass_t tree_class, [[maybe_unused]] t8_locidx_t lelement_id, + [[maybe_unused]] const t8_scheme* scheme, const int is_family, + [[maybe_unused]] const int num_elements, t8_element_t* elements[]) +{ + + if (refine_ciretion) { + /* Refine this element. */ + return 1; + } + else if (is_family && coarsen_crit) { + /* Coarsen this family. Note that we check for is_family before, since returning < 0 + * if we do not have a family as input is illegal. */ + return -1; + } + /* Do not change this element. */ + return 0; +} + +template +void +adapt_mesh (mesh_class& mesh_handle, + refine_mesh_element refinement_callback, + coarsen_mesh_element_family coarsen_callback, + bool recursive) +{ + auto forest_from = mesh_handle.get_forest (); + + t8_forest_t forest; + t8_forest_init (&forest); + t8_forest_set_adapt (forest, forest_from, adapt_fn, recursive); + t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); + t8_forest_set_user_data (forest, mesh_handle.get_user_data ()); + + t8_forest_commit (forest); + mesh_handle.set_forest (forest); +} + +} // namespace t8_mesh_handle +#endif /* !T8_MESH_HXX */ diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index bf7bf31911..10e6994263 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -201,6 +201,11 @@ class mesh { m_forest = input_forest; T8_ASSERT (t8_forest_is_committed (m_forest)); update_elements (); + if constexpr (!std::is_void::value) { + t8_global_infof ( + "The elements of the mesh handle has been updated. Please note that the element_data are not interpolated " + "automatically. Use the function set_element_data() to provide new adapted element data."); + } } /** From a7dbbbbe9a919c2bd0f94af5a6637e8609e7fc65 Mon Sep 17 00:00:00 2001 From: lenaploetzke <70579874+lenaploetzke@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:32:48 +0100 Subject: [PATCH 02/16] idea with singleton for adapt --- mesh_handle/adapt.hxx | 45 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index 19d420a34e..2cc52d2953 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -35,28 +35,62 @@ #include #include #include +#include namespace t8_mesh_handle { + +template +/** Singleton for callback. Problem is the mesh class template!*/ +class CallbackRegistry { + public: + using CoarsenCallBackType = std::function& family)>; + + static void + register_coarsen_callback (CoarsenCallBackType Coarsencallback) + { + get_map ()[name] = std::move (cb); + } + + static CallbackType + get_coarsen_callback () + { + auto& map = get_map (); + auto it = map.find (name); + if (it != map.end ()) { + return it->second; + } + return nullptr; + } + + private: + static std::unordered_map& + get_map () + { + static std::unordered_map map; + return map; + } +}; + template using coarsen_mesh_element_family = bool (*) (mesh_class mesh, std::vector& family); template using refine_mesh_element = bool (*) (mesh_class mesh, mesh_element& element); -template +template int -mesh_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, +mesh_adapt_callback (mesh_class mesh, t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, [[maybe_unused]] t8_eclass_t tree_class, [[maybe_unused]] t8_locidx_t lelement_id, [[maybe_unused]] const t8_scheme* scheme, const int is_family, [[maybe_unused]] const int num_elements, t8_element_t* elements[]) { - if (refine_ciretion) { + if (refine_mesh_element (mesh, mesh.get_mesh_element (lelement_id))) { /* Refine this element. */ return 1; } - else if (is_family && coarsen_crit) { + else if (is_family && coarsen_mesh_element_family (mesh, )) { /* Coarsen this family. Note that we check for is_family before, since returning < 0 * if we do not have a family as input is illegal. */ return -1; @@ -76,7 +110,8 @@ adapt_mesh (mesh_class& mesh_handle, t8_forest_t forest; t8_forest_init (&forest); - t8_forest_set_adapt (forest, forest_from, adapt_fn, recursive); + t8_forest_set_adapt (forest, forest_from, mesh_adapt_callback, + recursive); t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); t8_forest_set_user_data (forest, mesh_handle.get_user_data ()); From 2260ef33e37d5d48b9e83db6097811951fe1553f Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 17 Dec 2025 10:16:28 +0100 Subject: [PATCH 03/16] small improvements --- mesh_handle/adapt.hxx | 13 +++++-------- mesh_handle/mesh.hxx | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index 2cc52d2953..b95232641a 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -28,10 +28,7 @@ #include #include -#include "abstract_element.hxx" -#include "mesh_element.hxx" -#include "ghost_element.hxx" -#include "competence_pack.hxx" +#include "mesh.hxx" #include #include #include @@ -72,11 +69,11 @@ class CallbackRegistry { } }; -template -using coarsen_mesh_element_family = bool (*) (mesh_class mesh, std::vector& family); +template +using coarsen_mesh_element_family = bool (*) (mesh_class mesh, std::vector& family); -template -using refine_mesh_element = bool (*) (mesh_class mesh, mesh_element& element); +template +using refine_mesh_element = bool (*) (mesh_class mesh, mesh_class::mesh_element_class& element); template int diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 10e6994263..559ea2f341 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -201,9 +201,9 @@ class mesh { m_forest = input_forest; T8_ASSERT (t8_forest_is_committed (m_forest)); update_elements (); - if constexpr (!std::is_void::value) { + if constexpr (!std::is_void::value) { t8_global_infof ( - "The elements of the mesh handle has been updated. Please note that the element_data are not interpolated " + "The elements of the mesh handle have been updated. Please note that the element_data are not interpolated " "automatically. Use the function set_element_data() to provide new adapted element data."); } } From 8395593ca3a7cbaffdd256fb13cb452e179ab6bd Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Wed, 17 Dec 2025 15:37:21 +0100 Subject: [PATCH 04/16] First working state of the adapt routine with registry and context --- mesh_handle/adapt.hxx | 149 +++++++++++++++------- mesh_handle/mesh.hxx | 4 +- test/CMakeLists.txt | 1 + test/mesh_handle/t8_gtest_adapt.cxx | 135 ++++++++++++++++++++ test/mesh_handle/t8_gtest_handle_data.cxx | 6 +- 5 files changed, 243 insertions(+), 52 deletions(-) create mode 100644 test/mesh_handle/t8_gtest_adapt.cxx diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index b95232641a..ba41507a5d 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -20,11 +20,11 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/** \file TODO +/** \file adapt.hxx */ -#ifndef T8_MESH_HXX -#define T8_MESH_HXX +#ifndef T8_ADAPT_HXX +#define T8_ADAPT_HXX #include #include @@ -37,84 +37,137 @@ namespace t8_mesh_handle { -template +struct MeshAdaptContextBase +{ + virtual ~MeshAdaptContextBase () = default; + + virtual int + adapt_callback (t8_locidx_t lelement_id, int is_family, int num_elements, t8_element_t* elements[]) + = 0; +}; + +template +using coarsen_mesh_element_family + = std::function&)>; + +template +using refine_mesh_element = std::function; + +template +struct MeshAdaptContext final: MeshAdaptContextBase +{ + using Element = typename TMesh::mesh_element_class; + + MeshAdaptContext (TMesh& m, refine_mesh_element r, coarsen_mesh_element_family c) + : mesh (m), refine (std::move (r)), coarsen (std::move (c)) + { + } + + int + adapt_callback (t8_locidx_t lelement_id, int is_family, int num_elements, t8_element_t* elements[]) override + { + // refine + if (refine) { + Element elem = mesh.get_mesh_element (lelement_id); + if (refine (mesh, elem)) { + return 1; + } + } + + // coarsen + if (is_family && coarsen) { + std::vector element_family; + for (int i = 0; i < num_elements; i++) { + element_family.push_back (mesh.get_mesh_element (lelement_id + i)); + } + if (coarsen (mesh, element_family)) { + return -1; + } + } + + return 0; + } + + private: + TMesh& mesh; + refine_mesh_element refine; + coarsen_mesh_element_family coarsen; +}; + /** Singleton for callback. Problem is the mesh class template!*/ -class CallbackRegistry { +class AdaptRegistry { public: - using CoarsenCallBackType = std::function& family)>; + static void + register_context (t8_forest_t forest, MeshAdaptContextBase* context) + { + get_map ()[forest] = context; + } static void - register_coarsen_callback (CoarsenCallBackType Coarsencallback) + unregister_context (t8_forest_t forest) { - get_map ()[name] = std::move (cb); + get_map ().erase (forest); } - static CallbackType - get_coarsen_callback () + static MeshAdaptContextBase* + get (t8_forest_t forest) { auto& map = get_map (); - auto it = map.find (name); - if (it != map.end ()) { - return it->second; - } - return nullptr; + auto it = map.find (forest); + return it != map.end () ? it->second : nullptr; } private: - static std::unordered_map& + // Use getter instead of private member variable to ensure single initialization + static std::unordered_map& get_map () { - static std::unordered_map map; + static std::unordered_map map; return map; } }; -template -using coarsen_mesh_element_family = bool (*) (mesh_class mesh, std::vector& family); - -template -using refine_mesh_element = bool (*) (mesh_class mesh, mesh_class::mesh_element_class& element); - -template int -mesh_adapt_callback (mesh_class mesh, t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, - [[maybe_unused]] t8_eclass_t tree_class, [[maybe_unused]] t8_locidx_t lelement_id, - [[maybe_unused]] const t8_scheme* scheme, const int is_family, - [[maybe_unused]] const int num_elements, t8_element_t* elements[]) +mesh_adapt_callback ([[maybe_unused]] t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + [[maybe_unused]] t8_eclass_t tree_class, t8_locidx_t lelement_id, + [[maybe_unused]] const t8_scheme* scheme, const int is_family, const int num_elements, + t8_element_t* elements[]) { - - if (refine_mesh_element (mesh, mesh.get_mesh_element (lelement_id))) { - /* Refine this element. */ - return 1; - } - else if (is_family && coarsen_mesh_element_family (mesh, )) { - /* Coarsen this family. Note that we check for is_family before, since returning < 0 - * if we do not have a family as input is illegal. */ - return -1; + auto* context = AdaptRegistry::get (forest_from); + if (!context) { + t8_global_infof ( + "Something went wrong while registering the adaption callbacks. Please check your implementation."); + return 0; // No adaption as default. } - /* Do not change this element. */ - return 0; + t8_locidx_t local_flat_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; + // TODO: adapt names in other callbacks to not get confused between flat and this element_in_tree_index. + return context->adapt_callback (local_flat_index, is_family, num_elements, elements); } -template +template void -adapt_mesh (mesh_class& mesh_handle, - refine_mesh_element refinement_callback, - coarsen_mesh_element_family coarsen_callback, - bool recursive) +adapt_mesh (TMesh& mesh_handle, refine_mesh_element refine_callback, + coarsen_mesh_element_family coarsen_callback, bool recursive) { auto forest_from = mesh_handle.get_forest (); t8_forest_t forest; t8_forest_init (&forest); - t8_forest_set_adapt (forest, forest_from, mesh_adapt_callback, - recursive); + + auto* context = new MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); + + AdaptRegistry::register_context (forest_from, context); + + t8_forest_set_adapt (forest, forest_from, mesh_adapt_callback, recursive); t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); - t8_forest_set_user_data (forest, mesh_handle.get_user_data ()); + t8_forest_set_user_data (forest, t8_forest_get_user_data (forest_from)); t8_forest_commit (forest); mesh_handle.set_forest (forest); + + AdaptRegistry::unregister_context (forest_from); + delete context; } } // namespace t8_mesh_handle -#endif /* !T8_MESH_HXX */ +#endif /* !T8_ADAPT_HXX */ diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 559ea2f341..bc8aa128c9 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -53,6 +53,8 @@ template , typename TUserData = voi class mesh { public: using SelfType = mesh; + using UserDataType = TUserData; + using ElementDataType = TUserData; /** Type definitions of the element classes with given competences. */ using abstract_element_class = TCompetencePack::template apply< SelfType, abstract_element>; /**< The abstract element class of the mesh (could be a mesh element of ghost). */ @@ -204,7 +206,7 @@ class mesh { if constexpr (!std::is_void::value) { t8_global_infof ( "The elements of the mesh handle have been updated. Please note that the element_data are not interpolated " - "automatically. Use the function set_element_data() to provide new adapted element data."); + "automatically. Use the function set_element_data() to provide new adapted element data.\n"); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6b6ff0e5a2..e1e2422489 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -215,6 +215,7 @@ if( T8CODE_BUILD_MESH_HANDLE ) 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 ) endif() copy_test_file( test_cube_unstructured_1.inp ) diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx new file mode 100644 index 0000000000..c0de8be147 --- /dev/null +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -0,0 +1,135 @@ +/* +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 t8_gtest_adapt.cxx + * TODO + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Dummy user data taken from a tutorial for test purposes. +struct dummy_user_data +{ + t8_3D_point 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 +bool +refine_mesh_element_test (TMeshClass mesh, typename TMeshClass::mesh_element_class &element) +{ + typename TMeshClass::UserDataType user_data = mesh.get_user_data (); + auto element_centroid = element.get_centroid (); + + /* Compute the distance to our sphere midpoint. */ + double dist = t8_dist (element_centroid, user_data.midpoint); + return (dist < user_data.refine_if_inside_radius); +} + +template +bool +coarsen_mesh_element_family_test (TMeshClass mesh, std::vector &elements) +{ + typename TMeshClass::UserDataType user_data = mesh.get_user_data (); + auto element_centroid = elements[0].get_centroid (); + + /* Compute the distance to our sphere midpoint. */ + double dist = t8_dist (element_centroid, user_data.midpoint); + return (dist > user_data.coarsen_if_outside_radius); +} + +int +t8_step3_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + [[maybe_unused]] t8_eclass_t tree_class, [[maybe_unused]] t8_locidx_t lelement_id, + [[maybe_unused]] const t8_scheme *scheme, const int is_family, + [[maybe_unused]] const int num_elements, t8_element_t *elements[]) +{ + const struct dummy_user_data *adapt_data = (const struct dummy_user_data *) t8_forest_get_user_data (forest); + + /* Compute the element's centroid coordinates. */ + t8_3D_point centroid; + t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); + + /* Compute the distance to our sphere midpoint. */ + double dist = t8_dist (centroid, adapt_data->midpoint); + if (dist < adapt_data->refine_if_inside_radius) { + /* Refine this element. */ + return 1; + } + else if (is_family && dist > adapt_data->coarsen_if_outside_radius) { + /* Coarsen this family. Note that we check for is_family before, since returning < 0 + * if we do not have a family as input is illegal. */ + return -1; + } + /* Do not change this element. */ + return 0; +} + +/** TODO + */ +TEST (t8_gtest_handle_adapt, define_adapt) +{ + // Define forest and mesh handle. + const int level = 3; + t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); + const t8_scheme *init_scheme = t8_scheme_new_default (); + t8_forest_t forest = t8_forest_new_uniform (cmesh, init_scheme, level, 0, sc_MPI_COMM_WORLD); + using mesh_class = t8_mesh_handle::mesh, dummy_user_data>; + mesh_class mesh_handle = mesh_class (forest); + + struct dummy_user_data user_data = { + t8_3D_point ({ 0.5, 0.5, 1 }), /* Midpoints of the sphere. */ + 0.2, /* Refine if inside this radius. */ + 0.4 /* Coarsen if outside this radius. */ + }; + mesh_handle.set_user_data (&user_data); + + t8_mesh_handle::adapt_mesh (mesh_handle, refine_mesh_element_test, + coarsen_mesh_element_family_test, false); + + t8_cmesh_t cmesh_compare = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); + const t8_scheme *init_scheme_compare = init_scheme; + init_scheme->ref (); + t8_forest_t forest_compare = t8_forest_new_uniform (cmesh_compare, init_scheme_compare, level, 0, sc_MPI_COMM_WORLD); + t8_forest_t forest_adapt; + forest_adapt = t8_forest_new_adapt (forest_compare, t8_step3_adapt_callback, 0, 1, &user_data); + + EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest_adapt)); + + // Unref the forests. + t8_forest_unref (&forest_adapt); + forest = mesh_handle.get_forest (); + t8_forest_unref (&forest); +} diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 196d28a38a..9af6fe9972 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -41,9 +41,9 @@ along with t8code; if not, write to the Free Software Foundation, Inc., // Dummy user data taken from a tutorial for test purposes. struct dummy_user_data { - t8_3D_point 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. */ + t8_3D_point 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. */ }; /** Check that user data can be set and accesses for the handle. From c7e0019df098e389bf7e2c65738932a04b6f4c6e Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Thu, 18 Dec 2025 11:04:25 +0100 Subject: [PATCH 05/16] missing part of merge --- mesh_handle/mesh.hxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index f2adf3a41c..44c3754d00 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -52,8 +52,9 @@ namespace t8_mesh_handle template , typename TUserData = void, typename TElementData = void> class mesh { public: - using SelfType = mesh; /**< Type of the current class with all template parameters specified. */ - using UserDataType = TUserData; /**< Make Type of the user data accessible. */ + using SelfType = mesh; /**< Type of the current class with all template parameters specified. */ + using UserDataType = TUserData; /**< Make Type of the user data accessible. */ using ElementDataType = TElementData; /**< Make Type of the element data accessible. */ /** Type definitions of the element classes with given competences. */ From 5800565a6878b9d4918c50cd4deb765cb71d7b8c Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 19 Dec 2025 10:15:48 +0100 Subject: [PATCH 06/16] add namespace detail --- mesh_handle/adapt.hxx | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index ba41507a5d..d466d916f4 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -37,6 +37,17 @@ namespace t8_mesh_handle { +/** TODO*/ +template +using coarsen_mesh_element_family + = std::function&)>; + +template +using refine_mesh_element = std::function; + +namespace detail +{ + struct MeshAdaptContextBase { virtual ~MeshAdaptContextBase () = default; @@ -46,13 +57,6 @@ struct MeshAdaptContextBase = 0; }; -template -using coarsen_mesh_element_family - = std::function&)>; - -template -using refine_mesh_element = std::function; - template struct MeshAdaptContext final: MeshAdaptContextBase { @@ -143,7 +147,25 @@ mesh_adapt_callback ([[maybe_unused]] t8_forest_t forest, t8_forest_t forest_fro // TODO: adapt names in other callbacks to not get confused between flat and this element_in_tree_index. return context->adapt_callback (local_flat_index, is_family, num_elements, elements); } - +} // namespace detail + +/** TODO Adapt a mesh handle according to the provided callbacks. + * By default, the forest takes ownership of the source \b set_from such that it + * will be destroyed on calling \ref t8_forest_commit. To keep ownership of \b + * set_from, call \ref t8_forest_ref before passing it into this function. + * This means that it is ILLEGAL to continue using \b set_from or dereferencing it + * UNLESS it is referenced directly before passing it into this function. + * \param [in,out] forest The forest + * \param [in] set_from The source forest from which \b forest will be adapted. + * We take ownership. This can be prevented by + * referencing \b set_from. + * If NULL, a previously (or later) set forest will + * be taken (\ref t8_forest_set_partition, \ref t8_forest_set_balance). + * \param [in] adapt_fn The adapt function used on committing. + * \param [in] recursive A flag specifying whether adaptation is to be done recursively + * or not. If the value is zero, adaptation is not recursive + * and it is recursive otherwise. + */ template void adapt_mesh (TMesh& mesh_handle, refine_mesh_element refine_callback, @@ -154,18 +176,19 @@ adapt_mesh (TMesh& mesh_handle, refine_mesh_element refine_callback, t8_forest_t forest; t8_forest_init (&forest); - auto* context = new MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); + auto* context + = new detail::MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); - AdaptRegistry::register_context (forest_from, context); + detail::AdaptRegistry::register_context (forest_from, context); - t8_forest_set_adapt (forest, forest_from, mesh_adapt_callback, recursive); + t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback, recursive); t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); t8_forest_set_user_data (forest, t8_forest_get_user_data (forest_from)); t8_forest_commit (forest); mesh_handle.set_forest (forest); - AdaptRegistry::unregister_context (forest_from); + detail::AdaptRegistry::unregister_context (forest_from); delete context; } From 1d4891e7d4852f35909b4ef238eaac59d46b82d0 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 19 Dec 2025 10:16:32 +0100 Subject: [PATCH 07/16] Add destructor to mesh handle --- mesh_handle/mesh.hxx | 12 +++++++++++- test/mesh_handle/t8_gtest_cache_competence.cxx | 4 +++- .../t8_gtest_compare_handle_to_forest.cxx | 3 --- test/mesh_handle/t8_gtest_custom_competence.cxx | 4 +--- test/mesh_handle/t8_gtest_ghost.cxx | 4 +++- test/mesh_handle/t8_gtest_handle_data.cxx | 6 ------ test/mesh_handle/t8_gtest_mesh_handle.cxx | 5 ++++- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 44c3754d00..e650c08f17 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -84,6 +84,16 @@ class mesh { update_elements (); } + /** + * Destructor for a mesh of the handle. + * The forest in use will be unreferenced. + * Call \ref t8_forest_ref before if you want to keep it alive. + */ + ~mesh () + { + t8_forest_unref (&m_forest); + } + /** * Getter for the number of local elements in the mesh. * \return Number of local elements in the mesh. @@ -310,7 +320,7 @@ class mesh { } } - t8_forest_t m_forest; /**< The forest the mesh should be defined for. */ + t8_forest_t m_forest = nullptr; /**< The forest the mesh should be defined for. */ std::vector m_elements; /**< Vector storing the (local) mesh elements. */ std::vector m_ghosts; /**< Vector storing the (local) ghost elements. */ std::conditional_t, std::vector, std::nullptr_t> diff --git a/test/mesh_handle/t8_gtest_cache_competence.cxx b/test/mesh_handle/t8_gtest_cache_competence.cxx index eb4fe57bb4..5c881fae64 100644 --- a/test/mesh_handle/t8_gtest_cache_competence.cxx +++ b/test/mesh_handle/t8_gtest_cache_competence.cxx @@ -98,7 +98,9 @@ class t8_gtest_cache_competence: public testing::Test { void TearDown () override { - t8_forest_unref (&forest); + if (forest->rc.refcount > 0) { + t8_forest_unref (&forest); + } } t8_forest_t forest; 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 b8ff456865..8265af1e73 100644 --- a/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx +++ b/test/mesh_handle/t8_gtest_compare_handle_to_forest.cxx @@ -78,7 +78,4 @@ TEST (t8_gtest_compare_handle_to_forest, compare_handle_to_forest) mesh_iterator++; } } - - // Unref the forest. - t8_forest_unref (&forest); } diff --git a/test/mesh_handle/t8_gtest_custom_competence.cxx b/test/mesh_handle/t8_gtest_custom_competence.cxx index 96a1b3a72e..788b35ab74 100644 --- a/test/mesh_handle/t8_gtest_custom_competence.cxx +++ b/test/mesh_handle/t8_gtest_custom_competence.cxx @@ -98,6 +98,7 @@ TEST (t8_gtest_custom_competence, custom_competence) EXPECT_EQ (level, it->get_level_dummy ()); } + t8_forest_ref (forest); // Test with two custom competences and a predefined competence. using competences = t8_mesh_handle::competence_pack; using mesh_class = t8_mesh_handle::mesh; @@ -114,7 +115,4 @@ TEST (t8_gtest_custom_competence, custom_competence) } EXPECT_TRUE (it->centroid_cache_filled ()); } - - // Unref the forest. - t8_forest_unref (&forest); } diff --git a/test/mesh_handle/t8_gtest_ghost.cxx b/test/mesh_handle/t8_gtest_ghost.cxx index 5270779f06..43a639b10a 100644 --- a/test/mesh_handle/t8_gtest_ghost.cxx +++ b/test/mesh_handle/t8_gtest_ghost.cxx @@ -55,7 +55,9 @@ class t8_mesh_ghost_test: public testing::TestWithParamrc.refcount > 0) { + t8_forest_unref (&forest); + } } t8_forest_t forest; int level; diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 9af6fe9972..dd13673775 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -71,9 +71,6 @@ TEST (t8_gtest_handle_data, set_and_get_user_data) EXPECT_EQ (mesh_user_data.midpoint, user_data.midpoint); EXPECT_EQ (mesh_user_data.refine_if_inside_radius, user_data.refine_if_inside_radius); EXPECT_EQ (mesh_user_data.coarsen_if_outside_radius, user_data.coarsen_if_outside_radius); - - // Unref the forest. - t8_forest_unref (&forest); } // --- Test for element data. --- @@ -113,7 +110,4 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) EXPECT_EQ (mesh_element_data[ielem].level, level) << "ielem = " << ielem; EXPECT_EQ (mesh_element_data[ielem].volume, mesh[ielem].get_volume ()) << "ielem = " << ielem; } - - // Unref the forest. - t8_forest_unref (&forest); } diff --git a/test/mesh_handle/t8_gtest_mesh_handle.cxx b/test/mesh_handle/t8_gtest_mesh_handle.cxx index 7b39beac29..989555727f 100644 --- a/test/mesh_handle/t8_gtest_mesh_handle.cxx +++ b/test/mesh_handle/t8_gtest_mesh_handle.cxx @@ -52,7 +52,9 @@ class t8_mesh_handle_test: public testing::TestWithParamrc.refcount > 0) { + t8_forest_unref (&forest); + } } t8_forest_t forest; @@ -135,6 +137,7 @@ TEST_P (t8_mesh_handle_test, test_competences) } // --- Version with cached centroid variable. --- + t8_forest_ref (forest); using competence_centroid = t8_mesh_handle::competence_pack; using mesh_class_centroid = t8_mesh_handle::mesh; using element_class_centroid = typename mesh_class_centroid::abstract_element_class; From 9ca199473e5c733535b5a12896f3ecb460baf936 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 19 Dec 2025 10:30:43 +0100 Subject: [PATCH 08/16] pass mesh by const ref in callbacks --- mesh_handle/adapt.hxx | 4 ++-- test/mesh_handle/t8_gtest_adapt.cxx | 19 ++++++------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index d466d916f4..838974eb76 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -40,10 +40,10 @@ namespace t8_mesh_handle /** TODO*/ template using coarsen_mesh_element_family - = std::function&)>; + = std::function&)>; template -using refine_mesh_element = std::function; +using refine_mesh_element = std::function; namespace detail { diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index c0de8be147..ba723764c1 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -48,7 +48,7 @@ struct dummy_user_data template bool -refine_mesh_element_test (TMeshClass mesh, typename TMeshClass::mesh_element_class &element) +refine_mesh_element_test (const TMeshClass &mesh, const typename TMeshClass::mesh_element_class &element) { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = element.get_centroid (); @@ -60,7 +60,8 @@ refine_mesh_element_test (TMeshClass mesh, typename TMeshClass::mesh_element_cla template bool -coarsen_mesh_element_family_test (TMeshClass mesh, std::vector &elements) +coarsen_mesh_element_family_test (const TMeshClass &mesh, + const std::vector &elements) { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = elements[0].get_centroid (); @@ -116,20 +117,12 @@ TEST (t8_gtest_handle_adapt, define_adapt) }; mesh_handle.set_user_data (&user_data); + t8_forest_ref (forest); t8_mesh_handle::adapt_mesh (mesh_handle, refine_mesh_element_test, coarsen_mesh_element_family_test, false); - t8_cmesh_t cmesh_compare = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); - const t8_scheme *init_scheme_compare = init_scheme; - init_scheme->ref (); - t8_forest_t forest_compare = t8_forest_new_uniform (cmesh_compare, init_scheme_compare, level, 0, sc_MPI_COMM_WORLD); - t8_forest_t forest_adapt; - forest_adapt = t8_forest_new_adapt (forest_compare, t8_step3_adapt_callback, 0, 1, &user_data); + forest = t8_forest_new_adapt (forest, t8_step3_adapt_callback, 0, 1, &user_data); - EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest_adapt)); - - // Unref the forests. - t8_forest_unref (&forest_adapt); - forest = mesh_handle.get_forest (); + EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest)); t8_forest_unref (&forest); } From cb309296c82b26855e3eb7c72f8758703718c32f Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 19 Dec 2025 11:11:39 +0100 Subject: [PATCH 09/16] Document adapt test --- test/mesh_handle/t8_gtest_adapt.cxx | 59 ++++++++++++++--------- test/mesh_handle/t8_gtest_handle_data.cxx | 2 +- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index ba723764c1..4ee15d482b 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -22,7 +22,10 @@ along with t8code; if not, write to the Free Software Foundation, Inc., /** * \file t8_gtest_adapt.cxx - * TODO + * Tests for the adapt routines of mesh handles. + * This tests uses the callbacks 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. */ #include @@ -38,7 +41,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include #include -// Dummy user data taken from a tutorial for test purposes. +/** Dummy user data taken from tutorial for test purposes. */ struct dummy_user_data { t8_3D_point midpoint; /**< The midpoint of our sphere. */ @@ -46,18 +49,28 @@ struct dummy_user_data double coarsen_if_outside_radius; /**< if an element's center is larger this value, we coarsen its family. */ }; +/** Callback function implementation to decide for the refinement of an element of a mesh handle. + * \param [in] mesh The mesh that should be adapted. + * \param [in] element The element to consider for refinement. + * \tparam TMeshClass The mesh handle class. + * \return true if the element should be refined, false otherwise. + */ template bool refine_mesh_element_test (const TMeshClass &mesh, const typename TMeshClass::mesh_element_class &element) { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = element.get_centroid (); - - /* Compute the distance to our sphere midpoint. */ double dist = t8_dist (element_centroid, user_data.midpoint); return (dist < user_data.refine_if_inside_radius); } +/** Callback function implementation to decide for coarsening. + * \param [in] mesh The mesh that should be adapted. + * \param [in] elements The element family considered to be coarsened. + * \tparam TMeshClass The mesh handle class. + * \return true if the family should be coarsened, false otherwise. + */ template bool coarsen_mesh_element_family_test (const TMeshClass &mesh, @@ -65,51 +78,49 @@ coarsen_mesh_element_family_test (const TMeshClass &mesh, { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = elements[0].get_centroid (); - - /* Compute the distance to our sphere midpoint. */ double dist = t8_dist (element_centroid, user_data.midpoint); return (dist > user_data.coarsen_if_outside_radius); } +/** Adapt callback implementation for a forest. + * This callback defines the same adaptation rules as \ref coarsen_mesh_element_family_test + * together with \ref refine_mesh_element_test. Callback is not used for the mesh handle + * but for the forest compared to the adapted mesh handle. + */ int -t8_step3_adapt_callback (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, - [[maybe_unused]] t8_eclass_t tree_class, [[maybe_unused]] t8_locidx_t lelement_id, - [[maybe_unused]] const t8_scheme *scheme, const int is_family, - [[maybe_unused]] const int num_elements, t8_element_t *elements[]) +forest_adapt_callback_example (t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + [[maybe_unused]] t8_eclass_t tree_class, [[maybe_unused]] t8_locidx_t lelement_id, + [[maybe_unused]] const t8_scheme *scheme, const int is_family, + [[maybe_unused]] const int num_elements, t8_element_t *elements[]) { const struct dummy_user_data *adapt_data = (const struct dummy_user_data *) t8_forest_get_user_data (forest); - - /* Compute the element's centroid coordinates. */ t8_3D_point centroid; t8_forest_element_centroid (forest_from, which_tree, elements[0], centroid.data ()); - - /* Compute the distance to our sphere midpoint. */ double dist = t8_dist (centroid, adapt_data->midpoint); if (dist < adapt_data->refine_if_inside_radius) { /* Refine this element. */ return 1; } else if (is_family && dist > adapt_data->coarsen_if_outside_radius) { - /* Coarsen this family. Note that we check for is_family before, since returning < 0 - * if we do not have a family as input is illegal. */ + /* Coarsen this family. */ return -1; } /* Do not change this element. */ return 0; } -/** TODO +/** Test the adapt routine of a mesh handle. + * We compare the result to a classically adapted forest with similar callback. */ -TEST (t8_gtest_handle_adapt, define_adapt) +TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) { - // Define forest and mesh handle. + // Define forest, a mesh handle and user data. const int level = 3; t8_cmesh_t cmesh = t8_cmesh_new_hypercube_hybrid (sc_MPI_COMM_WORLD, 0, 0); const t8_scheme *init_scheme = t8_scheme_new_default (); t8_forest_t forest = t8_forest_new_uniform (cmesh, init_scheme, level, 0, sc_MPI_COMM_WORLD); using mesh_class = t8_mesh_handle::mesh, dummy_user_data>; mesh_class mesh_handle = mesh_class (forest); - struct dummy_user_data user_data = { t8_3D_point ({ 0.5, 0.5, 1 }), /* Midpoints of the sphere. */ 0.2, /* Refine if inside this radius. */ @@ -117,12 +128,16 @@ TEST (t8_gtest_handle_adapt, define_adapt) }; mesh_handle.set_user_data (&user_data); + // Ref the forest as we want to keep using it after the adapt call to compare results. t8_forest_ref (forest); + + // Adapt mesh handle and the forest with similar callbacks. t8_mesh_handle::adapt_mesh (mesh_handle, refine_mesh_element_test, coarsen_mesh_element_family_test, false); + forest = t8_forest_new_adapt (forest, forest_adapt_callback_example, 0, 1, &user_data); - forest = t8_forest_new_adapt (forest, t8_step3_adapt_callback, 0, 1, &user_data); - + // Compare results. EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest)); + // Clean up. t8_forest_unref (&forest); } diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index dd13673775..53adab5eac 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -38,7 +38,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., #include // --- Test for user data. --- -// Dummy user data taken from a tutorial for test purposes. +/** Dummy user data taken from a tutorial for test purposes. */ struct dummy_user_data { t8_3D_point midpoint; /**< The midpoint of our sphere. */ From da3225e08ac5f0848c66cd2e84361b32e87fc38d Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 22 Dec 2025 11:15:40 +0100 Subject: [PATCH 10/16] documentation of adapt routine --- mesh_handle/adapt.hxx | 208 +++++++++++++++++++++++++++++------------- 1 file changed, 147 insertions(+), 61 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index 838974eb76..ab9d89d23e 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -21,6 +21,8 @@ */ /** \file adapt.hxx + * This file provides the functionality to adapt a \ref t8_mesh_handle::mesh + * according to user defined callbacks. */ #ifndef T8_ADAPT_HXX @@ -29,142 +31,225 @@ #include #include #include "mesh.hxx" -#include #include -#include #include namespace t8_mesh_handle { - -/** TODO*/ +/** Callback function prototype to decide for coarsening of a family of elements in a mesh handle. + * This callback works with a family of elements, i.e., multiple elements that can be coarsened + * into one parent element. + * \param [in] mesh The mesh that should be adapted. + * \param [in] elements The element family considered to be coarsened. + * \tparam TMeshClass The mesh handle class. + * \return true if the family should be coarsened, false otherwise. + */ template using coarsen_mesh_element_family - = std::function&)>; + = std::function& elements)>; +/** Callback function prototype to decide for the refinement of an element of a mesh handle. + * \param [in] mesh The mesh that should be adapted. + * \param [in] element The element to consider for refinement. + * \tparam TMeshClass The mesh handle class. + * \return true if the element should be refined, false otherwise. + */ template -using refine_mesh_element = std::function; +using refine_mesh_element + = std::function; +/** Namespace detail to hide implementation details from the user. */ namespace detail { +/** Virtual base class for mesh adaptation contexts. + * We need this base class and not only \ref MeshAdaptContext for the \ref AdaptRegistry. + * AdaptRegistry should not be templated because we need to access registered contexts in \ref mesh_adapt_callback_wrapper, + * where we do not know the type of the mesh. Therefore, we work with a map of forests to instances of this base class to remain template free. + */ struct MeshAdaptContextBase { + /** Virtual destructor for safe polymorphic deletion. + */ virtual ~MeshAdaptContextBase () = default; + /** Pure virtual callback for mesh adaptation. + * \param[in] lflat_element_id Local flat element ID in the mesh handle. + * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. + * \param [in] num_elements The number of entries in \a elements. + * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \return 1 if the first entry in \a elements should be refined, + * -1 if the family \a elements shall be coarsened, + * 0 else. + */ virtual int - adapt_callback (t8_locidx_t lelement_id, int is_family, int num_elements, t8_element_t* elements[]) + adapt_callback (const t8_locidx_t lflat_element_id, const int is_family, const int num_elements, + t8_element_t* elements[]) = 0; }; +/** Mesh adaptation context holding the mesh handle and the user defined callbacks. + * Class inherits from \ref MeshAdaptContextBase and implements the virtual adapt callback using the mesh and the callbacks. + * \tparam TMesh The mesh handle class. + */ template struct MeshAdaptContext final: MeshAdaptContextBase { - using Element = typename TMesh::mesh_element_class; - - MeshAdaptContext (TMesh& m, refine_mesh_element r, coarsen_mesh_element_family c) - : mesh (m), refine (std::move (r)), coarsen (std::move (c)) + using Element = typename TMesh::mesh_element_class; /**< Type alias for the mesh element class. */ + + /** Constructor of the context with the mesh handle and the user defined callbacks. + * \param [in] mesh_handle The mesh handle to adapt. + * \param [in] refine_callback The refinement callback. + * \param [in] coarsen_callback The coarsening callback. + */ + MeshAdaptContext (TMesh& mesh_handle, refine_mesh_element refine_callback, + coarsen_mesh_element_family coarsen_callback) + : m_mesh_handle (mesh_handle), m_refine_callback (std::move (refine_callback)), + m_coarsen_callback (std::move (coarsen_callback)) { } + /** Callback for mesh adaptation using user defined callbacks. + * \param [in] lflat_element_id Local flat element ID in the mesh handle. + * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. + * \param [in] num_elements The number of entries in \a elements. + * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \return 1 if the first entry in \a elements should be refined, + * -1 if the family \a elements shall be coarsened, + * 0 else. + */ int - adapt_callback (t8_locidx_t lelement_id, int is_family, int num_elements, t8_element_t* elements[]) override + adapt_callback (const t8_locidx_t lflat_element_id, const int is_family, const int num_elements, + t8_element_t* elements[]) override { - // refine - if (refine) { - Element elem = mesh.get_mesh_element (lelement_id); - if (refine (mesh, elem)) { + // Check if refine callback is set and call it using the correct mesh handle function arguments. + if (m_refine_callback) { + Element elem = m_mesh_handle.get_mesh_element (lflat_element_id); + if (m_refine_callback (m_mesh_handle, elem)) { return 1; } } - // coarsen - if (is_family && coarsen) { + // Check if a family is provided for adaption, if coarsen callback is set and call it using the correct mesh handle function arguments. + if (is_family && m_coarsen_callback) { std::vector element_family; for (int i = 0; i < num_elements; i++) { - element_family.push_back (mesh.get_mesh_element (lelement_id + i)); + element_family.push_back (m_mesh_handle.get_mesh_element (lflat_element_id + i)); } - if (coarsen (mesh, element_family)) { + if (m_coarsen_callback (m_mesh_handle, element_family)) { return -1; } } - + // Do nothing if the callbacks no refinement or coarsening should be done according to the callbacks. return 0; } private: - TMesh& mesh; - refine_mesh_element refine; - coarsen_mesh_element_family coarsen; + TMesh& m_mesh_handle; /**< The mesh handle to adapt. */ + refine_mesh_element m_refine_callback; /**< The refinement callback. */ + coarsen_mesh_element_family m_coarsen_callback; /**< The coarsening callback. */ }; -/** Singleton for callback. Problem is the mesh class template!*/ +/** Registry pattern is used to register contexts, which provides access to the callbacks and the mesh handle. + * This globally accessible static class is required to get the handle and the callbacks in the forest callback, + * as the predefined header permits to give these as function arguments. + */ 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. + * \param [in] context The context to register. + */ static void - register_context (t8_forest_t forest, MeshAdaptContextBase* context) + register_context (t8_forest_t forest, MeshAdaptContextBase& context) { - get_map ()[forest] = context; + auto& map = get_map (); + auto [it, inserted] = map.emplace (forest, context); + if (!inserted) { + throw std::logic_error ("Context already registered"); + } } + /** Static function to unregister a context using \a forest as identifier. + * \param [in] forest The forest identifier. + */ static void unregister_context (t8_forest_t forest) { - get_map ().erase (forest); + auto& map = get_map (); + const auto erased = map.erase (forest); + T8_ASSERT (erased == 1); } + /** Getter for a context using \a forest as identifier. + * \param [in] forest The forest identifier. + * \return Pointer to the context registered with the id \a forest if found, nullptr otherwise. + */ static MeshAdaptContextBase* get (t8_forest_t forest) { auto& map = get_map (); auto it = map.find (forest); - return it != map.end () ? it->second : nullptr; + return it != map.end () ? &it->second.get () : nullptr; } private: - // Use getter instead of private member variable to ensure single initialization - static std::unordered_map& + /** Get the static map associating t8_forest_t with MeshAdaptContextBase references. + * We use a getter instead of private member variable to ensure single initialization + * \return Reference to the static unordered map of t8_forest_t to MeshAdaptContextBase references. + */ + static std::unordered_map>& get_map () { - static std::unordered_map map; + static std::unordered_map> map; return map; } }; +/** Wrapper around the mesh handle adapt functionality to be able to pass the callbacks to the classic adapt routine of a forest. + * The function header fits the definition of \ref t8_forest_adapt_t. + * \param [in] forest_from Forest that is adapted. + * \param [in] which_tree Local tree containing \a elements. + * \param [in] lelement_id The local element id in the tree of the first element in elements. + * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. + * \param [in] num_elements The number of entries in \a elements. + * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \see t8_forest_set_adapt for the definition of unused parameters. + * \return 1 if the first entry in \a elements should be refined, + * -1 if the family \a elements shall be coarsened, + * 0 else. + */ int -mesh_adapt_callback ([[maybe_unused]] t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, - [[maybe_unused]] t8_eclass_t tree_class, t8_locidx_t lelement_id, - [[maybe_unused]] const t8_scheme* scheme, const int is_family, const int num_elements, - t8_element_t* elements[]) +mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t forest_from, t8_locidx_t which_tree, + [[maybe_unused]] t8_eclass_t tree_class, t8_locidx_t lelement_id, + [[maybe_unused]] const t8_scheme* scheme, const int is_family, const int num_elements, + t8_element_t* elements[]) { + // Get static adapt context from the registry. + // Via this, we can access the mesh handle and the user defined callbacks that are using mesh handle functionality. auto* context = AdaptRegistry::get (forest_from); if (!context) { t8_global_infof ( "Something went wrong while registering the adaption callbacks. Please check your implementation."); return 0; // No adaption as default. } + // Flat index is used in mesh handle to access elements. t8_locidx_t local_flat_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; - // TODO: adapt names in other callbacks to not get confused between flat and this element_in_tree_index. + // Call the actual adapt callback stored in the context. return context->adapt_callback (local_flat_index, is_family, num_elements, elements); } + } // namespace detail -/** TODO Adapt a mesh handle according to the provided callbacks. - * By default, the forest takes ownership of the source \b set_from such that it - * will be destroyed on calling \ref t8_forest_commit. To keep ownership of \b - * set_from, call \ref t8_forest_ref before passing it into this function. - * This means that it is ILLEGAL to continue using \b set_from or dereferencing it - * UNLESS it is referenced directly before passing it into this function. - * \param [in,out] forest The forest - * \param [in] set_from The source forest from which \b forest will be adapted. - * We take ownership. This can be prevented by - * referencing \b set_from. - * If NULL, a previously (or later) set forest will - * be taken (\ref t8_forest_set_partition, \ref t8_forest_set_balance). - * \param [in] adapt_fn The adapt function used on committing. - * \param [in] recursive A flag specifying whether adaptation is to be done recursively - * or not. If the value is zero, adaptation is not recursive - * and it is recursive otherwise. +/** Adapt a mesh handle according to the provided callbacks. + * The forest used to define the mesh handle is replaced in this function. + * If you want to additionally keep the current forest, call \ref t8_forest_ref before this function. + * \param [in,out] mesh_handle The mesh handle to adapt. + * \param [in] refine_callback Callback to decide if a single element should be refined. + * \param [in] coarsen_callback Callback to decide if a family of elements should be coarsened. + * \param [in] recursive Specifying whether adaptation is to be done recursively or not. + * \tparam TMesh The mesh handle class. */ template void @@ -172,24 +257,25 @@ adapt_mesh (TMesh& mesh_handle, refine_mesh_element refine_callback, coarsen_mesh_element_family coarsen_callback, bool recursive) { auto forest_from = mesh_handle.get_forest (); - + // Initialize forest for the adapted mesh. t8_forest_t forest; t8_forest_init (&forest); - auto* context - = new detail::MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); - + // Create and register adaptation context holding the mesh handle and the user defined callbacks. + auto context + = detail::MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); detail::AdaptRegistry::register_context (forest_from, context); - t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback, recursive); + // Set up the forest for adaptation using the wrapper callback. + t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback_wrapper, recursive); t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); t8_forest_set_user_data (forest, t8_forest_get_user_data (forest_from)); - t8_forest_commit (forest); - mesh_handle.set_forest (forest); + // Replace the forest in the mesh handle with the adapted forest. + mesh_handle.set_forest (forest); + // Clean up. detail::AdaptRegistry::unregister_context (forest_from); - delete context; } } // namespace t8_mesh_handle From faae2a615b2c1c58f30800817a0b2702aed414cd Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 22 Dec 2025 11:54:33 +0100 Subject: [PATCH 11/16] full doxygen documentation --- mesh_handle/adapt.hxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index ab9d89d23e..6d468166d4 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -209,13 +209,15 @@ class AdaptRegistry { /** Wrapper around the mesh handle adapt functionality to be able to pass the callbacks to the classic adapt routine of a forest. * The function header fits the definition of \ref t8_forest_adapt_t. + * \param [in] forest Unused; forest to which the new elements belong. * \param [in] forest_from Forest that is adapted. * \param [in] which_tree Local tree containing \a elements. + * \param [in] tree_class Unused; eclass of \a which_tree. * \param [in] lelement_id The local element id in the tree of the first element in elements. + * \param [in] scheme Unused; scheme of the forest. * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements. * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. - * \see t8_forest_set_adapt for the definition of unused parameters. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. From a342dcb7d6e7976b43396be9becc43211bbddc71 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Thu, 8 Jan 2026 17:02:20 +0100 Subject: [PATCH 12/16] update adapt routine --- mesh_handle/adapt.hxx | 57 +++++++++++------------ test/mesh_handle/t8_gtest_adapt.cxx | 13 +++--- test/mesh_handle/t8_gtest_handle_data.cxx | 3 -- 3 files changed, 34 insertions(+), 39 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index 6d468166d4..b23f689624 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -45,8 +45,8 @@ namespace t8_mesh_handle * \return true if the family should be coarsened, false otherwise. */ template -using coarsen_mesh_element_family - = std::function& elements)>; +using coarsen_element_family + = std::function& elements)>; /** Callback function prototype to decide for the refinement of an element of a mesh handle. * \param [in] mesh The mesh that should be adapted. @@ -55,8 +55,7 @@ using coarsen_mesh_element_family * \return true if the element should be refined, false otherwise. */ template -using refine_mesh_element - = std::function; +using refine_element = std::function; /** Namespace detail to hide implementation details from the user. */ namespace detail @@ -74,16 +73,16 @@ struct MeshAdaptContextBase virtual ~MeshAdaptContextBase () = default; /** Pure virtual callback for mesh adaptation. - * \param[in] lflat_element_id Local flat element ID in the mesh handle. - * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. - * \param [in] num_elements The number of entries in \a elements. - * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \param[in] lelement_handle_id Local element ID in the mesh handle. + * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. + * \param [in] num_elements The number of entries in \a elements. + * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. */ virtual int - adapt_callback (const t8_locidx_t lflat_element_id, const int is_family, const int num_elements, + adapt_callback (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, t8_element_t* elements[]) = 0; }; @@ -95,36 +94,36 @@ struct MeshAdaptContextBase template struct MeshAdaptContext final: MeshAdaptContextBase { - using Element = typename TMesh::mesh_element_class; /**< Type alias for the mesh element class. */ + using Element = typename TMesh::element_class; /**< Type alias for the element class. */ /** Constructor of the context with the mesh handle and the user defined callbacks. - * \param [in] mesh_handle The mesh handle to adapt. - * \param [in] refine_callback The refinement callback. + * \param [in] mesh_handle The mesh handle to adapt. + * \param [in] refine_callback The refinement callback. * \param [in] coarsen_callback The coarsening callback. */ - MeshAdaptContext (TMesh& mesh_handle, refine_mesh_element refine_callback, - coarsen_mesh_element_family coarsen_callback) + MeshAdaptContext (TMesh& mesh_handle, refine_element refine_callback, + coarsen_element_family coarsen_callback) : m_mesh_handle (mesh_handle), m_refine_callback (std::move (refine_callback)), m_coarsen_callback (std::move (coarsen_callback)) { } /** Callback for mesh adaptation using user defined callbacks. - * \param [in] lflat_element_id Local flat element ID in the mesh handle. - * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. - * \param [in] num_elements The number of entries in \a elements. - * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \param [in] lelement_handle_id Local flat element ID in the mesh handle. + * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. + * \param [in] num_elements The number of entries in \a elements. + * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. */ int - adapt_callback (const t8_locidx_t lflat_element_id, const int is_family, const int num_elements, + adapt_callback (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, t8_element_t* elements[]) override { // Check if refine callback is set and call it using the correct mesh handle function arguments. if (m_refine_callback) { - Element elem = m_mesh_handle.get_mesh_element (lflat_element_id); + Element elem = m_mesh_handle[lelement_handle_id]; if (m_refine_callback (m_mesh_handle, elem)) { return 1; } @@ -134,7 +133,7 @@ struct MeshAdaptContext final: MeshAdaptContextBase if (is_family && m_coarsen_callback) { std::vector element_family; for (int i = 0; i < num_elements; i++) { - element_family.push_back (m_mesh_handle.get_mesh_element (lflat_element_id + i)); + element_family.push_back (m_mesh_handle[lelement_handle_id + i]); } if (m_coarsen_callback (m_mesh_handle, element_family)) { return -1; @@ -145,9 +144,9 @@ struct MeshAdaptContext final: MeshAdaptContextBase } private: - TMesh& m_mesh_handle; /**< The mesh handle to adapt. */ - refine_mesh_element m_refine_callback; /**< The refinement callback. */ - coarsen_mesh_element_family m_coarsen_callback; /**< The coarsening callback. */ + TMesh& m_mesh_handle; /**< The mesh handle to adapt. */ + refine_element m_refine_callback; /**< The refinement callback. */ + coarsen_element_family m_coarsen_callback; /**< The coarsening callback. */ }; /** Registry pattern is used to register contexts, which provides access to the callbacks and the mesh handle. @@ -236,10 +235,10 @@ mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t fo "Something went wrong while registering the adaption callbacks. Please check your implementation."); return 0; // No adaption as default. } - // Flat index is used in mesh handle to access elements. - t8_locidx_t local_flat_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; + // Convert to index used in the mesh handle. + t8_locidx_t mesh_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; // Call the actual adapt callback stored in the context. - return context->adapt_callback (local_flat_index, is_family, num_elements, elements); + return context->adapt_callback (mesh_index, is_family, num_elements, elements); } } // namespace detail @@ -255,8 +254,8 @@ mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t fo */ template void -adapt_mesh (TMesh& mesh_handle, refine_mesh_element refine_callback, - coarsen_mesh_element_family coarsen_callback, bool recursive) +adapt_mesh (TMesh& mesh_handle, refine_element refine_callback, coarsen_element_family coarsen_callback, + bool recursive) { auto forest_from = mesh_handle.get_forest (); // Initialize forest for the adapted mesh. diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index 4ee15d482b..344415576c 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -57,7 +57,7 @@ struct dummy_user_data */ template bool -refine_mesh_element_test (const TMeshClass &mesh, const typename TMeshClass::mesh_element_class &element) +refine_element_test (const TMeshClass &mesh, const typename TMeshClass::element_class &element) { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = element.get_centroid (); @@ -73,8 +73,7 @@ refine_mesh_element_test (const TMeshClass &mesh, const typename TMeshClass::mes */ template bool -coarsen_mesh_element_family_test (const TMeshClass &mesh, - const std::vector &elements) +coarsen_element_family_test (const TMeshClass &mesh, const std::vector &elements) { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = elements[0].get_centroid (); @@ -83,8 +82,8 @@ coarsen_mesh_element_family_test (const TMeshClass &mesh, } /** Adapt callback implementation for a forest. - * This callback defines the same adaptation rules as \ref coarsen_mesh_element_family_test - * together with \ref refine_mesh_element_test. Callback is not used for the mesh handle + * This callback defines the same adaptation rules as \ref coarsen_element_family_test + * together with \ref refine_element_test. Callback is not used for the mesh handle * but for the forest compared to the adapted mesh handle. */ int @@ -132,8 +131,8 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) t8_forest_ref (forest); // Adapt mesh handle and the forest with similar callbacks. - t8_mesh_handle::adapt_mesh (mesh_handle, refine_mesh_element_test, - coarsen_mesh_element_family_test, false); + t8_mesh_handle::adapt_mesh (mesh_handle, refine_element_test, + coarsen_element_family_test, false); forest = t8_forest_new_adapt (forest, forest_adapt_callback_example, 0, 1, &user_data); // Compare results. diff --git a/test/mesh_handle/t8_gtest_handle_data.cxx b/test/mesh_handle/t8_gtest_handle_data.cxx index 450551345c..82ef2e99de 100644 --- a/test/mesh_handle/t8_gtest_handle_data.cxx +++ b/test/mesh_handle/t8_gtest_handle_data.cxx @@ -143,7 +143,4 @@ TEST (t8_gtest_handle_data, set_and_get_element_data) EXPECT_EQ (mesh[ighost].get_element_data ().volume, mesh[ighost].get_volume ()); } } - - // Unref the forest. - t8_forest_unref (&forest); } From 67bb837e6830a2fb5b2a31030321f0198759cc15 Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Thu, 15 Jan 2026 16:33:29 +0100 Subject: [PATCH 13/16] WIP: some updates to adapt --- mesh_handle/adapt.hxx | 66 ++++++++++------------------- mesh_handle/mesh.hxx | 63 +++++++++++++++++++++------ test/mesh_handle/t8_gtest_adapt.cxx | 2 +- 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index b23f689624..e1dece28d0 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -36,26 +36,6 @@ namespace t8_mesh_handle { -/** Callback function prototype to decide for coarsening of a family of elements in a mesh handle. - * This callback works with a family of elements, i.e., multiple elements that can be coarsened - * into one parent element. - * \param [in] mesh The mesh that should be adapted. - * \param [in] elements The element family considered to be coarsened. - * \tparam TMeshClass The mesh handle class. - * \return true if the family should be coarsened, false otherwise. - */ -template -using coarsen_element_family - = std::function& elements)>; - -/** Callback function prototype to decide for the refinement of an element of a mesh handle. - * \param [in] mesh The mesh that should be adapted. - * \param [in] element The element to consider for refinement. - * \tparam TMeshClass The mesh handle class. - * \return true if the element should be refined, false otherwise. - */ -template -using refine_element = std::function; /** Namespace detail to hide implementation details from the user. */ namespace detail @@ -252,32 +232,32 @@ mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t fo * \param [in] recursive Specifying whether adaptation is to be done recursively or not. * \tparam TMesh The mesh handle class. */ -template -void -adapt_mesh (TMesh& mesh_handle, refine_element refine_callback, coarsen_element_family coarsen_callback, - bool recursive) -{ - auto forest_from = mesh_handle.get_forest (); - // Initialize forest for the adapted mesh. - t8_forest_t forest; - t8_forest_init (&forest); +// template +// void +// adapt_mesh (TMesh& mesh_handle, refine_element refine_callback, coarsen_element_family coarsen_callback, +// bool recursive) +// { +// auto forest_from = mesh_handle.get_forest (); +// // Initialize forest for the adapted mesh. +// t8_forest_t forest; +// t8_forest_init (&forest); - // Create and register adaptation context holding the mesh handle and the user defined callbacks. - auto context - = detail::MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); - detail::AdaptRegistry::register_context (forest_from, context); +// // Create and register adaptation context holding the mesh handle and the user defined callbacks. +// auto context +// = detail::MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); +// detail::AdaptRegistry::register_context (forest_from, context); - // Set up the forest for adaptation using the wrapper callback. - t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback_wrapper, recursive); - t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); - t8_forest_set_user_data (forest, t8_forest_get_user_data (forest_from)); - t8_forest_commit (forest); +// // Set up the forest for adaptation using the wrapper callback. +// t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback_wrapper, recursive); +// t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); +// t8_forest_set_user_data (forest, t8_forest_get_user_data (forest_from)); +// t8_forest_commit (forest); - // Replace the forest in the mesh handle with the adapted forest. - mesh_handle.set_forest (forest); - // Clean up. - detail::AdaptRegistry::unregister_context (forest_from); -} +// // Replace the forest in the mesh handle with the adapted forest. +// mesh_handle.set_forest (forest); +// // Clean up. +// detail::AdaptRegistry::unregister_context (forest_from); +// } } // namespace t8_mesh_handle #endif /* !T8_ADAPT_HXX */ diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index e30459121b..9382b3a524 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -62,6 +62,17 @@ class mesh { using mesh_iterator = typename std::vector::iterator; /**< Non-const iterator type for the mesh elements. */ + /** Callback function prototype to decide for coarsening of a family of elements in a mesh handle. + * This callback works with a family of elements, i.e., multiple elements that can be coarsened + * into one parent element. + * \param [in] mesh The mesh that should be adapted. + * \param [in] elements The element family considered to be coarsened. + Callback function prototype to decide for the refinement of an element of a mesh handle. + * \param [in] element The element to consider for refinement. + * \return true if the element should be refined, false otherwise. + */ + using adapt_callback_type = std::function& elements)>; + /** * Constructor for a mesh of the handle. * \param [in] forest The forest from which the mesh should be created. @@ -194,23 +205,49 @@ class mesh { return m_forest; } - /** - * Setter for the forest. - * \param [in] input_forest The forest from which the mesh should be a wrapper. - */ + /** Set an adapt function to be used to adapt the mesh on committing. + * \param [in] refine_callback The adapt callback used on committing. + * \param [in] recursive Specifying whether adaptation is to be done recursively or not. + * \note This setting can be combined with \ref set_partition and \ref set_balance. The order in which + * these operations are executed is always 1) Adapt 2) Partition 3) Balance. + */ void - set_forest (t8_forest_t input_forest) + set_adapt (adapt_callback adapt_callback, bool recursive) { - T8_ASSERT (t8_forest_is_committed (input_forest)); - m_forest = input_forest; - update_elements (); - if constexpr (!std::is_void::value) { - t8_global_infof ( - "The elements of the mesh handle have been updated. Please note that the element data is not interpolated " - "automatically. Use the function set_element_data() to provide new adapted element data.\n"); + if (!m_uncommitted_forest.has_value ()) { + t8_forest_t new_forest; + t8_forest_init (&new_forest); + m_uncommitted_forest = new_forest; } + // Create and register adaptation context holding the mesh handle and the user defined callbacks. + auto context = detail::MeshAdaptContext (this, std::move (adapt_callback)); + detail::AdaptRegistry::register_context (m_forest, context); + + // Set up the forest for adaptation using the wrapper callback. + t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback_wrapper, recursive); } + /** After allocating and adding properties to the mesh, commit the changes. + * This call updates the internal state of the forest. + * The forest used to define the mesh handle is replaced in this function. + * Specialize the update with calls like /ref set_adapt calls first. + */ + void + commit () + { + if (!std::is_void::value) { + t8_forest_set_user_data (m_uncommitted_forest, t8_forest_get_user_data (m_forest)); + } + + t8_forest_commit (m_uncommitted_forest); + detail::AdaptRegistry::unregister_context (forest_from); + 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"); + } + m_forest = m_uncommitted_forest; + update_elements (); + } /** * Set the user data of the mesh. This can i.e. be used to pass user defined arguments to the adapt routine. * \param [in] data The user data of class TUserData. Data will never be touched by mesh handling routines. @@ -325,6 +362,8 @@ class mesh { std::vector m_ghosts; /**< Vector storing the (local) ghost elements. */ std::conditional_t, std::vector, std::nullptr_t> m_element_data; /**< Vector storing the (local) element data. */ + std::optional + m_uncommitted_forest; /**< Forest in which the set flags are set for a new forest before committing. */ }; } // namespace t8_mesh_handle diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index 344415576c..3587020de4 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -27,7 +27,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., * 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. */ - +// TODO: if userdata is not deleted, check that it is updated in the new forest. #include #include From 47eab25e39ccad41a87c3f4143ea59db674df4cc Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Fri, 16 Jan 2026 14:48:24 +0100 Subject: [PATCH 14/16] first working version with set_adapt --- mesh_handle/adapt.hxx | 108 ++++++++------------------- mesh_handle/mesh.hxx | 110 ++++++++++++++++++---------- test/mesh_handle/t8_gtest_adapt.cxx | 46 ++++++------ 3 files changed, 122 insertions(+), 142 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index e1dece28d0..498f5d47b5 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.hxx @@ -25,14 +25,14 @@ * according to user defined callbacks. */ -#ifndef T8_ADAPT_HXX -#define T8_ADAPT_HXX +#pragma once #include #include #include "mesh.hxx" #include #include +#include namespace t8_mesh_handle { @@ -62,8 +62,8 @@ struct MeshAdaptContextBase * 0 else. */ virtual int - adapt_callback (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, - t8_element_t* elements[]) + adapt_mesh (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, + t8_element_t* elements[]) = 0; }; @@ -76,19 +76,16 @@ struct MeshAdaptContext final: MeshAdaptContextBase { using Element = typename TMesh::element_class; /**< Type alias for the element class. */ - /** Constructor of the context with the mesh handle and the user defined callbacks. + /** Constructor of the context with the mesh handle and the user defined callback. * \param [in] mesh_handle The mesh handle to adapt. - * \param [in] refine_callback The refinement callback. - * \param [in] coarsen_callback The coarsening callback. + * \param [in] adapt_callback The adapt callback. */ - MeshAdaptContext (TMesh& mesh_handle, refine_element refine_callback, - coarsen_element_family coarsen_callback) - : m_mesh_handle (mesh_handle), m_refine_callback (std::move (refine_callback)), - m_coarsen_callback (std::move (coarsen_callback)) + MeshAdaptContext (TMesh& mesh_handle, typename TMesh::adapt_callback_type adapt_callback) + : m_mesh_handle (mesh_handle), m_adapt_callback (std::move (adapt_callback)) { } - /** Callback for mesh adaptation using user defined callbacks. + /** Callback for mesh adaptation using user defined callback. * \param [in] lelement_handle_id Local flat element ID in the mesh handle. * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements. @@ -98,35 +95,26 @@ struct MeshAdaptContext final: MeshAdaptContextBase * 0 else. */ int - adapt_callback (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, - t8_element_t* elements[]) override + adapt_mesh (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, + t8_element_t* elements[]) override { + T8_ASSERTF (m_adapt_callback, "No adapt callback set."); // Check if refine callback is set and call it using the correct mesh handle function arguments. - if (m_refine_callback) { - Element elem = m_mesh_handle[lelement_handle_id]; - if (m_refine_callback (m_mesh_handle, elem)) { - return 1; - } - } - - // Check if a family is provided for adaption, if coarsen callback is set and call it using the correct mesh handle function arguments. - if (is_family && m_coarsen_callback) { - std::vector element_family; + std::vector element_vec; + if (is_family) { for (int i = 0; i < num_elements; i++) { - element_family.push_back (m_mesh_handle[lelement_handle_id + i]); - } - if (m_coarsen_callback (m_mesh_handle, element_family)) { - return -1; + element_vec.push_back (m_mesh_handle[lelement_handle_id + i]); } } - // Do nothing if the callbacks no refinement or coarsening should be done according to the callbacks. - return 0; + else { + element_vec.push_back (m_mesh_handle[lelement_handle_id]); + } + return m_adapt_callback (m_mesh_handle, element_vec); } private: - TMesh& m_mesh_handle; /**< The mesh handle to adapt. */ - refine_element m_refine_callback; /**< The refinement callback. */ - coarsen_element_family m_coarsen_callback; /**< The coarsening callback. */ + TMesh& m_mesh_handle; /**< The mesh handle to adapt. */ + typename TMesh::adapt_callback_type m_adapt_callback; /**< The adapt callback. */ }; /** Registry pattern is used to register contexts, which provides access to the callbacks and the mesh handle. @@ -141,10 +129,10 @@ class AdaptRegistry { * \param [in] context The context to register. */ static void - register_context (t8_forest_t forest, MeshAdaptContextBase& context) + register_context (t8_forest_t forest, std::unique_ptr context) { auto& map = get_map (); - auto [it, inserted] = map.emplace (forest, context); + auto [it, inserted] = map.emplace (forest, std::move (context)); if (!inserted) { throw std::logic_error ("Context already registered"); } @@ -170,7 +158,7 @@ class AdaptRegistry { { auto& map = get_map (); auto it = map.find (forest); - return it != map.end () ? &it->second.get () : nullptr; + return it != map.end () ? it->second.get () : nullptr; } private: @@ -178,10 +166,10 @@ class AdaptRegistry { * We use a getter instead of private member variable to ensure single initialization * \return Reference to the static unordered map of t8_forest_t to MeshAdaptContextBase references. */ - static std::unordered_map>& + static std::unordered_map>& get_map () { - static std::unordered_map> map; + static std::unordered_map> map; return map; } }; @@ -209,55 +197,17 @@ 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 callbacks that are using mesh handle functionality. - auto* context = AdaptRegistry::get (forest_from); + auto* context = AdaptRegistry::get (forest); if (!context) { t8_global_infof ( "Something went wrong while registering the adaption callbacks. Please check your implementation."); return 0; // No adaption as default. } // Convert to index used in the mesh handle. - t8_locidx_t mesh_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; + const t8_locidx_t mesh_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; // Call the actual adapt callback stored in the context. - return context->adapt_callback (mesh_index, is_family, num_elements, elements); + return context->adapt_mesh (mesh_index, is_family, num_elements, elements); } } // namespace detail - -/** Adapt a mesh handle according to the provided callbacks. - * The forest used to define the mesh handle is replaced in this function. - * If you want to additionally keep the current forest, call \ref t8_forest_ref before this function. - * \param [in,out] mesh_handle The mesh handle to adapt. - * \param [in] refine_callback Callback to decide if a single element should be refined. - * \param [in] coarsen_callback Callback to decide if a family of elements should be coarsened. - * \param [in] recursive Specifying whether adaptation is to be done recursively or not. - * \tparam TMesh The mesh handle class. - */ -// template -// void -// adapt_mesh (TMesh& mesh_handle, refine_element refine_callback, coarsen_element_family coarsen_callback, -// bool recursive) -// { -// auto forest_from = mesh_handle.get_forest (); -// // Initialize forest for the adapted mesh. -// t8_forest_t forest; -// t8_forest_init (&forest); - -// // Create and register adaptation context holding the mesh handle and the user defined callbacks. -// auto context -// = detail::MeshAdaptContext (mesh_handle, std::move (refine_callback), std::move (coarsen_callback)); -// detail::AdaptRegistry::register_context (forest_from, context); - -// // Set up the forest for adaptation using the wrapper callback. -// t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback_wrapper, recursive); -// t8_forest_set_ghost (forest, 1, T8_GHOST_FACES); -// t8_forest_set_user_data (forest, t8_forest_get_user_data (forest_from)); -// t8_forest_commit (forest); - -// // Replace the forest in the mesh handle with the adapted forest. -// mesh_handle.set_forest (forest); -// // Clean up. -// detail::AdaptRegistry::unregister_context (forest_from); -// } - } // namespace t8_mesh_handle -#endif /* !T8_ADAPT_HXX */ diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 9382b3a524..393a203455 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -28,12 +28,14 @@ #define T8_MESH_HXX #include -#include #include "element.hxx" #include "competence_pack.hxx" +#include "adapt.hxx" +#include #include #include #include +#include namespace t8_mesh_handle { @@ -62,15 +64,18 @@ class mesh { using mesh_iterator = typename std::vector::iterator; /**< Non-const iterator type for the mesh elements. */ - /** Callback function prototype to decide for coarsening of a family of elements in a mesh handle. - * This callback works with a family of elements, i.e., multiple elements that can be coarsened - * into one parent element. - * \param [in] mesh The mesh that should be adapted. - * \param [in] elements The element family considered to be coarsened. - Callback function prototype to decide for the refinement of an element of a mesh handle. - * \param [in] element The element to consider for refinement. - * \return true if the element should be refined, false otherwise. - */ + /** Callback function prototype to decide for refining and coarsening of a family of elements + * or one element in a mesh handle. + * If \a elements contains more than one element, they must form a family and we decide whether this family should be coarsened + * or only the first element should be refined. + * Family means multiple elements that can be coarsened into one parent element. + * \see set_adapt for more the usage of this callback. + * \param [in] mesh The mesh that should be adapted. + * \param [in] elements One element or a family of elements to consider for adaption. + * \return 1 if the first entry in \a elements should be refined, + * -1 if the family \a elements shall be coarsened, + * 0 else. + */ using adapt_callback_type = std::function& elements)>; /** @@ -94,6 +99,7 @@ class mesh { t8_forest_unref (&m_forest); } + // --- Getter for mesh related information. --- /** * Getter for the number of local elements in the mesh. * \return Number of local elements in the mesh. @@ -124,6 +130,17 @@ class mesh { return t8_forest_get_dimension (m_forest); } + /** + * Getter for the forest the mesh is defined for. + * \return The forest the mesh is defined for. + */ + t8_forest_t + get_forest () const + { + return m_forest; + } + + // --- Methods to access elements. --- /** * Returns a constant iterator to the first (local) mesh element. * \return Constant iterator to the first (local) mesh element. @@ -195,59 +212,72 @@ class mesh { return const_cast (static_cast (this)->operator[] (local_index)); } - /** - * Getter for the forest the mesh is defined for. - * \return The forest the mesh is defined for. + // --- Methods to change the mesh, e.g. adapt, partition, balance, ... --- + /** Set an adapt function to be used to adapt the mesh on committing. + * \param [in] adapt_callback The adapt callback used on committing. + * \param [in] recursive Specifying whether adaptation is to be done recursively or not. + * \note The adaptation is carried out only when \ref commit is called. + * \note This setting can be combined with \ref set_partition and \ref set_balance. The order in which + * these operations are executed is always 1) Adapt 2) Partition 3) Balance. */ - t8_forest_t - get_forest () const + void + set_adapt (adapt_callback_type adapt_callback, bool recursive) { - return m_forest; + if (!m_uncommitted_forest.has_value ()) { + t8_forest_t new_forest; + t8_forest_init (&new_forest); + m_uncommitted_forest = new_forest; + } + // 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))); + + // 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); } - /** Set an adapt function to be used to adapt the mesh on committing. - * \param [in] refine_callback The adapt callback used on committing. - * \param [in] recursive Specifying whether adaptation is to be done recursively or not. - * \note This setting can be combined with \ref set_partition and \ref set_balance. The order in which - * these operations are executed is always 1) Adapt 2) Partition 3) Balance. - */ + /** Enable or disable the creation of a layer of ghost elements. + * On default no ghosts are created. + * \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_adapt (adapt_callback adapt_callback, bool recursive) + set_ghost (bool do_ghost = true, t8_ghost_type_t ghost_type = T8_GHOST_FACES) { if (!m_uncommitted_forest.has_value ()) { t8_forest_t new_forest; t8_forest_init (&new_forest); m_uncommitted_forest = new_forest; } - // Create and register adaptation context holding the mesh handle and the user defined callbacks. - auto context = detail::MeshAdaptContext (this, std::move (adapt_callback)); - detail::AdaptRegistry::register_context (m_forest, context); - - // Set up the forest for adaptation using the wrapper callback. - t8_forest_set_adapt (forest, forest_from, detail::mesh_adapt_callback_wrapper, recursive); + t8_forest_set_ghost (m_uncommitted_forest.value (), do_ghost, ghost_type); } /** After allocating and adding properties to the mesh, commit the changes. - * This call updates the internal state of the forest. - * The forest used to define the mesh handle is replaced in this function. - * Specialize the update with calls like /ref set_adapt calls first. - */ + * This call updates the internal state of the mesh. + * The forest used to define the mesh handle is replaced in this function. + * Specialize the update with calls like \ref set_adapt calls first. + */ void commit () { if (!std::is_void::value) { - t8_forest_set_user_data (m_uncommitted_forest, t8_forest_get_user_data (m_forest)); + t8_forest_set_user_data (m_uncommitted_forest.value (), t8_forest_get_user_data (m_forest)); } - - t8_forest_commit (m_uncommitted_forest); - detail::AdaptRegistry::unregister_context (forest_from); + t8_forest_commit (m_uncommitted_forest.value ()); + detail::AdaptRegistry::unregister_context (m_uncommitted_forest.value ()); 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"); } - m_forest = m_uncommitted_forest; + m_forest = m_uncommitted_forest.value (); + m_uncommitted_forest = std::nullopt; update_elements (); } + + // --- Methods to set and get user and element data and exchange data between processes. --- /** * Set the user data of the mesh. This can i.e. be used to pass user defined arguments to the adapt routine. * \param [in] data The user data of class TUserData. Data will never be touched by mesh handling routines. @@ -345,6 +375,10 @@ class mesh { void update_ghost_elements () { + if (get_num_ghosts () == 0) { + m_ghosts.clear (); + return; + } m_ghosts.clear (); m_ghosts.reserve (get_num_ghosts ()); t8_locidx_t num_loc_trees = t8_forest_get_num_local_trees (m_forest); diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index 3587020de4..c5a81e0ca0 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -49,36 +49,30 @@ struct dummy_user_data double coarsen_if_outside_radius; /**< if an element's center is larger this value, we coarsen its family. */ }; -/** Callback function implementation to decide for the refinement of an element of a mesh handle. - * \param [in] mesh The mesh that should be adapted. - * \param [in] element The element to consider for refinement. +/** Callback function prototype to decide for refining and coarsening of a family of elements + * or one element in a mesh handle. * \tparam TMeshClass The mesh handle class. - * \return true if the element should be refined, false otherwise. - */ -template -bool -refine_element_test (const TMeshClass &mesh, const typename TMeshClass::element_class &element) -{ - typename TMeshClass::UserDataType user_data = mesh.get_user_data (); - auto element_centroid = element.get_centroid (); - double dist = t8_dist (element_centroid, user_data.midpoint); - return (dist < user_data.refine_if_inside_radius); -} - -/** Callback function implementation to decide for coarsening. * \param [in] mesh The mesh that should be adapted. - * \param [in] elements The element family considered to be coarsened. - * \tparam TMeshClass The mesh handle class. - * \return true if the family should be coarsened, false otherwise. + * \param [in] elements One element or a family of elements to consider for adaption. + * \return 1 if the first entry in \a elements should be refined, + * -1 if the family \a elements shall be coarsened, + * 0 else. */ template -bool -coarsen_element_family_test (const TMeshClass &mesh, const std::vector &elements) +int +adapt_callback_test (const TMeshClass &mesh, const std::vector &elements) { typename TMeshClass::UserDataType user_data = mesh.get_user_data (); auto element_centroid = elements[0].get_centroid (); double dist = t8_dist (element_centroid, user_data.midpoint); - return (dist > user_data.coarsen_if_outside_radius); + 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; } /** Adapt callback implementation for a forest. @@ -130,13 +124,15 @@ TEST (t8_gtest_handle_adapt, compare_adapt_with_forest) // Ref the forest as we want to keep using it after the adapt call to compare results. t8_forest_ref (forest); - // Adapt mesh handle and the forest with similar callbacks. - t8_mesh_handle::adapt_mesh (mesh_handle, refine_element_test, - coarsen_element_family_test, false); + // Adapt mesh handle. + mesh_handle.set_adapt (adapt_callback_test, false); + mesh_handle.commit (); + // Adapt forest classically. forest = t8_forest_new_adapt (forest, forest_adapt_callback_example, 0, 1, &user_data); // Compare results. EXPECT_TRUE (t8_forest_is_equal (mesh_handle.get_forest (), forest)); + // Clean up. t8_forest_unref (&forest); } From d3459fbd9bcad557fdb7dda82114300590c92fcc Mon Sep 17 00:00:00 2001 From: Lena Ploetzke Date: Mon, 19 Jan 2026 09:47:45 +0100 Subject: [PATCH 15/16] doxygen error --- mesh_handle/mesh.hxx | 2 +- test/mesh_handle/t8_gtest_ghost.cxx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index 5c4fb27879..d704d9e126 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -224,7 +224,7 @@ class mesh { * \param [in] adapt_callback The adapt callback used on committing. * \param [in] recursive Specifying whether adaptation is to be done recursively or not. * \note The adaptation is carried out only when \ref commit is called. - * \note This setting can be combined with \ref set_partition and \ref set_balance. The order in which + * \note This setting can be combined with set_partition and set_balance. The order in which * these operations are executed is always 1) Adapt 2) Partition 3) Balance. */ void diff --git a/test/mesh_handle/t8_gtest_ghost.cxx b/test/mesh_handle/t8_gtest_ghost.cxx index b3b8563bb1..bcb9516548 100644 --- a/test/mesh_handle/t8_gtest_ghost.cxx +++ b/test/mesh_handle/t8_gtest_ghost.cxx @@ -53,6 +53,7 @@ struct t8_mesh_ghost_test: public testing::TestWithParam Date: Mon, 19 Jan 2026 10:36:36 +0100 Subject: [PATCH 16/16] cleanup --- mesh_handle/adapt.hxx | 48 ++++++++++++++--------------- mesh_handle/mesh.hxx | 7 +++-- test/mesh_handle/t8_gtest_adapt.cxx | 22 ++++++------- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/mesh_handle/adapt.hxx b/mesh_handle/adapt.hxx index 498f5d47b5..990de76e41 100644 --- a/mesh_handle/adapt.hxx +++ b/mesh_handle/adapt.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 @@ -21,8 +21,8 @@ */ /** \file adapt.hxx - * This file provides the functionality to adapt a \ref t8_mesh_handle::mesh - * according to user defined callbacks. + * This file provides helper functionality to adapt a \ref t8_mesh_handle::mesh + * according to a user defined callback. */ #pragma once @@ -31,7 +31,6 @@ #include #include "mesh.hxx" #include -#include #include namespace t8_mesh_handle @@ -56,7 +55,7 @@ struct MeshAdaptContextBase * \param[in] lelement_handle_id Local element ID in the mesh handle. * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements. - * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \param [in] elements Pointers to members of a family or, if \a is_family is zero, pointer to one element. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. @@ -67,29 +66,27 @@ struct MeshAdaptContextBase = 0; }; -/** Mesh adaptation context holding the mesh handle and the user defined callbacks. - * Class inherits from \ref MeshAdaptContextBase and implements the virtual adapt callback using the mesh and the callbacks. +/** Templated mesh adaptation context holding the mesh handle and the user defined callback. + * Class inherits from \ref MeshAdaptContextBase and implements the virtual adapt callback using the mesh and the callback. * \tparam TMesh The mesh handle class. */ template struct MeshAdaptContext final: MeshAdaptContextBase { - using Element = typename TMesh::element_class; /**< Type alias for the element class. */ - /** Constructor of the context with the mesh handle and the user defined callback. * \param [in] mesh_handle The mesh handle to adapt. - * \param [in] adapt_callback The adapt callback. + * \param [in] adapt_callback The adapt callback. */ MeshAdaptContext (TMesh& mesh_handle, typename TMesh::adapt_callback_type adapt_callback) : m_mesh_handle (mesh_handle), m_adapt_callback (std::move (adapt_callback)) { } - /** Callback for mesh adaptation using user defined callback. + /** Callback for mesh adaptation using the user defined adapt callback. * \param [in] lelement_handle_id Local flat element ID in the mesh handle. * \param [in] is_family If 1, the entries in \a elements form a family. If 0, they do not. * \param [in] num_elements The number of entries in \a elements. - * \param [in] elements Pointers to a family or, if \a is_family is zero, pointer to one element. + * \param [in] elements Pointers to members of a family or, if \a is_family is zero, pointer to one element. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. @@ -98,9 +95,9 @@ struct MeshAdaptContext final: MeshAdaptContextBase adapt_mesh (const t8_locidx_t lelement_handle_id, const int is_family, const int num_elements, t8_element_t* elements[]) override { + // Check if adapt callback is set and call it using the correct mesh handle function arguments. T8_ASSERTF (m_adapt_callback, "No adapt callback set."); - // Check if refine callback is set and call it using the correct mesh handle function arguments. - std::vector element_vec; + std::vector element_vec; if (is_family) { for (int i = 0; i < num_elements; i++) { element_vec.push_back (m_mesh_handle[lelement_handle_id + i]); @@ -117,16 +114,17 @@ struct MeshAdaptContext final: MeshAdaptContextBase typename TMesh::adapt_callback_type m_adapt_callback; /**< The adapt callback. */ }; -/** Registry pattern is used to register contexts, which provides access to the callbacks and the mesh handle. - * This globally accessible static class is required to get the handle and the callbacks in the forest callback, +/** Registry pattern is used to register contexts, which provides access to the adapt callback and the mesh handle. + * This globally accessible static class is required to get the handle and the callback in the forest callback, * as the predefined header permits to give these as function arguments. */ 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. - * \param [in] context The context to register. + * \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] context The context to register. Use unique pointer to ensure proper memory management and ownership. */ static void register_context (t8_forest_t forest, std::unique_ptr context) @@ -139,7 +137,7 @@ class AdaptRegistry { } /** Static function to unregister a context using \a forest as identifier. - * \param [in] forest The forest identifier. + * \param [in] forest The forest identifier. In our case, this is the forest to be adapted. */ static void unregister_context (t8_forest_t forest) @@ -150,7 +148,7 @@ class AdaptRegistry { } /** Getter for a context using \a forest as identifier. - * \param [in] forest The forest identifier. + * \param [in] forest The forest identifier. In our case, this is the forest to be adapted. * \return Pointer to the context registered with the id \a forest if found, nullptr otherwise. */ static MeshAdaptContextBase* @@ -163,7 +161,7 @@ class AdaptRegistry { private: /** Get the static map associating t8_forest_t with MeshAdaptContextBase references. - * We use a getter instead of private member variable to ensure single initialization + * We use a getter instead of private member variable to ensure single initialization. * \return Reference to the static unordered map of t8_forest_t to MeshAdaptContextBase references. */ static std::unordered_map>& @@ -174,7 +172,7 @@ class AdaptRegistry { } }; -/** Wrapper around the mesh handle adapt functionality to be able to pass the callbacks to the classic adapt routine of a forest. +/** Wrapper around the mesh handle adapt functionality to be able to pass the callback to the classic adapt routine of a forest. * The function header fits the definition of \ref t8_forest_adapt_t. * \param [in] forest Unused; forest to which the new elements belong. * \param [in] forest_from Forest that is adapted. @@ -196,12 +194,12 @@ mesh_adapt_callback_wrapper ([[maybe_unused]] t8_forest_t forest, t8_forest_t fo t8_element_t* elements[]) { // Get static adapt context from the registry. - // Via this, we can access the mesh handle and the user defined callbacks that are using mesh handle functionality. + // Via this, we can access the mesh handle and the user defined adapt callback that uses mesh handle functionality. auto* context = AdaptRegistry::get (forest); if (!context) { t8_global_infof ( - "Something went wrong while registering the adaption callbacks. Please check your implementation."); - return 0; // No adaption as default. + "Something went wrong while registering the adaptation callbacks. Please check your implementation."); + return 0; // No adaptation as default. } // Convert to index used in the mesh handle. const t8_locidx_t mesh_index = t8_forest_get_tree_element_offset (forest_from, which_tree) + lelement_id; diff --git a/mesh_handle/mesh.hxx b/mesh_handle/mesh.hxx index d704d9e126..a9b17ecf10 100644 --- a/mesh_handle/mesh.hxx +++ b/mesh_handle/mesh.hxx @@ -34,6 +34,7 @@ #include #include #include +#include #include namespace t8_mesh_handle @@ -78,7 +79,7 @@ class mesh { * Family means multiple elements that can be coarsened into one parent element. * \see set_adapt for more the usage of this callback. * \param [in] mesh The mesh that should be adapted. - * \param [in] elements One element or a family of elements to consider for adaption. + * \param [in] elements One element or a family of elements to consider for adaptation. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. @@ -245,7 +246,6 @@ class mesh { } /** Enable or disable the creation of a layer of ghost elements. - * On default no ghosts are created. * \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 @@ -265,7 +265,8 @@ class mesh { /** After allocating and adding properties to the mesh, commit the changes. * This call updates the internal state of the mesh. * The forest used to define the mesh handle is replaced in this function. - * Specialize the update with calls like \ref set_adapt calls first. + * The previous forest is unreferenced. Call \ref t8_forest_ref before if you want to keep it alive. + * Specialize the update with calls like \ref set_adapt first. */ void commit () diff --git a/test/mesh_handle/t8_gtest_adapt.cxx b/test/mesh_handle/t8_gtest_adapt.cxx index c5a81e0ca0..885b6c7af6 100644 --- a/test/mesh_handle/t8_gtest_adapt.cxx +++ b/test/mesh_handle/t8_gtest_adapt.cxx @@ -3,7 +3,7 @@ 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 +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 @@ -23,7 +23,7 @@ along with t8code; if not, write to the Free Software Foundation, Inc., /** * \file t8_gtest_adapt.cxx * Tests for the adapt routines of mesh handles. - * This tests uses the callbacks and user data of tutorial step 3 as example. + * 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 * they are inside a sphere around a given midpoint we refine, if they are outside, we coarsen. */ @@ -45,15 +45,15 @@ along with t8code; if not, write to the Free Software Foundation, Inc., struct dummy_user_data { t8_3D_point 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. */ + 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. */ }; -/** Callback function prototype to decide for refining and coarsening of a family of elements - * or one element in a mesh handle. +/** Callback function for the mesh handle to decide for refining or coarsening of (a family of) elements. + * The function header fits the definition of \ref TMesh::adapt_callback_type. * \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 adaption. + * \param [in] elements One element or a family of elements to consider for adaptation. * \return 1 if the first entry in \a elements should be refined, * -1 if the family \a elements shall be coarsened, * 0 else. @@ -76,9 +76,8 @@ adapt_callback_test (const TMeshClass &mesh, const std::vector (centroid, adapt_data->midpoint); if (dist < adapt_data->refine_if_inside_radius) { - /* Refine this element. */ return 1; } else if (is_family && dist > adapt_data->coarsen_if_outside_radius) { - /* Coarsen this family. */ return -1; } - /* Do not change this element. */ return 0; }